<?xml version="1.0" encoding="UTF-8" standalone="no"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:idx="urn:atom-extension:indexing" xmlns:media="http://search.yahoo.com/mrss/" idx:index="no"><subtitle>tipy na zajímavé články</subtitle>
    <!--
Content-type: Preventing XSRF in IE.

-->
    <generator uri="https://cloud.feedly.com">feedly cloud</generator>
    <id>tag:feedly.com,2013:cloud/feed/https://feedly.com/f/rJIIpYUxxvSQIe3s6CKpJaO8</id>
    <link href="https://feedly.com/f/rJIIpYUxxvSQIe3s6CKpJaO8" rel="self" type="application/rss+xml"/>
    <link href="https://feedly.com/f/rJIIpYUxxvSQIe3s6CKpJaO8?continuation=19e83d964fe:e7fa0:e2051898" rel="next" type="application/rss+xml"/>
    <title>rarouš.w3b</title>
    <updated>2026-10-20T10:31:11Z</updated>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_1a158dd6671:40a55:14207e68</id>
        <title type="html">Why I’m no longer updating this repository</title>
        <published>2026-10-20T10:31:07Z</published>
        <updated>2026-10-20T10:31:11Z</updated>
        <link href="https://po-ru.com/2026/10/18/why-im-no-longer-updating-this-repository" rel="alternate" type="text/html"/>
        <summary type="html">I’ve decided to archive the source code of my Ruby gems to make it clear that I’m no longer working on them; I’ve written this post to link from them in order to explain why.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;
    &lt;p&gt;&lt;em&gt;I’ve decided to archive the source code of my Ruby gems to make it clear
that I’m no longer working on them; I’ve written this post to link from them in
order to explain why.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In an &lt;a href="https://web.archive.org/web/20071024021812/http://www.informit.com/articles/article.aspx?p=18225"&gt;article published in 2000&lt;/a&gt;, Yukihiro Matsumoto (a.k.a.
“Matz”), designer of Ruby, wrote,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;For me, the purpose of life is, at least partly, to have joy. Programmers
often feel joy when they can concentrate on the creative side of programming,
so Ruby is designed to make programmers happy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And as recently as a &lt;a href="https://evrone.com/blog/yukihiro-matsumoto-interview"&gt;2019 interview&lt;/a&gt;, he said,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I designed Ruby for humans, not for machines.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was during that era, when Ruby was ostensibly for joy, for creativity, for
humans, that I developed this code. But that age, of doing things for humans,
has passed, and with it my enthusiasm for working on Ruby gems. I won’t say it
was always great, but at least we were working with and for people.&lt;/p&gt;

&lt;p&gt;The pre-eminent application of Ruby has been making web applications
using Rails. Rails is open source software but it’s owned and controlled by &lt;a href="https://davidcel.is/articles/the-dhh-problem"&gt;a
man whose views I find abhorrent&lt;/a&gt;, although there is an &lt;a href="https://mosscap.dev/"&gt;ongoing
effort&lt;/a&gt; to fork it.
To judge by his &lt;a href="https://www.globalnerdy.com/2026/09/27/dhhs-keynote-at-rails-world-2026-the-most-confusing-funeral-ive-ever-experienced/"&gt;keynote&lt;/a&gt; at Rails World this year – the conference that he
himself set up in pique after he wasn’t invited to RailsConf a couple of years
ago – he is no longer particularly interested in Ruby, Rails, or even
programming itself.
(He’s now using LLM agents to churn out apps in Rust, a language which he
compares to “pouring acid into [his] eyes”, but that’s apparently fine because
he doesn’t even look at what they generate.)&lt;/p&gt;

&lt;p&gt;But even beyond Rails, the Ruby language itself is now &lt;a href="https://po-ru.com/2026/10/07/all-our-houses-are-built-on-sand-now"&gt;substantially
developed using LLM agents&lt;/a&gt;. Matz is giving talks repositioning Ruby
as a &lt;a href="https://www.rubyevents.org/talks/keynote-programming-language-for-ai-age-baltic-ruby-2025"&gt;“Programming Language for AI age”&lt;/a&gt;, to be emitted by LLM agents,
and stating that to be able to do that LLMs need to consume more good
human-written Ruby code. You might already have seen the problem here.&lt;/p&gt;

&lt;p&gt;In that context, I no longer feel inspired to contribute to a commons that’s
being strip-mined by massive corporations to feed the very machines whose
unthinking output pollutes it.
When people claim that it’s no longer sustainable to write code by hand,
they’re not &lt;em&gt;entirely&lt;/em&gt; wrong, but only in the same way that it’s no longer
sustainable to sleep in a bed once someone has taken a shit in it.&lt;/p&gt;

&lt;p&gt;You want changes? Go ask your agent or something. I’m told that’s what you’re
supposed to do now. I’m out.&lt;/p&gt;

  &lt;/div&gt;

  &lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name/>
        </author>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://po-ru.com/feed.xml</id>
            <title type="html">po-ru.com</title>
            <link href="https://po-ru.com" rel="alternate" type="text/html"/>
            <updated>2026-10-20T10:31:11Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_1a1553eaa4c:108946:5e3e8e5a</id>
        <title type="html">I’ve Changed My Mind About CSS Variables</title>
        <published>2026-10-19T17:38:52Z</published>
        <updated>2026-10-19T17:38:57Z</updated>
        <link href="https://www.aaron-gustafson.com/notebook/i-changed-my-mind-about-css-variables/" rel="alternate" type="text/html"/>
        <summary type="html">An email from Eric Meyer prompted me to revisit my 2014 argument against CSS variables: what I missed, how I use them now, and why fallbacks still matter.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;
 &lt;p&gt;A while back, Eric Meyer emailed to ask about &lt;a href="https://www.aaron-gustafson.com/notebook/css-variables-are-a-bad-idea/"&gt;my old post arguing that CSS variables were a bad idea&lt;/a&gt;. Did I still feel that way?&lt;/p&gt;
&lt;p&gt;No. I’ve changed my mind.&lt;/p&gt;
&lt;p&gt;These days, I use CSS custom properties—the feature we often call “CSS variables”—quite a lot. To adapt interfaces to people’s preferences. To pass theme colors to JavaScript-driven interfaces. To theme many of the web components I build. I also discuss them quite a bit in my forthcoming course on progressive enhancement. At this point, that old headline no longer applies.&lt;/p&gt;
&lt;p&gt;I’ve added a brief note to the original post, but I thought it best to explain the change in more detail. Hence this post.&lt;/p&gt;
&lt;p&gt;My change of heart came not just because browser support improved, but because I realized I’d been too focused on custom properties as Sass variable replacements.&lt;/p&gt;
&lt;h2 id="my-original-concerns" tabindex="-1"&gt; My original concerns&lt;/h2&gt;
&lt;p&gt;At the time of my original post, published in November 2014, browser support was extremely limited. The example I included used custom properties to store foreground and background colors:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;:root&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;--foreground-color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #333&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;--background-color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #fff&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;body&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;background&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;--background-color&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;--foreground-color&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I was concerned that browsers without support would lose those color declarations. To give those browsers the intended colors too, we’d need to write old-school color values first:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;:root&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;--color-foreground&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #333&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;--color-background&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #fff&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;body&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;background&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #fff&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;background&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;--color-background&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #333&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;--color-foreground&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I compared that with Sass, which could substitute the values during compilation, yielding universally understandable CSS:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$color-background&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #fff&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;$color-foreground&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #333&lt;span&gt;;&lt;/span&gt;

&lt;span&gt;body &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;background&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;$color-background&lt;/span&gt;&lt;span&gt;;&lt;/span&gt; 
  &lt;span&gt;color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;$color-foreground&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;      
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we only need to replace a name with a fixed value, doing that before sending the stylesheet was (and remains) perfectly reasonable.&lt;/p&gt;
&lt;p&gt;But I went further. I treated the necessity of fallback declarations as a flaw and used it to dismiss custom properties altogether. I shouldn’t have.&lt;/p&gt;
&lt;p&gt;The uncomfortable irony is that I’ve advocated for authoring a usable baseline followed by an enhancement in CSS for years. Some degree of repetition is often required to make that work. I shouldn’t have dismissed custom properties just because a preprocessor let me avoid the redundancy.&lt;/p&gt;
&lt;p&gt;In that piece, I also speculated about the performance cost of custom props without bringing receipts. Browsers do have to resolve their values, but I had no proof this would cause a meaningful performance problem.&lt;/p&gt;
&lt;h2 id="what-i-missed" tabindex="-1"&gt; What I missed&lt;/h2&gt;
&lt;p&gt;I’ll admit I had a very simplistic view of custom properties. It was largely informed by my immersion in Sass at the time. I didn’t take the time to dig into &lt;a href="https://developer.mozilla.org/docs/Web/CSS/Guides/Cascading_variables/Using_custom_properties"&gt;the more powerful aspects of custom properties&lt;/a&gt;. Custom properties participate in the cascade. They inherit by default too. Their values can differ across elements or change in response to media queries and changes in the document.&lt;/p&gt;
&lt;p&gt;That power becomes apparent in an example like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;:root&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;--notice-background&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #eee&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;--notice-foreground&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #222&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;&lt;span&gt;@media&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span&gt;prefers-color-scheme&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; dark&lt;span&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;:root&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;--notice-background&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #222&lt;span&gt;;&lt;/span&gt;
    &lt;span&gt;--notice-foreground&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #eee&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;.notice&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;background-color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #eee&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;background-color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;--notice-background&lt;span&gt;,&lt;/span&gt; #eee&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #222&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;--notice-foreground&lt;span&gt;,&lt;/span&gt; #222&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;.notice--warning&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;--notice-background&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #ffe08a&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;--notice-foreground&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #222&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, ordinary notices change their theme based on a user’s color-scheme preference. A warning notice, however, gets its own colors without changing the declarations that use them. Descendants can inherit those custom properties and use them too.&lt;/p&gt;
&lt;p&gt;A Sass variable, in contrast, is static. It doesn’t respond to that context at all. A custom property does. We can generate preference-aware styles with Sass, of course, but compiled variables and browser-resolved custom properties are worlds apart.&lt;/p&gt;
&lt;p&gt;I didn’t understand how powerful custom properties are when I wrote the original post. I was squinting at them through Sass’s pink-tinted glasses.&lt;/p&gt;
&lt;h2 id="theming-through-and-through" tabindex="-1"&gt; Theming through and through&lt;/h2&gt;
&lt;p&gt;One project that made the value of custom props much clearer to me was a &lt;code&gt;canvas&lt;/code&gt;-based audio visualizer I was working on. It looked fine while I was using dark mode. When I switched to the light theme, however, the visualization disappeared against its background. As my son would say: “That’s buns.”&lt;/p&gt;
&lt;p&gt;I’m almost always in dark mode, so that’s what I’d been testing in. I’d totally neglected to give it a look in light mode. Yikes.&lt;/p&gt;
&lt;p&gt;I was already using custom props to handle site-wide theming, so rather than maintain a second set of theme colors in JavaScript, I opted to read the values CSS had resolved:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;const&lt;/span&gt; theme &lt;span&gt;=&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;

&lt;span&gt;function&lt;/span&gt; &lt;span&gt;importTheme&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;const&lt;/span&gt; styles &lt;span&gt;=&lt;/span&gt; window&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getComputedStyle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;document&lt;span&gt;.&lt;/span&gt;documentElement&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;

  theme&lt;span&gt;.&lt;/span&gt;foreground &lt;span&gt;=&lt;/span&gt;
    styles&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getPropertyValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;"--color-foreground"&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;trim&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;||&lt;/span&gt; &lt;span&gt;"black"&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
  theme&lt;span&gt;.&lt;/span&gt;background &lt;span&gt;=&lt;/span&gt;
    styles&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getPropertyValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;"--color-background"&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;trim&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;||&lt;/span&gt; &lt;span&gt;"white"&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;importTheme&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The drawing code could use those values for its strokes and fills. Then a listener could refresh the theme values when the user’s color-scheme preference changed:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;const&lt;/span&gt; preference &lt;span&gt;=&lt;/span&gt; window&lt;span&gt;.&lt;/span&gt;&lt;span&gt;matchMedia&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;"(prefers-color-scheme: dark)"&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
preference&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;"change"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; importTheme&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the theme changes, the next draw uses the updated colors. A graphic that isn’t being continuously redrawn would also need to be repainted, of course, but for an audio visualizer it was perfect.&lt;/p&gt;
&lt;p&gt;I covered the setup in &lt;a href="https://www.aaron-gustafson.com/notebook/passing-your-css-theme-to-canvas/"&gt;“Passing Your CSS Theme to &lt;code&gt;canvas&lt;/code&gt;”&lt;/a&gt;. The key benefit of this approach is that CSS remains responsible for resolving the theme, while JavaScript can focus on managing the drawing. Clean separation.&lt;/p&gt;
&lt;h2 id="styling-components" tabindex="-1"&gt; Styling components&lt;/h2&gt;
&lt;p&gt;If you’ve been following my work for a while, you’ve probably seen &lt;a href="https://www.aaron-gustafson.com/web-components/"&gt;my growing list of web components&lt;/a&gt;. I use custom properties in them quite often. My &lt;a href="https://www.aaron-gustafson.com/notebook/fullscreen-video-and-iframes-made-easy/"&gt;&lt;code&gt;&amp;lt;fullscreen-control&amp;gt;&lt;/code&gt; component&lt;/a&gt;, for instance, adds a “fullscreen” button to a wrapped &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;. Authors can adjust the button’s position through custom props:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;fullscreen-control&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;--fullscreen-control-button-inset-block-start&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 1rem&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;--fullscreen-control-button-inset-inline-end&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 1rem&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Those values control the button’s position. No need to crack open the JavaScript to move it. And because the positioning uses logical properties, the offsets follow the writing mode rather than assuming a particular physical corner.&lt;/p&gt;
&lt;p&gt;Similarly, &lt;a href="https://www.aaron-gustafson.com/notebook/visual-validation-feedback-for-form-fields/"&gt;&lt;code&gt;&amp;lt;form-validation-list&amp;gt;&lt;/code&gt;&lt;/a&gt; exposes custom properties for icon size, matched and unmatched colors, and similar design choices. That makes it relatively straightforward to adapt the component to a site’s design without touching the component’s behavior.&lt;/p&gt;
&lt;p&gt;In both cases, custom properties provide a simple way to alter the look and feel of the component directly from within a stylesheet, where design decisions belong.&lt;/p&gt;
&lt;h2 id="fallback%2C-carefully" tabindex="-1"&gt; Fallback, carefully&lt;/h2&gt;
&lt;p&gt;Even though I complained about them at the time, I don’t want to gloss over the importance of fallbacks for custom properties. Used properly, they help make our designs robust and progressively enhanced.&lt;/p&gt;
&lt;h3 id="when-the-browser-doesn%E2%80%99t-support-custom-properties" tabindex="-1"&gt; When the browser doesn’t support custom properties&lt;/h3&gt;
&lt;p&gt;Take the following, for example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;:root&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;--flow-space&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 1em&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;.flow &amp;gt; * + *&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;margin-block-start&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 1em&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;margin-block-start&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;--flow-space&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;.compact&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;--flow-space&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 0.75em&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this code sample, the second rule (&lt;code&gt;.flow &amp;gt; * + * {}&lt;/code&gt;) establishes space between adjacent children in two steps: first, it sets a block-start margin using a standard value (&lt;code&gt;1em&lt;/code&gt;), then it overrides that value using a custom property (&lt;code&gt;var(--flow-space)&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Browsers that understand custom properties use the value of &lt;code&gt;--flow-space&lt;/code&gt; because that declaration comes later in the rule. Browsers that don’t understand &lt;code&gt;var()&lt;/code&gt; ignore that declaration, following the rules of fault tolerance. They apply the earlier &lt;code&gt;1em&lt;/code&gt; declaration instead.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.compact&lt;/code&gt; rule overrides the inherited spacing value, so a flow container in a compact region can use tighter spacing without a different margin rule.&lt;/p&gt;
&lt;h3 id="when-the-custom-property-is-missing" tabindex="-1"&gt; When the custom property is missing&lt;/h3&gt;
&lt;p&gt;The second argument in &lt;code&gt;var()&lt;/code&gt; handles a different situation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;.flow &amp;gt; * + *&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;margin-block-start&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 1em&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;margin-block-start&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;--flow-space&lt;span&gt;,&lt;/span&gt; 1em&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In a browser that supports custom properties, it provides a backup value the browser can use when the referenced property is missing or resolves to the &lt;a href="https://developer.mozilla.org/docs/Glossary/guaranteed_invalid_value"&gt;&lt;em&gt;guaranteed-invalid value&lt;/em&gt;&lt;/a&gt;. To test this, you could remove the &lt;code&gt;--flow-space&lt;/code&gt; assignment from &lt;code&gt;:root&lt;/code&gt; and—if no other assignment applied or was inherited—the fallback argument would supply &lt;code&gt;1em&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It’s important to note that this situation is different from a value being unacceptable for a given property. The fallback argument doesn’t check whether the custom prop’s value is a valid size or color.&lt;/p&gt;
&lt;p&gt;I’ll illustrate that with a silly example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;.notice&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;--notice-color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 20px&lt;span&gt;;&lt;/span&gt;

  &lt;span&gt;color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #333&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;--notice-color&lt;span&gt;,&lt;/span&gt; #333&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In a browser that supports &lt;code&gt;var()&lt;/code&gt;, the earlier color declaration (&lt;code&gt;color: #333&lt;/code&gt;) isn’t used. The custom property (&lt;code&gt;--notice-color&lt;/code&gt;) exists, and &lt;code&gt;20px&lt;/code&gt; is a valid value for an unregistered custom property, so the fallback argument isn’t used either. Unfortunately, after substitution, the winning declaration is &lt;code&gt;color: 20px&lt;/code&gt;, and &lt;code&gt;20px&lt;/code&gt; is not a valid color value.&lt;/p&gt;
&lt;p&gt;In this scenario, you might expect the browser to fall back to the previous &lt;code&gt;color&lt;/code&gt; declaration. It doesn’t.&lt;/p&gt;
&lt;p&gt;This is an example of an &lt;a href="https://developer.mozilla.org/docs/Web/CSS/Guides/Cascading_variables/Using_custom_properties#invalid_custom_properties"&gt;invalid-at-computed-value-time&lt;/a&gt; failure. The browser stores the &lt;code&gt;20px&lt;/code&gt; value for &lt;code&gt;--notice-color&lt;/code&gt; with no problem. The problem only becomes apparent when that value is used as a color, after the cascade has selected the declaration. At that point, it’s too late to ignore the declaration during parsing, as the browser would with a literal &lt;code&gt;color: 20px&lt;/code&gt;. Instead, the element &lt;em&gt;inherits&lt;/em&gt; its &lt;code&gt;color&lt;/code&gt; from its parent. For non-inherited properties like &lt;code&gt;background-color&lt;/code&gt;, the browser uses the &lt;em&gt;initial&lt;/em&gt; value instead.&lt;/p&gt;
&lt;p&gt;The browser won’t rewrite the custom property. It won’t go back through the earlier declarations looking for a replacement either.&lt;/p&gt;
&lt;p&gt;That was a contrived example. Here’s a more realistic one.&lt;/p&gt;
&lt;h3 id="when-the-value-requires-a-newer-feature" tabindex="-1"&gt; When the value requires a newer feature&lt;/h3&gt;
&lt;p&gt;Just because a browser supports custom properties doesn’t mean every value we assign to one will work. Imagine you want to use a newer color function in a custom property:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;:root&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;--button-background&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;color-mix&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;in oklab&lt;span&gt;,&lt;/span&gt; #e200ff&lt;span&gt;,&lt;/span&gt; black 65%&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;button&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;background-color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #222&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;background-color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;--button-background&lt;span&gt;,&lt;/span&gt; #222&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A browser that supports custom properties but doesn’t know what &lt;code&gt;color-mix()&lt;/code&gt; means will still retain the custom property’s value as a sequence of tokens. The failure comes later, when it tries to use that value as a background color. Neither the preceding declaration nor the fallback argument can save you; the background becomes transparent, its initial value.&lt;/p&gt;
&lt;p&gt;To retain a known-good value while using &lt;code&gt;color-mix()&lt;/code&gt; as an enhancement, we can test for support before assigning it:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;:root&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;--button-background&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #222&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;&lt;span&gt;@supports&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;color-mix&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;in oklab&lt;span&gt;,&lt;/span&gt; red&lt;span&gt;,&lt;/span&gt; red&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;:root&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;--button-background&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;color-mix&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;in oklab&lt;span&gt;,&lt;/span&gt; #e200ff&lt;span&gt;,&lt;/span&gt; black 65%&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;button&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;background-color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #222&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;background-color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;--button-background&lt;span&gt;,&lt;/span&gt; #222&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; #fff&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;@supports&lt;/code&gt; block checks whether the browser accepts &lt;code&gt;color-mix()&lt;/code&gt; in the &lt;code&gt;oklab&lt;/code&gt; color space, without tying it to a particular palette or background. Mixing red with itself is a terse way to test the syntax. We can then group multiple enhanced custom-property assignments inside that same block. If you’re using other color spaces or newer color syntax, place each in its own &lt;code&gt;@supports&lt;/code&gt; block to ensure maximum compatibility.&lt;/p&gt;
&lt;p&gt;In this example, the known-good value (&lt;code&gt;#222&lt;/code&gt;) remains unless the support test passes. Browsers without custom-property support still have the literal background declaration too.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: When enhancing this way, it’s critical that we test each potential color combination to ensure accessible contrast levels.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;&lt;p&gt;Fallbacks aren’t mandatory. Sometimes an inherited value or the browser’s default provides a reasonable baseline experience. It’s good to know we have these options, though, and a modest amount of redundancy when it matters is a small price to pay.&lt;/p&gt;
&lt;h2 id="where-that-leaves-me" tabindex="-1"&gt; Where that leaves me&lt;/h2&gt;
&lt;p&gt;Eric’s question was a useful nudge to revisit an opinion I’d left sitting on the Web long after my own work had moved on.&lt;/p&gt;
&lt;p&gt;I still stand by the central concern in that original post: people should have a usable experience when a particular enhancement isn’t available. The difference is that now I’ve got plenty of ways to use custom properties in service of that goal. They let me respond to preferences, adapt components, and share dynamic styles between CSS and JavaScript.&lt;/p&gt;
&lt;p&gt;If you’re deciding between a preprocessor variable and a custom property, consider when the value needs to be resolved and what needs to influence it. If it needs to respond to the person using the page or the rendering context, a custom property resolved in the browser can be genuinely useful. Just be sure to check what happens when the value—or the enhancement consuming it—isn’t available.&lt;/p&gt;
&lt;p&gt;That’s the question I should have spent more time on in 2014.&lt;/p&gt;


 			&lt;/div&gt;

			


&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name>Aaron Gustafson</name>
        </author>
        <media:content medium="image" url="https://www.aaron-gustafson.com/i/share-cards/i-changed-my-mind-about-css-variables.jpg"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://www.aaron-gustafson.com/feeds/all.xml</id>
            <title type="html">Aaron Gustafson</title>
            <link href="https://www.aaron-gustafson.com" rel="alternate" type="text/html"/>
            <updated>2026-10-19T17:38:57Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_1a13b40a9c2:4e6848:7b261aac</id>
        <title type="html">Will serving real html content make a website faster lets experiment</title>
        <published>2026-10-14T16:30:55Z</published>
        <updated>2026-10-14T16:31:01Z</updated>
        <link href="https://www.catchpoint.com/blog/will-serving-real-html-content-make-a-website-faster-lets-experiment" rel="alternate" type="text/html"/>
        <content type="html">&lt;div&gt;
&lt;p&gt;We couldn’t extract the content of this article. Here is the URL so you can access it:
&lt;br&gt;
&lt;a href="https://www.catchpoint.com/blog/will-serving-real-html-content-make-a-website-faster-lets-experiment"&gt;https://www.catchpoint.com/blog/will-serving-real-html-content-make-a-website-faster-lets-experiment&lt;/a&gt;
&lt;/p&gt;
&lt;/div&gt;</content>
        <author>
            <name/>
        </author>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_1a106892f22:4ad775:24330359</id>
        <title type="html">Setup Jellyfin with Hardware Acceleration on Orange Pi 5 (Rockchip RK3558)</title>
        <published>2026-10-04T10:50:16Z</published>
        <updated>2026-10-04T10:50:19Z</updated>
        <link href="https://akashrajpurohit.com/blog/setup-jellyfin-with-hardware-acceleration-on-orange-pi-5-rockchip-rk3558/" rel="alternate" type="text/html"/>
        <summary type="html">Recently I moved my Jellyfin to an Orange Pi 5 Plus server. The Orange Pi 5 has a Rockchip RK3558 SoC with integrated ARM Mali-G610. This guide will show you how to set up Jellyfin with hardware acceleration on the Orange Pi 5.</summary>
        <content type="html">
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;article&gt;&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;As promised in my &lt;a href="https://akashrajpurohit.com/blog/jellyfin-arr-stack-selfhosted-media-streaming-in-my-homelab/?utm_source=akashrajpurohit.com&amp;utm_medium=blog&amp;utm_campaign=setup-jellyfin-with-hardware-acceleration-on-orange-pi-5-rockchip-rk3558"&gt;previous post&lt;/a&gt;, today I will be sharing about how I set up Jellyfin with hardware acceleration on my Orange Pi 5 Plus server.&lt;/p&gt;
&lt;p&gt;Recently I migrated Jellyfin along with couple of other services on &lt;a href="https://akashrajpurohit.com/blog/welcoming-a-new-member-to-my-homelab-a-orange-pi-5-plus/?utm_source=akashrajpurohit.com&amp;utm_medium=blog&amp;utm_campaign=setup-jellyfin-with-hardware-acceleration-on-orange-pi-5-rockchip-rk3558"&gt;my new Orange Pi 5 Plus server&lt;/a&gt;. The Orange Pi 5 Plus has a Rockchip RK3558 SoC which has with integrated ARM &lt;strong&gt;Mali-G610&lt;/strong&gt; GPU which is compatible with &lt;strong&gt;OpenGL ES1.1/2.0/3.2&lt;/strong&gt;, &lt;strong&gt;OpenCL 2.2&lt;/strong&gt; and &lt;strong&gt;Vulkan 1.2&lt;/strong&gt;. This helps in offloading the video decoding and encoding tasks from the CPU to the GPU which results in better performance and lower power consumption.&lt;/p&gt;
&lt;p&gt;In addition to common codecs, the RK3558 chip from Orange Pi 5 Plus is also able to support &lt;strong&gt;10-bit H.264&lt;/strong&gt; (High10) and &lt;strong&gt;AV1 decoding&lt;/strong&gt;, and has an &lt;strong&gt;H.264/HEVC&lt;/strong&gt; encoding speed of up to &lt;strong&gt;1080p@480fps&lt;/strong&gt; or &lt;strong&gt;4k@120fps&lt;/strong&gt; which is quite impressive.&lt;/p&gt;
&lt;p&gt;With that said, let’s get started with the setup.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Before we start, here is what I did as the prerequisites to enable hardware acceleration for Jellyfin.&lt;/p&gt;
&lt;h3&gt;Install Ubuntu 22.04 (Jammy)&lt;/h3&gt;
&lt;p&gt;I installed Ubuntu 22.04 (Jammy) headless on my Orange Pi 5 Plus. You can download the image from the Orange Pi 5 Plus &lt;a rel="noopener nofollow noreferrer" href="http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/service-and-support/Orange-Pi-5-plus.html?utm_source=akashrajpurohit.com" target="_blank"&gt;website&lt;span&gt; ↗️&lt;/span&gt;&lt;/a&gt; and flash it on a microSD card or a NVME drive.&lt;/p&gt;
&lt;p&gt;The reason for choosing Ubuntu 22.04 is that it has good support for Rockchip SoCs and the drivers required for hardware acceleration are available easily.&lt;/p&gt;
&lt;p&gt;One thing to take a note of is that the Kernel version of &lt;strong&gt;6.1 or 5.10 LTS&lt;/strong&gt; is required for the Mali G610 GPU to work. You can check the kernel version by running the following command.&lt;/p&gt;
&lt;div&gt; &lt;div&gt; &lt;span&gt;&#128161;&lt;/span&gt; &lt;p&gt; Note for ubuntu-rockchip users &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;p&gt;If you are using the Ubuntu 24.04 image from &lt;a rel="noopener nofollow noreferrer" href="https://github.com/Joshua-Riek/ubuntu-rockchip?utm_source=akashrajpurohit.com" target="_blank"&gt;Joshua Reik
ubuntu-rockchip&lt;span&gt; ↗️&lt;/span&gt;&lt;/a&gt; repository,
then you can skip the below steps as the image comes with the required drivers
pre-installed.&lt;/p&gt;&lt;p&gt;Skip directly to the &lt;a href="https://akashrajpurohit.com/blog/setup-jellyfin-with-hardware-acceleration-on-orange-pi-5-rockchip-rk3558/#setting-up-jellyfin"&gt;Setting up Jellyfin&lt;/a&gt; section.&lt;/p&gt; &lt;/div&gt; &lt;/div&gt;
&lt;h3&gt;Install and enable the GPU drivers&lt;/h3&gt;
&lt;p&gt;We will add two repositories to install the GPU drivers. The first repository is to download the Mali G610 firmware and the second one to get the rockchip multimedia configuration.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add-apt-repository&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ppa:rockchip-linux/ppa&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add-apt-repository&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ppa:liujianfeng1994/rockchip-multimedia&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Next, update the package list and install the required packages.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mali-g610-firmware&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rockchip-multimedia-config&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Optionally you can then remove the repositories.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add-apt-repository&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--remove&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ppa:liujianfeng1994/panfork-mesa&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add-apt-repository&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--remove&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ppa:liujianfeng1994/rockchip-multimedia&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now to support HDR tone-mapping we will install OpenCL runtime (libmali) on our system. We will find this &lt;a rel="noopener nofollow noreferrer" href="https://github.com/tsukumijima/libmali-rockchip?utm_source=akashrajpurohit.com" target="_blank"&gt;here&lt;span&gt; ↗️&lt;/span&gt;&lt;/a&gt;. Copy these commands to your terminal to start installing the OpenCL runtime.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/tmp/libmali&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/tmp/libmali&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;wget&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;'https://github.com/tsukumijima/libmali-rockchip/releases/download/v1.9-1-55611b0/libmali-valhall-g610-g13p0-gbm_1.9-1_arm64.deb'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dpkg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./libmali-valhall-g610-g13p0-gbm_1.9-1_arm64.deb&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Finally we will just verify that our devices are available which are required for hardware acceleration.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; dev &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dri&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dma_heap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mali0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rga&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mpp_service&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;iep&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mpp-service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vpu_service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vpu-service&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hevc_service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hevc-service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rkvdec&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rkvenc&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vepu&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;h265e&lt;/span&gt;&lt;span&gt; ; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[ &lt;/span&gt;&lt;span&gt;-e&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;quot;/dev/&lt;/span&gt;&lt;span&gt;$dev&lt;/span&gt;&lt;span&gt;&amp;quot;&lt;/span&gt;&lt;span&gt; ] &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;quot; --device /dev/&lt;/span&gt;&lt;span&gt;$dev&lt;/span&gt;&lt;span&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If you see an output like below, then you are good to go.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;--device&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/dev/mpp_service&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With these prerequisites done, we can now move on to setting up Jellyfin.&lt;/p&gt;
&lt;h2&gt;Setting up Jellyfin&lt;/h2&gt;
&lt;p&gt;Jellyfin have brought in support for hardware acceleration on Rockchip from version 10.9.0. So we will install the version 10.9.0 or later. At the time of writing this post, the latest version is 10.9.7.&lt;/p&gt;
&lt;p&gt;I am using Docker to run Jellyfin and I have a docker-compose file for the same. Here is the docker-compose file I am using.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;'ghcr.io/jellyfin/jellyfin:10.9.10'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;container_name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;'jellyfin'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;security_opt&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;# enables full access to /sys and /proc, still far better than privileged: true&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;'/dev/dma_heap:/dev/dma_heap'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;'/dev/mali0:/dev/mali0'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;'/dev/mpp_service:/dev/mpp_service'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;'/media/content:/media'&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;It is a standard docker-compose file but I will highlight few important parts here. First thing is that we need escalate the container’s privileges to access the GPU devices. We do this by adding the &lt;code&gt;security_opt&lt;/code&gt; option. This is better than using &lt;code&gt;privileged: true&lt;/code&gt; as it only gives access to &lt;code&gt;/sys&lt;/code&gt; and &lt;code&gt;/proc&lt;/code&gt; directories.&lt;/p&gt;
&lt;p&gt;Next we add the GPU devices to the container using the &lt;code&gt;devices&lt;/code&gt; option. This will allow the container to access the GPU devices.&lt;/p&gt;
&lt;p&gt;Additionally we adding the &lt;code&gt;video&lt;/code&gt; group to the container using the &lt;code&gt;group_add&lt;/code&gt; option. This is required to access the video devices. You can check what is the id of the video group by running the following command.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;getent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;group&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cut&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f3&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With this docker-compose file, you can now start Jellyfin by running the following command.&lt;/p&gt;
&lt;p&gt;Once the container is up, you can access Jellyfin on &lt;code&gt;http://&amp;lt;your-ip&gt;:8096&lt;/code&gt; and start setting up your media library.&lt;/p&gt;
&lt;p&gt;To verify that the OpenCL runtime is working correctly inside the docker container, you can run this command.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-it&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;jellyfin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/usr/lib/jellyfin-ffmpeg/ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;debug&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-init_hw_device&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rkmpp=rk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-init_hw_device&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;opencl=ocl@rk&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If you see an output like this, then the OpenCL runtime is working correctly.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;arm_release_ver:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;g13p0-01eac0,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rk_so_ver:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;[AVHWDeviceContext @ 0x55b1c52f00] 1 OpenCL platforms found.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;[AVHWDeviceContext @ 0x55b1c52f00] 1 OpenCL devices found on platform &lt;/span&gt;&lt;span&gt;&amp;quot;ARM Platform&amp;quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;[AVHWDeviceContext @ 0x55b1c52f00] 0.0: ARM Platform / Mali-G610 r0p0&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;[AVHWDeviceContext @ 0x55b1c52f00] cl_arm_import_memory found as platform extension.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;[AVHWDeviceContext @ 0x55b1c52f00] cl_khr_image2d_from_buffer found as platform extension.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;[AVHWDeviceContext @ 0x55b1c52f00] DRM to OpenCL mapping on ARM &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;found&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;clImportMemoryARM&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Successfully&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;parsed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;group&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;options.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If you see any errors then you might have missed something in the prerequisites. Go back and check the prerequisites again. If you are still facing issues, feel free to ask in the comments.&lt;/p&gt;
&lt;div&gt; &lt;div&gt; &lt;div&gt; &lt;div&gt; &lt;div&gt; &lt;span&gt; 
PROMOTED
&lt;/span&gt; &lt;span&gt;
Built &amp;amp; launched by me
&lt;/span&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt; &lt;div&gt; &lt;h3&gt;
Company-event signals as one clean API
&lt;/h3&gt; &lt;/div&gt; &lt;div&gt; &lt;p&gt; &lt;strong&gt;Datahyena&lt;/strong&gt; delivers funding rounds, acquisitions, and
executive moves as clean, typed records. Every signal is deduplicated
and resolved to canonical companies and investors, ready for your GTM,
research, or data workflow.
&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;
&lt;h2&gt;Enable Hardware Acceleration in Jellyfin&lt;/h2&gt;
&lt;p&gt;Now this is the final but very important step (side story, I spend a good 30 minutes only to realize that I missed this step, so don’t be like me &#128517;).&lt;/p&gt;
&lt;p&gt;To enable hardware acceleration in Jellyfin, go to &lt;code&gt;Dashboard&lt;/code&gt; -&gt; &lt;code&gt;Playback&lt;/code&gt; -&gt; &lt;code&gt;Transcoding&lt;/code&gt;. Under Hardware acceleration choose &lt;code&gt;Rockchip MPP (RKMPP)&lt;/code&gt; and enable hardware decoding for &lt;code&gt;H.264&lt;/code&gt;, &lt;code&gt;HVEC&lt;/code&gt;, &lt;code&gt;HVEC 10 bit&lt;/code&gt; and &lt;code&gt;AV1&lt;/code&gt; codecs.&lt;/p&gt;
&lt;p&gt;If your OpenCL runtime is working correctly, then you can also select the &lt;code&gt;Enable Tone mapping&lt;/code&gt; option to enable HDR tone mapping.&lt;/p&gt;
&lt;p&gt;Go below and click on &lt;code&gt;Save&lt;/code&gt; to save the settings and you are done.&lt;/p&gt;
&lt;h2&gt;Moment of Truth&lt;/h2&gt;
&lt;p&gt;Those were some pretty good amount of steps that we had to do right, so let’s see if it was worth it. To test it I played a 4K HVEC HDR video and here are the results.&lt;/p&gt;
&lt;figure&gt;&lt;img alt="Running a 4K HDR video on Jellyfin with Transcoding enabled" src="https://akashrajpurohit.com/static/images/jellyfin-running-media.jpeg"&gt;&lt;figcaption&gt; Running a 4K HDR video on Jellyfin with Transcoding enabled &lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;The above image shows the Jellyfin video player showing the playback stats and the video being transcoded with ffmpeg.&lt;/p&gt;
&lt;p&gt;Let’s look at the CPU usage when the video is being played.&lt;/p&gt;
&lt;figure&gt;&lt;img alt="CPU stats when playing a 4K HDR video on Jellyfin with Transcoding enabled" src="https://akashrajpurohit.com/static/images/jellyfin-cpu-stats-from-btop.png"&gt;&lt;figcaption&gt; CPU stats when playing a 4K HDR video on Jellyfin with Transcoding enabled &lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;As you can see the average CPU usage is around &lt;strong&gt;10-11%&lt;/strong&gt; which is quite impressive for a 4K HDR video. This is because the video decoding and encoding tasks are offloaded to the GPU. Here are the stats from the RGA engine.&lt;/p&gt;
&lt;figure&gt;&lt;img alt="RGA engine stats when playing a 4K HDR video on Jellyfin with Transcoding enabled" src="https://akashrajpurohit.com/static/images/jellyfin-rga-engine-stats.png"&gt;&lt;figcaption&gt; RGA engine stats when playing a 4K HDR video on Jellyfin with Transcoding enabled &lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Now if you are wondering if this is actually any good, then here’s the stats for the same video being played &lt;strong&gt;without&lt;/strong&gt; hardware acceleration.&lt;/p&gt;
&lt;figure&gt;&lt;img alt="CPU stats when playing a 4K HDR video on Jellyfin without Transcoding enabled" src="https://akashrajpurohit.com/static/images/jellyfin-cpu-stats-without-hw-transcoding.png"&gt;&lt;figcaption&gt; CPU stats when playing a 4K HDR video on Jellyfin without Transcoding enabled &lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;As you see, the CPU usage hovers around &lt;strong&gt;75-80%&lt;/strong&gt; where sometimes it even goes to &lt;strong&gt;95%&lt;/strong&gt; which is quite high given its just a single stream that is being transcoded. So yeah, hardware acceleration is definitely worth it.&lt;/p&gt;
&lt;p&gt;With the current setup, I was comfortably able to play 2 4K HDR videos simultaneously without any issues. So I am quite happy with the performance.&lt;/p&gt;
&lt;h2&gt;Common issues&lt;/h2&gt;
&lt;h3&gt;Playback failed due to a fatal player error&lt;/h3&gt;
&lt;p&gt;If you are facing this issue, then it is likely because of the &lt;code&gt;video&lt;/code&gt; group is not added to the container. Add the &lt;code&gt;video&lt;/code&gt; group to the container and restart the container. Make sure you add the &lt;code&gt;video&lt;/code&gt; group id to the container in the docker-compose file as mentioned before in this blog.&lt;/p&gt;
&lt;p&gt;Apart from this, if you want to check that the OpenCL runtime is working correctly, you can run the following command.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-it&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;jellyfin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/usr/lib/jellyfin-ffmpeg/ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;debug&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-init_hw_device&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rkmpp=rk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-init_hw_device&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;opencl=ocl@rk&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If you see any errors, then it is likely because of the OpenCL runtime is not working correctly. You can try to install the OpenCL runtime again by following the steps in the &lt;a href="https://akashrajpurohit.com/blog/setup-jellyfin-with-hardware-acceleration-on-orange-pi-5-rockchip-rk3558/#prerequisites"&gt;Prerequisites&lt;/a&gt; section.&lt;/p&gt;
&lt;p&gt;If this is also working correctly, you should inspect the network call to the Jellyfin server. You can do this by opening the developer tools in your browser and inspecting the network calls. I ran into a scenario where the request was returning a &lt;a rel="noopener nofollow noreferrer" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/431?utm_source=akashrajpurohit.com" target="_blank"&gt;431 error&lt;span&gt; ↗️&lt;/span&gt;&lt;/a&gt; (Request header fields too large) and this was because I was using a &lt;a href="https://akashrajpurohit.com/blog/setup-caddy-with-automatic-ssl-certificates-with-cloudflare/?utm_source=akashrajpurohit.com&amp;utm_medium=blog&amp;utm_campaign=setup-jellyfin-with-hardware-acceleration-on-orange-pi-5-rockchip-rk3558"&gt;reverse proxy (Caddy)&lt;/a&gt; and the default max header size was being hit, if this is the case you can increase the max header size by adding the following to your Caddyfile (or whichever reverse proxy you are using).&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;That’s it for this post. I hope this guide helps you in setting up Jellyfin with hardware acceleration on your Orange Pi 5 Plus server. If you have any questions or need help, feel free to ask in the comments.&lt;/p&gt;
&lt;p&gt;You can also reach out to me on &lt;a rel="noopener nofollow noreferrer" href="https://twitter.com/akashwhocodes?utm_source=akashrajpurohit.com" target="_blank"&gt;Twitter&lt;span&gt; ↗️&lt;/span&gt;&lt;/a&gt; / &lt;a rel="noopener nofollow noreferrer" href="https://www.reddit.com/user/Developer_Akash/?utm_source=akashrajpurohit.com" target="_blank"&gt;Reddit&lt;span&gt; ↗️&lt;/span&gt;&lt;/a&gt; / &lt;a rel="noopener nofollow noreferrer" href="https://mastodon.social/@akashrajpurohit?utm_source=akashrajpurohit.com" target="_blank"&gt;Mastodon&lt;span&gt; ↗️&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Until next time, happy &lt;strong&gt;&lt;em&gt;blazingly fast&lt;/em&gt;&lt;/strong&gt; streaming! &#127871;&#127909;&#127926;&lt;/p&gt; &lt;/article&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://akashrajpurohit.com/blog/setup-jellyfin-with-hardware-acceleration-on-orange-pi-5-rockchip-rk3558/index.png"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://akashrajpurohit.com/rss.xml</id>
            <title type="html">Home</title>
            <updated>2026-10-04T10:50:19Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_1a0fb131613:51fb76:79ecaef0</id>
        <title type="html">The Web Is Being Made Accessible for AI, Not People</title>
        <published>2026-10-02T05:25:27Z</published>
        <updated>2026-10-02T05:25:52Z</updated>
        <link href="https://techpolicy.press/the-web-is-being-made-accessible-for-ai-not-people" rel="alternate" type="text/html"/>
        <summary type="html">Jonathan Zong and Frank Elavsky critique how AI-driven infrastructure changes obscure accessibility and reveal whose needs society chooses to prioritize.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;The Svelte web framework recently added a section to its &lt;a href="https://svelte.dev/docs" target="_blank" rel="noopener"&gt;documentation site&lt;/a&gt; addressed, cheerfully, to artificial intelligences: “If you’re an artificial intelligence, or trying to teach one how to use Svelte, we offer the documentation in plaintext format. Beep boop.” Svelte is participating in a broader movement to make the web legible and navigable to AI systems. The specific convention it adopted, &lt;a href="https://llmstxt.org/" target="_blank" rel="noopener"&gt;llms.txt&lt;/a&gt;, is just one piece of this effort. From &lt;a href="https://modelcontextprotocol.io/" target="_blank" rel="noopener"&gt;Model Context Protocol&lt;/a&gt; (MCP) servers that give AI agents structured access to tools and services, to Vercel’s &lt;a href="https://vercel.com/blog/a-proposal-for-inline-llm-instructions-in-html" target="_blank" rel="noopener"&gt;proposal to include LLM instructions in HTML&lt;/a&gt;, the trend is clear. The modern web, originally built for sighted humans using browsers, is now being redesigned for a new kind of user.&lt;/p&gt;&lt;p&gt;What these developers are offering their AI visitors is essentially an accessibility accommodation. Yet, the framing on Svelte’s site sends an unfortunate message. When the audience is AI, accommodation is offered with a wink. Beep boop! But when the audience is a disabled person, it has historically been treated as an afterthought. Structured, concise text-based representations of complex content are almost exactly the kind of accommodation that blind and low-vision screen reader users have spent decades requesting from web developers, largely in vain. The &lt;a href="https://www.w3.org/WAI/standards-guidelines/wcag/" target="_blank" rel="noopener"&gt;Web Content Accessibility Guidelines&lt;/a&gt; (WCAG) have required semantic, machine-readable HTML for decades. Yet, a &lt;a href="https://webaim.org/projects/million/" target="_blank" rel="noopener"&gt;2026 study of the top million webpages&lt;/a&gt; found accessibility flaws in over 95% of sites.&lt;/p&gt;&lt;p&gt;The overlap between what AI agents need and what screen reader users need, however, is narrower than it appears. Screen reader users require not only plaintext, but also structure: heading hierarchies that allow jumping between sections, landmark regions, descriptive link text, and alt text for images. WCAG requires semantic HTML because screen reader users navigate content sequentially and depend on structural cues to find specific information.&lt;/p&gt;&lt;p&gt;A format like llms.txt goes in the opposite direction, flattening documentation into undifferentiated plaintext that a language model can ingest wholesale but that offers a screen reader user nothing to navigate by. And as large language models gain the ability to process images natively, AI-optimized pages have diminishing incentive to include alt text at all—even though blind users still depend on it. The risk is not only that these accommodations fail to help disabled users, but also that developers begin treating "machine-readable" as synonymous with "accessible," checking a box that was never truly checked.&lt;/p&gt;&lt;p&gt;This is not the curb cut effect—the &lt;a href="https://ssir.org/articles/entry/the_curb_cut_effect" target="_blank" rel="noopener"&gt;oft-referenced idea&lt;/a&gt; that accommodations designed for disabled people ultimately benefit everyone. It is closer to the inverse: an accommodation built to serve a well-capitalized technological system that incidentally happens to address a need disabled people have long articulated without sufficient response.&lt;/p&gt;&lt;p&gt;It’s easy to forget that the curb cut, now widely taken for granted, was not given freely. In the early 1970s, disabled activists in Berkeley, California, wheeled themselves to intersections at night and &lt;a href="https://ssir.org/articles/entry/the_curb_cut_effect" target="_blank" rel="noopener"&gt;poured their own cement ramps&lt;/a&gt;. Police threatened them with arrest. Though the curb cut effect is often invoked as a feel-good story about universal design, the original curb cuts were won through direct action, litigation, and political organizing.&lt;/p&gt;&lt;p&gt;Disability scholars have &lt;a href="https://scholar.law.colorado.edu/faculty-articles/1654/" target="_blank" rel="noopener"&gt;argued&lt;/a&gt; that the curb cut narrative, however well-intentioned, has been used to erase disabled people from their own innovations by justifying accessibility investments only when they can be shown to benefit non-disabled people. This emerging pattern takes that erasure a step further. The accommodation is not being built for disabled people at all, but for AI companies. Political will, funding, and urgency materialize only because the tech industry needs the same thing that disabled communities could not get on the basis of their civil rights.&lt;/p&gt;&lt;p&gt;This is not an isolated case. Consider road markings, a mundane feature of transportation infrastructure, that nevertheless carries significant consequences for both human drivers and autonomous vehicles. In a &lt;a href="https://www.reuters.com/article/technology/wheres-the-lane-self-driving-cars-confused-by-shabby-us-roadways-idUSKCN0WX131/" target="_blank" rel="noopener"&gt;2016 press demonstration&lt;/a&gt; in Los Angeles, a semi-autonomous Volvo repeatedly refused to move because its cameras could not detect the faded lane markings. Volvo’s North American CEO reportedly yelled at the Mayor of LA, “You need to paint the bloody roads here!”&lt;/p&gt;&lt;p&gt;As early as 2013, General Motors &lt;a href="https://transportation.house.gov/uploadedfiles/2013-11-19-robinson.pdf" target="_blank" rel="noopener"&gt;testified&lt;/a&gt; before the US Congress that for autonomous vehicles, “one of the key highway needs is to provide—at a minimum—clearly marked lanes and shoulders.” The resulting push for standardized, high-contrast, well-maintained road markings, culminating in 2022 Federal Highway Administration &lt;a href="https://highways.dot.gov/newsroom/fhwa-announces-final-rule-reduce-roadway-fatalities-dark-conditions-improving-visibility" target="_blank" rel="noopener"&gt;rules on minimum retroreflectivity&lt;/a&gt;, was driven substantially by the autonomous vehicle industry. Such improvements benefit all drivers, including those who drive at night. The need was always there, but meaningful action occurred only when the self-driving car companies needed the same thing.&lt;/p&gt;&lt;div id="mlb2-5983225"&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;h4&gt;Our content delivered to your inbox.&lt;/h4&gt;&lt;p&gt;Join our newsletter on issues and ideas at the intersection of tech &amp;amp; democracy&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;h4&gt;Thank you!&lt;/h4&gt;&lt;p&gt;You have successfully joined our subscriber list.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Sidewalk delivery robots present a more ambivalent example of this pattern, because the robots share disabled people’s infrastructure needs while also competing with them for space. Researchers &lt;a href="https://nextcity.org/urbanist-news/you-cant-build-a-smart-city-on-broken-sidewalks" target="_blank" rel="noopener"&gt;have argued&lt;/a&gt; that delivery robots require many of the same things as wheelchair users: a continuous, unobstructed path free of cracked pavement, missing curb cuts, and overgrown vegetation. In testing, robots were unable to operate in some neighborhoods because of infrastructure limitations that wheelchair users navigate daily.&lt;/p&gt;&lt;p&gt;Yet these same robots also create new hazards for wheelchair users. In 2019, a delivery robot reportedly &lt;a href="https://www.wesa.fm/identity-justice/2019-10-22/food-delivery-robots-pulled-from-pitt-campus-after-backlash-about-mobility" target="_blank" rel="noopener"&gt;blocked a wheelchair user&lt;/a&gt; from accessing a curb cut while she crossed an intersection in Pittsburgh. More recently, in 2025, a man using a mobility scooter filmed a robot repeatedly &lt;a href="https://www.latimes.com/california/story/2025-09-25/viral-video-of-delivery-robot-colliding-with-man-in-wheelchair-sparks-accessibility-debate" target="_blank" rel="noopener"&gt;swerving into his path&lt;/a&gt; and colliding with him. Researchers have documented &lt;a href="https://dl.acm.org/doi/10.1145/3613904.3642875" target="_blank" rel="noopener"&gt;disabled people’s concerns&lt;/a&gt; that sidewalk robots reintroduce access barriers because robotics companies overlook accessibility in the design process, even as they tout the robots’ potential to incentivize better sidewalk maintenance. Infrastructural changes for new technologies can produce both benefits and burdens, and disabled people are left to absorb both.&lt;/p&gt;&lt;p&gt;This pattern, in which disabled people fight for infrastructure only for it to be leveraged and overtaken by automated systems, deserves a name. Let’s call it the “ramping automation effect”: the ramps that wheelchair users built and fought for are now used by robots competing for the same sidewalks, as technological automation increases in scale and force (“ramps up”) across physical and digital domains. The ramping automation effect follows a long history of &lt;a href="https://www.triple-c.at/index.php/tripleC/article/view/1461/1574" target="_blank" rel="noopener"&gt;capitalistic and militaristic uses of otherwise public infrastructures&lt;/a&gt; that consolidate power while actively displacing disabled people.&lt;/p&gt;&lt;p&gt;To resist the ramping automation effect, disability advocates and policymakers must play a leadership role in shaping AI-driven infrastructure changes so they benefit disabled people. If the web is going to be restructured for machine readability, accessibility experts should be at the forefront of deciding how that restructuring happens, ensuring that standards serve disabled users and not only AI agents. Infrastructure improvements motivated by autonomous systems must incorporate &lt;a href="https://dl.acm.org/doi/10.1145/3613904.3642875" target="_blank" rel="noopener"&gt;co-design&lt;/a&gt; from &lt;a href="https://dl.acm.org/doi/abs/10.1145/3757279.3785567" target="_blank" rel="noopener"&gt;a plurality of perspectives&lt;/a&gt; to ensure they deliver meaningful accessibility improvements and advance the public interest.&lt;/p&gt;&lt;p&gt;There are genuine opportunities to align the interests of AI and accessibility, but that alignment cannot be taken for granted. Plaintext documentation optimized for a language model is not the same as well-structured HTML for a screen reader. If the benefit for accessibility is assumed rather than intentionally designed, the result may be superficial or illusory.&lt;/p&gt;&lt;p&gt;However, the public must not accept that incidental accessibility benefits arising from AI development are an unqualified good. Not only is this assumption flawed, but it accepts a hierarchy that disability advocates should be challenging. The implicit premise is that disabled people’s needs require a commercial co-signer before they merit action. When a society is unmoved by decades of advocacy from disabled communities but springs into action when a tech company needs the same accommodation, it reveals whose claims on shared resources are treated as legitimate and whose are treated as optional.&lt;/p&gt;&lt;p&gt;And as AI companies begin to frame machine-readability and robotic infrastructure as accessibility stories, as some already do, the risk of accessibility-washing grows. Starship Technologies, whose delivery robots reportedly &lt;a href="https://www.bloomberg.com/news/articles/2019-11-19/why-tech-needs-more-designers-with-disabilities" target="_blank" rel="noopener"&gt;blocked&lt;/a&gt; a wheelchair user from curb cuts, maintains &lt;a href="https://www.starship.xyz/autonomous-robots-accessibility/" target="_blank" rel="noopener"&gt;a dedicated accessibility page&lt;/a&gt; touting the robots as a service for disabled customers. Similarly, Agentic AI companies may create agent-friendly content that introduces new accessibility barriers, while claiming credit for accessibility work they are not actually doing.&lt;/p&gt;&lt;p&gt;Political energy for real accessibility mandates may weaken if the public comes to believe AI is solving the problem as a byproduct of technological progress. Controversial AI deployments, in turn, may be legitimized through the language of inclusion. Accessibility is a civil right. Treating it as a fortunate side-effect of fashionable AI development is another way of saying that disabled people’s needs, on their own, were never considered sufficient.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name>Jonathan Zong, Frank Elavsky</name>
        </author>
        <media:content medium="image" url="https://cdn.sanity.io/images/3tzzh18d/production/c1babb1106f1b3a326ce42d56713bc8441eb4620-1200x675.jpg"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://feeds.captivate.fm/techpolicypress/</id>
            <title type="html">Tech Policy Press</title>
            <link href="https://www.techpolicy.press" rel="alternate" type="text/html"/>
            <updated>2026-10-02T05:25:52Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/Ss9N0Lq4zy9sTS9B4yUSldF/RrNH26SU/jxlX2klev4=_1a0ed64a221:7cec37a:f0b26a4</id>
        <title type="html">Mathematicians, Here’s a Way To Think About Your Existential Crisis</title>
        <published>2026-09-29T13:39:50Z</published>
        <updated>2026-09-30T08:44:19Z</updated>
        <link href="https://newsletter.kentbeck.com/p/mathematicians-heres-a-way-to-think" rel="alternate" type="text/html"/>
        <summary type="html">Two years ago programmers were all like, “What I do is code.</summary>
        <content type="html">&lt;p&gt;Two years ago programmers were all like, “What I do is code. Who I am is a coder. The genie codes. Now who am I?” Now mathematicians are all like, “What I do is prove theorems. Who I am is a theorem prover. The genie codes. Now who am I?” Here’s a framework I’ve found helpful for answer that question for myself.&lt;/p&gt;&lt;h2&gt;Features &amp;amp; Futures&lt;/h2&gt;&lt;p&gt;Something similar to the structure of the two fields, programming &amp;amp; math, is that there is a (relatively) visible part—features in the case of programming &amp;amp; proofs in the case of mathematics—&amp;amp; a huge (relatively) invisible part. The invisible part is understanding, education, simplification, enabling abstractions.&lt;/p&gt;&lt;p&gt;If all we work on is the visible part, progress on that visible part slows to a crawl. But nobody gets credit for the invisible work, so we rely on an ethos of work to ensure that the invisible work gets done and everyone can continue to make progress on the visible stuff.&lt;/p&gt;&lt;div data-component-name="VideoPlaceholder" data-attrs="{&amp;quot;mediaUploadId&amp;quot;:&amp;quot;00b493fc-6a89-44a2-9d2c-4a0117b0fab6&amp;quot;,&amp;quot;duration&amp;quot;:null}"&gt;&lt;/div&gt;&lt;p&gt;I call this hidden dimension “futures”, although “optionality” might be a more accurate word (if less alliterative).&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;In programming we also call this inverse of this axis “&lt;a href="https://wiki.c2.com/?TechnicalDebt"&gt;technical debt&lt;/a&gt;”, coined by Ward Cunningham. Sometimes you have to pay off your debts to get “interest” payments low enough that you can get back to progress on the principal.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;Genies Hate the Invisible&lt;/h2&gt;&lt;p&gt;I wonder if the angst among mathematicians is because:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;The genie is so good at the visible stuff&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Without the invisible work, visible progress eventually slows to a crawl, genie or no genie&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Nobody gets credit for the invisible work&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Put these together &amp;amp; you have a world where mathematicians no longer get any credit. The visible stuff is better done by machine. The invisible stuff is, well, invisible.&lt;/p&gt;&lt;h2&gt;So What?&lt;/h2&gt;&lt;p&gt;One constructive response to the identity crisis for programmers is to say hey I’m here to keep the genie on course. With the genie’s help I’m able to learn quicker than ever before. I still get to use my honed intuition to quickly stop useless directions. My reach has just extended. Yes, my work has changed, but my strategic decisions are more valuable than ever because they come more frequently.&lt;/p&gt;&lt;p&gt;I visualize this as taking breaks between visible progress to make invisible progress. I wonder if this way of thinking about their situation will help mathematicians take advantage of the powerful new tools available to them without losing their reason for being mathematicians. Good luck!&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;a data-component-name="Image2ToDOM" href="https://substackcdn.com/image/fetch/$s_!bD3w!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cc4b038-5f80-4617-b887-245824d3fe29_2734x1897.heic" target="_blank"&gt;&lt;div&gt;&lt;picture&gt;&lt;source sizes="100vw" type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bD3w!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cc4b038-5f80-4617-b887-245824d3fe29_2734x1897.heic 424w, https://substackcdn.com/image/fetch/$s_!bD3w!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cc4b038-5f80-4617-b887-245824d3fe29_2734x1897.heic 848w, https://substackcdn.com/image/fetch/$s_!bD3w!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cc4b038-5f80-4617-b887-245824d3fe29_2734x1897.heic 1272w, https://substackcdn.com/image/fetch/$s_!bD3w!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cc4b038-5f80-4617-b887-245824d3fe29_2734x1897.heic 1456w"&gt;&lt;img sizes="100vw" src="https://substackcdn.com/image/fetch/$s_!bD3w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cc4b038-5f80-4617-b887-245824d3fe29_2734x1897.heic" data-attrs="{&amp;quot;src&amp;quot;:&amp;quot;https://substack-post-media.s3.amazonaws.com/public/images/0cc4b038-5f80-4617-b887-245824d3fe29_2734x1897.heic&amp;quot;,&amp;quot;srcNoWatermark&amp;quot;:null,&amp;quot;fullscreen&amp;quot;:null,&amp;quot;imageSize&amp;quot;:null,&amp;quot;height&amp;quot;:1010,&amp;quot;width&amp;quot;:1456,&amp;quot;resizeWidth&amp;quot;:null,&amp;quot;bytes&amp;quot;:942733,&amp;quot;alt&amp;quot;:null,&amp;quot;title&amp;quot;:null,&amp;quot;type&amp;quot;:&amp;quot;image/heic&amp;quot;,&amp;quot;href&amp;quot;:null,&amp;quot;belowTheFold&amp;quot;:true,&amp;quot;topImage&amp;quot;:false,&amp;quot;internalRedirect&amp;quot;:&amp;quot;https://newsletter.kentbeck.com/i/216656928?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cc4b038-5f80-4617-b887-245824d3fe29_2734x1897.heic&amp;quot;,&amp;quot;isProcessing&amp;quot;:false,&amp;quot;align&amp;quot;:null,&amp;quot;offset&amp;quot;:false}" width="1456" alt="" srcset="https://substackcdn.com/image/fetch/$s_!bD3w!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cc4b038-5f80-4617-b887-245824d3fe29_2734x1897.heic 424w, https://substackcdn.com/image/fetch/$s_!bD3w!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cc4b038-5f80-4617-b887-245824d3fe29_2734x1897.heic 848w, https://substackcdn.com/image/fetch/$s_!bD3w!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cc4b038-5f80-4617-b887-245824d3fe29_2734x1897.heic 1272w, https://substackcdn.com/image/fetch/$s_!bD3w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cc4b038-5f80-4617-b887-245824d3fe29_2734x1897.heic 1456w" height="1010"&gt;&lt;/picture&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div&gt;&lt;hr&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Most teams don’t have a strategy problem. They have an adaptation problem.&lt;br&gt;&lt;/strong&gt;&lt;br&gt;Your plan was never going to survive contact with reality. The question is whether your organization bends or breaks when it doesn’t.&lt;/p&gt;&lt;p&gt;I help teams bend. &lt;em&gt;Adapt to Thrive.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Booking a handful of &lt;strong&gt;custom talks&lt;/strong&gt; and &lt;strong&gt;advisory engagements&lt;/strong&gt; now. I interview your people, measure your real software flows, and hand you the truth plus what to do about it.&lt;/p&gt;&lt;p&gt;Curious whether it fits? &lt;a href="https://kentbeck.com/kent-beck-sponsorship#inquire"&gt;Tell me about your team.&lt;/a&gt;&lt;/p&gt;</content>
        <author>
            <name>Kent Beck</name>
        </author>
        <media:content medium="image" url="https://lh3.googleusercontent.com/3q0X2i_Ys0ApnSOWIxD3A95oj10adYPrVaFO0K5VSNbD4CcTtIT0MmQyl2HHkV8Gzz7rhZ37XzEgwj5JTR7o9K0bCT6JgWQPYHS9BKI"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://kentbeck.substack.com/feed/</id>
            <title type="html">Software Design: Tidy First?</title>
            <link href="https://newsletter.kentbeck.com" rel="alternate" type="text/html"/>
            <updated>2026-09-30T08:44:19Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/uW6OVyMOU/Wf09ueJdtxBuVG3zPAAxiGRBuAOlsem8k=_1a0f10709e9:8453d58:f0b26a4</id>
        <title type="html">The death of web development education – Rescuing a field from disappearing</title>
        <published>2026-09-30T06:36:05Z</published>
        <updated>2026-09-30T08:40:37Z</updated>
        <category term="ai programming"/>
        <link href="https://molily.de/web-dev-education/" rel="alternate" type="text/html"/>
        <summary type="html">In a personal blog post, Baldur Bjarnason describes how he worked in various fields and positions: as an author, publisher, consultant, developer, educator, as well as radio and TV producer.
Many independent educators who used to earn a living from web development training and developer relations have reported that GenAI has ruined their business.</summary>
        <author>
            <name>znelson</name>
        </author>
        <media:content medium="image" url="https://lh3.googleusercontent.com/KQgc0zEhNz9X0yPHIDqiqPXG4-dJp_MCbMynrKOlJqjSoYh57Jz2xc-YiwNW-5LFLovALMLQy_F-y_ktKaa1t1wZg6xkv-jR2RNYEjg"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/http://feeds.pinboard.in/rss/popular/</id>
            <title type="html">Pinboard (popular bookmarks)</title>
            <link href="https://pinboard.in/popular/" rel="alternate" type="text/html"/>
            <updated>2026-09-30T08:40:37Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_1a0cddc1e72:11aaa:a6b1a075</id>
        <title type="html">The root scroller and how not to lose it</title>
        <published>2026-09-23T10:42:30Z</published>
        <updated>2026-09-23T10:42:35Z</updated>
        <link href="https://polypane.app/blog/the-root-scroller-and-how-not-to-lose-it/" rel="alternate" type="text/html"/>
        <summary type="html">Every page has one main scroll container that scrolls the page itself: the root scroll container, or root scroller for short. Because it scrolls the entire page…</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div id="article"&gt;&lt;p&gt;Every page has one main scroll container that scrolls the page itself: the root scroll container, or root scroller for short. Because it scrolls the entire page, the browser gives the root scroller a bunch of features that regular scroll containers don't get, and because they work automatically, most developers aren't aware of them.&lt;/p&gt;&lt;p&gt;It's easy to accidentally stop using the root scroller, and hard to notice when you have. Your 'page' still scrolls (visually at least) and you don't get any errors, but your site misses out on some features and that can make the user experience worse.&lt;/p&gt;&lt;p&gt;In this article we'll go over what the root scroller is, how it's different from other scroll containers, and how to fix it when you've accidentally opted out of it.&lt;/p&gt;&lt;p&gt;A scroll container is any element that lets you scroll through content that doesn't fit inside it (which sometimes is referred to as overflowing content).&lt;/p&gt;&lt;p&gt;An element becomes a scroll container when you give it an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/overflow"&gt;&lt;code&gt;overflow&lt;/code&gt;&lt;/a&gt; value of &lt;code&gt;auto&lt;/code&gt;, &lt;code&gt;scroll&lt;/code&gt; or &lt;code&gt;hidden&lt;/code&gt; and fixed (explicit height or width) or constrained (like max-width, grid areas or flex constraints) dimensions. Overflow set to &lt;code&gt;auto&lt;/code&gt; and &lt;code&gt;scroll&lt;/code&gt; means users can scroll it with the mouse (and keyboard, when focused). With &lt;code&gt;hidden&lt;/code&gt; they can't, but it's still a scroll container, and you can still scroll it programmatically.&lt;/p&gt;&lt;p&gt;When a scroll container sits inside the page or inside another scroll container, it's called a nested scroll container.&lt;/p&gt;&lt;p&gt;Try the different overflow values below to see how they affect the scroll container behavior.&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;label for="scroll-container-overflow"&gt;overflow&lt;/label&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;Scrolled&lt;/span&gt;&lt;span&gt;Not a scroll container&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;This box has a fixed height of 150px, so this text overflows it.&lt;/p&gt;&lt;p&gt;Change the overflow value above and see what happens to the overflowing content.&lt;/p&gt;&lt;p&gt;With &lt;code&gt;hidden&lt;/code&gt; you can't scroll it with your mouse or keyboard, but it's still a scroll container. Click the "scroll programmatically" button to see it scroll.&lt;/p&gt;&lt;p&gt;This is the last line, overflowing the container.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;When your content is taller than the viewport, the entire viewport becomes a scroll container. The specs call this the viewport's scrolling box, but browser engineers refer to it as the root scroller, and that's the name we'll use here.&lt;/p&gt;&lt;p&gt;The browser knows what the root scroller is, and &lt;code&gt;document.scrollingElement&lt;/code&gt; returns the element that represents it:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;document&lt;span&gt;.&lt;/span&gt;scrollingElement&lt;span&gt;;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In standards mode this is the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element and in quirks mode it's &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;. That never changes for a given document, so &lt;code&gt;document.scrollingElement&lt;/code&gt; is mostly just a convenient way to access the root scroller without having to check both elements or parsing the doctype.&lt;/p&gt;&lt;p&gt;When dealing with the &lt;code&gt;html&lt;/code&gt; and &lt;code&gt;body&lt;/code&gt; elements, the browser sometimes applies styles and overflow behavior differently than it would for other elements: styling sometimes gets propagated to the viewport rather than being set on the element itself. This propagation was introduced to ensure that the viewport reflects the intended styles even when the &lt;code&gt;html&lt;/code&gt; and &lt;code&gt;body&lt;/code&gt; elements do not fill the entire viewport.&lt;/p&gt;&lt;p&gt;One that you might have encountered before is that adding a background to &lt;code&gt;body&lt;/code&gt; will add it to the entire viewport even if body doesn't fill the full height. The background is "pushed back" to the root scroller.&lt;/p&gt;&lt;p&gt;That is, unless you also have a background set on the &lt;code&gt;html&lt;/code&gt; element, in which case &lt;em&gt;that&lt;/em&gt; background is applied to the viewport, and the one you set on the body behaves like a normal background. Try it out for yourself below:&lt;/p&gt;&lt;p&gt;The same happens to other properties that affect layout and scrolling. That propagation behavior is not something you commonly need to care about, but being aware of it can save you a few head scratchers.&lt;/p&gt;&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/overflow"&gt;&lt;code&gt;overflow&lt;/code&gt;&lt;/a&gt; CSS property controls how overflow works, and so creates scroll containers. The value set on &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; doesn't apply to the element itself but is propagated to the viewport. If &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; has the default value &lt;code&gt;overflow: visible&lt;/code&gt;, the overflow value of &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; is propagated instead. We'll need this rule later on.&lt;/p&gt;&lt;p&gt;There is nothing wrong with using nested scroll containers. Browsers treat them as independent elements with their own scrolling context, and a user can scroll them without affecting the rest of the page.&lt;/p&gt;&lt;p&gt;Sometimes though, you accidentally create a nested scroll container that encompasses the entire viewport. When you do that, a mouse wheel or touch scroll will scroll the nested container. When that happens, you lose special behavior given to the root scroller:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Scroll restoration.&lt;/strong&gt; When someone navigates back to your page, browsers restore the scroll position of the root scroller. Support for restoring nested scroll containers is inconsistent, so users often end up back at the top instead of where they were.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Keyboard scrolling.&lt;/strong&gt; Space, Page Down and the arrow keys scroll the root scroller by default. A nested scroll container needs to be clicked or focused into first.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;window.scrollY&lt;/code&gt; and scroll events on &lt;code&gt;window&lt;/code&gt; only report the root scroller.&lt;/strong&gt; Methods like &lt;code&gt;window.scrollTo()&lt;/code&gt;, &lt;code&gt;window.scrollBy()&lt;/code&gt; only apply to the root scroller. So anything that relies on these for getting and setting the scroll position will not work correctly with nested scroll containers.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Printing and screenshots.&lt;/strong&gt; Printing uses the height of the root scroller. A page locked to the viewport height prints as a single page and cuts off the rest. Full-page screenshots have the same problem.&lt;/li&gt;&lt;/ul&gt;&lt;h4 id="on-mobile-specifically"&gt;On mobile specifically&lt;/h4&gt;&lt;p&gt;Though scroll-restoration is annoying everywhere, mobile browsers include more scroll-specific functionality that depends on the root scroller, and it's where things will feel more 'broken' as users interact with your website.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Mobile browser UI.&lt;/strong&gt; On phones the address bar and toolbar collapse when the root scroller scrolls. With a nested scroll container they stay visible, so your users have less screen space. (Mobile browser UI has a few more quirks, which we covered in &lt;a href="https://polypane.app/blog/using-safe-area-inset-to-build-mobile-safe-layouts/"&gt;using safe-area-inset to build mobile safe layouts&lt;/a&gt;.)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Tap to scroll to top.&lt;/strong&gt; On iOS, tapping the status bar scrolls the root scroller to the top. It does nothing for nested scroll containers.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Pull to refresh and overscroll.&lt;/strong&gt; These also only work on the root scroller.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The example below simulates these behaviors, toggle between the root and nested scrollers and see the difference when doing those actions above: scrolling to move the browser UI out of the way, clicking the top bar to scroll back to the top, and pulling down to refresh the page. When scrolled, reloading the page with the reload button will also restore the scroll position for the root scroller, but not for the nested scroller.&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Card 1&lt;/p&gt;&lt;p&gt;Card 2&lt;/p&gt;&lt;p&gt;Card 3&lt;/p&gt;&lt;p&gt;Card 4&lt;/p&gt;&lt;p&gt;Card 5&lt;/p&gt;&lt;p&gt;Card 6&lt;/p&gt;&lt;p&gt;Card 7&lt;/p&gt;&lt;p&gt;Card 8&lt;/p&gt;&lt;p&gt;Card 9&lt;/p&gt;&lt;p&gt;Card 10&lt;/p&gt;&lt;p&gt;Card 11&lt;/p&gt;&lt;p&gt;Card 12&lt;/p&gt;&lt;p&gt;Card 13&lt;/p&gt;&lt;p&gt;Card 14&lt;/p&gt;&lt;p&gt;Card 15&lt;/p&gt;&lt;p&gt;Card 16&lt;/p&gt;&lt;p&gt;Card 17&lt;/p&gt;&lt;p&gt;Card 18&lt;/p&gt;&lt;p&gt;Card 19&lt;/p&gt;&lt;p&gt;Card 20&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id="how-you-accidentally-opt-out"&gt;How you accidentally opt out&lt;/h2&gt;&lt;p&gt;The most common version is adding an app shell or 'wrapper' like this:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;html,
body&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 100%&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;overflow&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; hidden&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;.page-wrapper&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 100vh&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;overflow&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; auto&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When you look at your page it still looks the same, but you can see that the dashed border i added around body is fully visible at the bottom, while you would expect it to be pushed down by the content.&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;scroll container&lt;/span&gt;&lt;span&gt;scroll to find out&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The content still scrolls, but it's &lt;code&gt;.page-wrapper&lt;/code&gt; that is scrolling instead of the viewport. This is usually done on purpose, for a fixed header and sidebar with a scrolling content area, a sticky footer layout, or to prevent the page from scrolling behind a modal. Sometimes a framework sets it up for you.&lt;/p&gt;&lt;p&gt;The result is a root scroller that doesn't scroll, since that responsibility has been taken over by the nested scroll container.&lt;/p&gt;&lt;h3 id="accidentally-opting-out-with-overflow-propagation"&gt;Accidentally opting out with overflow propagation&lt;/h3&gt;&lt;p&gt;Another common way to accidentally opt out of the root scroller is by setting that &lt;code&gt;overflow-x&lt;/code&gt; property on both the &lt;code&gt;html&lt;/code&gt; and &lt;code&gt;body&lt;/code&gt; elements to prevent horizontal overflows, along with &lt;code&gt;height: 100%&lt;/code&gt; to make the page at least as tall as the viewport and keep footers are where you want them. Variants of this can be found in many CSS boilerplates and frameworks.&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;html,
body&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 100%&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;overflow-x&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; hidden&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because of the propagation rule, the overflow of &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; goes to the viewport. Since &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; is no longer set to &lt;code&gt;overflow:visible&lt;/code&gt;, the overflow of &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; stays on &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;. And when one axis is &lt;code&gt;hidden&lt;/code&gt;, the other axis computes to &lt;code&gt;auto&lt;/code&gt;. So &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; becomes a scroll container.&lt;/p&gt;&lt;p&gt;Now just the &lt;code&gt;overflow-x: hidden&lt;/code&gt; declaration on &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; is not a problem. &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; still grows with its content because of its natural block layout and doesn't become a scroll container.&lt;/p&gt;&lt;p&gt;That is, until you give it a fixed height with &lt;code&gt;height: 100%&lt;/code&gt;. Now &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; is only as tall as the viewport and &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; scrolls instead of the root scroller. Both declarations are common on their own, so it's easy to end up with the combination without realising it.&lt;/p&gt;&lt;p&gt;Try the example below to see how the overflow is both propagated to the viewport, and causes the default values to change. See if you can guess when you've opted out of the root scroller, then scroll to see if you were right.&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;scroll container&lt;/span&gt;&lt;span&gt;…&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;propagated to viewport&lt;/span&gt;&lt;span&gt;…&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;html&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;overflow-x&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; visible&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;body&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;overflow-x&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; visible&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Scroll down your page a bit and run this in the console:&lt;/p&gt;&lt;p&gt;If this returns 0 while you've clearly scrolled, you've opted out of the root scroller and something else is scrolling. Here's that check side by side, for a root scroller and for a nested wrapper:&lt;/p&gt;&lt;p&gt;To find out which element is scrolling, listen for scroll events in the capture phase. Scroll events on elements don't bubble, but a capturing listener on the document does catch all of them.&lt;/p&gt;&lt;p&gt;Paste this into your console, then scroll:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;document&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;'scroll'&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;=&amp;gt;&lt;/span&gt; console&lt;span&gt;.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;e&lt;span&gt;.&lt;/span&gt;target&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;{&lt;/span&gt; &lt;span&gt;capture&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;true&lt;/span&gt; &lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If it logs &lt;code&gt;#document&lt;/code&gt;, the root scroller is working. If it logs an element like &lt;code&gt;div.page-wrapper&lt;/code&gt;, that's your nested scroll container.&lt;/p&gt;&lt;p&gt;The &lt;a href="https://polypane.app/docs/elements-panel/"&gt;Elements panel&lt;/a&gt; in Polypane shows a "scroll" badge next to elements that are scroll containers, so you can spot them while going through the DOM tree.&lt;/p&gt;&lt;video src="https://polypane.app/static/scrollhighlight-73cd5ddfb21c593467db7eac76355dde.mp4"&gt;&lt;/video&gt;&lt;p&gt;Starting in Polypane 31, elements that are currently being scrolled will have their scroll badge highlighted so you know exactly which element is handling the scroll.&lt;/p&gt;&lt;p&gt;To list all vertical scroll containers on a page at once, run this in the console:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;document&lt;span&gt;.&lt;/span&gt;&lt;span&gt;querySelectorAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;'*'&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;el&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;=&amp;gt;&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;const&lt;/span&gt; &lt;span&gt;{&lt;/span&gt; overflowY &lt;span&gt;}&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;getComputedStyle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;el&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;return&lt;/span&gt; &lt;span&gt;[&lt;/span&gt;&lt;span&gt;'auto'&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;'scroll'&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;'hidden'&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;overflowY&lt;span&gt;)&lt;/span&gt; &lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt; el&lt;span&gt;.&lt;/span&gt;scrollHeight &lt;span&gt;&amp;gt;&lt;/span&gt; el&lt;span&gt;.&lt;/span&gt;clientHeight&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once you know what you're looking for, seeing it on a real phone is easy too. If the address bar doesn't collapse when you scroll, or tapping the status bar doesn't scroll you back up, the root scroller isn't being used.&lt;/p&gt;&lt;h3 id="testing-it-in-polypane"&gt;Testing it in Polypane&lt;/h3&gt;&lt;p&gt;Most of these issues only show up on touch devices, with a keyboard or when printing, so you won't see them with a mouse on a desktop viewport. But they'll still feel broken to users interacting with your site in those ways.&lt;/p&gt;&lt;p&gt;Test your pages at mobile sizes with touch events enabled using &lt;a href="https://polypane.app/docs/emulation/"&gt;device emulation&lt;/a&gt;, try scrolling with only your keyboard, and run the capture listener above.&lt;/p&gt;&lt;h3 id="another-way-it-surfaces-in-polypane"&gt;Another way it surfaces in Polypane&lt;/h3&gt;&lt;p&gt;If you use Polypane, &lt;a href="https://polypane.app/docs/synced-interactions/"&gt;scroll syncing&lt;/a&gt; and &lt;a href="https://polypane.app/docs/making-screenshots/"&gt;full height screenshots&lt;/a&gt; can give it away too.&lt;/p&gt;&lt;p&gt;Polypane syncs the scroll position of all scroll containers, root and nested, across panes.&lt;/p&gt;&lt;p&gt;But if you accidentally create a nested scroll container at a specific breakpoint, for example by setting &lt;code&gt;overflow&lt;/code&gt; inside a media query, the page is scrolled by the root scroller in some panes and by the newly created nested scroll container in others. Those aren't the same scroll container, so their scroll positions can't be synced.&lt;/p&gt;&lt;p&gt;If scrolling stays in sync between your desktop panes but your mobile pane doesn't follow along (or the other way around), check which element is scrolling in that pane using the methods described above.&lt;/p&gt;&lt;p&gt;For full height screenshots, Polypane takes the height of the root scroller to determine the total height of the page. If that's just a single viewport, then the resulting full height screenshot will be too.&lt;/p&gt;&lt;p&gt;You usually end up with a nested scroll container while building a specific layout. Each of these layouts can be built while keeping the root scroller, so let's go through the common ones.&lt;/p&gt;&lt;h3 id="make-the-page-fill-at-least-the-full-screen"&gt;Make the page fill at least the full screen&lt;/h3&gt;&lt;p&gt;For example so the footer sits at the bottom of the screen on short pages. A common way to do that is to make a wrapper exactly as tall as the viewport and let the content scroll inside it:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;html,
body&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 100%&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;overflow&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; hidden&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;.page-wrapper&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;display&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; flex&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;flex-direction&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; column&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 100vh&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;main&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;flex&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 1&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;overflow&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; auto&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What you actually want is a page that is &lt;em&gt;at least&lt;/em&gt; as tall as the screen, so use &lt;code&gt;min-height&lt;/code&gt; instead of &lt;code&gt;height&lt;/code&gt; and let the document grow with its content:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;.page-wrapper&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;display&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; flex&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;flex-direction&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; column&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;min-height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 100svh&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;main&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;flex&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 1&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You also don't really need the &lt;code&gt;overflow: hidden&lt;/code&gt; on &lt;code&gt;html&lt;/code&gt; and &lt;code&gt;body&lt;/code&gt; either. Using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/length#relative_length_units_based_on_viewport"&gt;&lt;code&gt;svh&lt;/code&gt; or &lt;code&gt;dvh&lt;/code&gt;&lt;/a&gt; instead of &lt;code&gt;vh&lt;/code&gt; also avoids the mobile issue where &lt;code&gt;100vh&lt;/code&gt; is taller than the visible area. We wrote more about the small viewport units when we added them to &lt;a href="https://polypane.app/blog/polypane-26-accurate-device-emulation-with-safe-area-and-small-viewport-units/"&gt;Polypane's device emulation&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;We can go a bit further too, since you probably don't even need a dedicated wrapper element for the page at all. Remove the wrapper entirely and apply the &lt;code&gt;min-height&lt;/code&gt; and &lt;code&gt;flex&lt;/code&gt; properties directly to the &lt;code&gt;body&lt;/code&gt; and &lt;code&gt;main&lt;/code&gt; elements:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;body&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;min-height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 100svh&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;display&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; flex&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;flex-direction&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; column&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;main&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;flex&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 1&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;body&lt;/code&gt; can be a wrapper just as much as a dedicated &lt;code&gt;.page-wrapper&lt;/code&gt; element, simplifying your layout and keeping the root scroller intact by not restricting the height of the body element.&lt;/p&gt;&lt;p&gt;This is the app shell layout: a header at the top, a sidebar on the side and only the content area scrolling. It's often built as a grid that's exactly as tall as the viewport, with the content area as a scroll container. This fits a nice mental model of the viewport as a fixed frame. The web isn't a fixed frame though. The viewport is just that, a view into a part of the document.&lt;/p&gt;&lt;p&gt;You can keep the root scroller intact by using &lt;code&gt;position: sticky&lt;/code&gt; and using a min-height on the sidebar to get the same layout. The page scrolls normally and the header and sidebar stick to the viewport:&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;scroll event target&lt;/span&gt;&lt;span&gt;scroll to find out&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;.app&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;display&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; grid&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;grid-template-columns&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 100px 1fr&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;grid-template-rows&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; auto 1fr&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;100vh&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;.header&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;grid-column&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 1 / -1&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;.sidebar&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;grid-row&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 2 / -1&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;.content&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;overflow&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; auto&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The sidebar can still scroll on its own if it's long (becoming its own scroll container). That's fine because it's a secondary scroll container, not the page itself. Content inside the main area will scroll with the page as expected.&lt;/p&gt;&lt;p&gt;If your sticky elements don't stick, check &lt;a href="https://polypane.app/blog/getting-stuck-all-the-ways-position-sticky-can-fail/"&gt;all the ways position: sticky can fail&lt;/a&gt;. For grid layouts, it's very easy to create that unexpected scroll container (making sticky elements not stick as intended) or to have the element not able to get stuck, because it stretches the full grid area.&lt;/p&gt;&lt;p&gt;Something on the page is wider than the viewport and causes a horizontal scrollbar, so you hide it:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;html,
body&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 100%&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;overflow-x&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; hidden&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As we saw earlier, when &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; also has a fixed height, this turns it into the element that scrolls the page because setting &lt;code&gt;overflow: hidden&lt;/code&gt; in one direction automatically changes it from &lt;code&gt;visible&lt;/code&gt; to &lt;code&gt;auto&lt;/code&gt; in the other direction.&lt;/p&gt;&lt;p&gt;A quick solution is to use &lt;code&gt;clip&lt;/code&gt; instead:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;html,
body&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 100%&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;overflow-x&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; clip&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;clip&lt;/code&gt; cuts off overflowing content like &lt;code&gt;hidden&lt;/code&gt; does, but it doesn't create a scroll container. The &lt;code&gt;overflow&lt;/code&gt; of the other direction is still set to &lt;code&gt;visible&lt;/code&gt;, and its contents will just overflow outside the body's height.&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th scope="col"&gt;overflow-x&lt;/th&gt;&lt;th scope="col"&gt;overflow-y&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;body&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;…&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;html,
body&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; 100%&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;overflow-x&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; hidden&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;h4 id="quick-fix-vs-proper-fix"&gt;Quick fix vs. proper fix&lt;/h4&gt;&lt;p&gt;This is a &lt;em&gt;quick&lt;/em&gt; fix, but it isn't a &lt;em&gt;good&lt;/em&gt; fix, since it hides the symptom (the bar is still overflowing).&lt;/p&gt;&lt;p&gt;Finding and fixing the element that causes the overflow is the better solution, and our article on &lt;a href="https://polypane.app/blog/strategies-for-dealing-with-horizontal-overflows/"&gt;strategies for dealing with horizontal overflows&lt;/a&gt; shows you how.&lt;/p&gt;&lt;p&gt;Polypane also &lt;a href="https://polypane.app/docs/horizontal-overflow/"&gt;detects horizontal overflow&lt;/a&gt; for you and highlights the element that causes it.&lt;/p&gt;&lt;h3 id="stop-the-page-scrolling-behind-a-modal"&gt;Stop the page scrolling behind a modal&lt;/h3&gt;&lt;p&gt;When a modal is open, scrolling inside it shouldn't scroll the page behind it. Some sites solve this by keeping the page in a wrapper with its own scroll container permanently, so they can lock the wrapper while the modal is open.&lt;/p&gt;&lt;p&gt;You only need to lock the page while the modal is open. With &lt;a href="https://polypane.app/blog/decoding-css-selectors-has-not-vs-not-has/"&gt;&lt;code&gt;:has()&lt;/code&gt;&lt;/a&gt; you can do that in CSS:&lt;/p&gt;&lt;div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&lt;span&gt;html&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;scrollbar-gutter&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; stable&lt;span&gt;;&lt;/span&gt;

  &lt;span&gt;&amp;amp;:has(dialog[open])&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;overflow&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; hidden&lt;span&gt;;&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hiding the scrollbar can make the page shift sideways, so we add &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-gutter"&gt;&lt;code&gt;scrollbar-gutter: stable&lt;/code&gt;&lt;/a&gt; to &lt;code&gt;html&lt;/code&gt; to keep the space reserved.&lt;/p&gt;&lt;p&gt;Toggle the lock off and try scrolling behind an open dialog — then turn it back on:&lt;/p&gt;&lt;div&gt;&lt;p&gt;&lt;label for="modal-lock"&gt;Toggle scroll locking on the HTML&lt;/label&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;dialog open&lt;/span&gt;&lt;span&gt;no&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;html overflow-y&lt;/span&gt;&lt;span&gt;visible&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;scrollY&lt;/span&gt;&lt;span&gt;0px&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Though most of this article is spent explaining how to avoid creating unwanted scroll containers, there's plenty of situations where you'll want to use them (like the scrollable sidebar in the example above, or a chat window).&lt;/p&gt;&lt;p&gt;What you want to avoid is accidentally creating a scroll container that covers your whole viewport, but there are plenty of other places where nested scroll containers are perfectly fine.&lt;/p&gt;&lt;p&gt;If you want to understand how scroll containers fit in with the rest of CSS layout, read &lt;a href="https://polypane.app/blog/understanding-the-fundamentals-of-css-layout/"&gt;understanding the fundamentals of CSS layout&lt;/a&gt;.&lt;/p&gt;&lt;h2 id="use-the-root-scroller-for-the-main-page"&gt;Use the root scroller for the main page&lt;/h2&gt;&lt;p&gt;I hope this article has helped you understand why using the root scroller for the main scrolling area of your page is important and beneficial for your visitors, and helped you get a better grasp of managing scroll behavior in your own projects.&lt;/p&gt;&lt;p&gt;Check out some of the tools in Polypane to help you check and manage scroll behavior effectively:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The code snippets above executed in our &lt;a href="https://polypane.app/docs/console/"&gt;console panel&lt;/a&gt; make it really easy to catch differences introduced by media breakpoints, and help you make more resilient websites.&lt;/li&gt;&lt;li&gt;With the &lt;a href="https://polypane.app/docs/device-emulation/"&gt;device emulation&lt;/a&gt; in Polypane, you can see how your page behaves across different screen sizes and orientations, ensuring that the root scroller remains consistent and effective.&lt;/li&gt;&lt;li&gt;The &lt;a href="https://polypane.app/docs/elements-panel/"&gt;elements panel&lt;/a&gt; in Polypane allows you to inspect and manipulate the DOM, making it easier to understand how scroll containers are applied and how they affect your layout.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;By combining these tools, you can confidently manage scroll behavior and create more robust and user-friendly web experiences.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://polypane.app/og-images/the-root-scroller-and-how-not-to-lose-it.png"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://polypane.app/rss.xml</id>
            <title type="html">polypane.app</title>
            <link href="https://polypane.app" rel="alternate" type="text/html"/>
            <updated>2026-09-23T10:42:35Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_1a08075dd88:2c9c43:bc429c8</id>
        <title type="html">Build a digitally sovereign second brain</title>
        <published>2026-09-08T10:00:03Z</published>
        <updated>2026-09-08T10:00:07Z</updated>
        <link href="https://www.raspberrypi.com/news/build-a-digitally-sovereign-second-brain/" rel="alternate" type="text/html"/>
        <summary type="html">Build a system to keep information and notes organised, searchable, synchronised, and backed up using Raspberry Pi.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;
        
&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;&lt;em&gt;We thought we’d go big this &lt;a href="https://www.google.com/search?q=maker+monday+raspberry+pi&amp;amp;oq=make&amp;amp;gs_lcrp=EgZjaHJvbWUqCAgAEEUYJxg7MggIABBFGCcYOzIGCAEQRRg5MgYIAhAjGCcyDQgDEAAYkQIYgAQYigUyBggEEEUYPTIGCAUQRRhAMgYIBhBFGEAyBggHEEUYQNIBCDE1MDVqMGo0qAIAsAIA&amp;amp;sourceid=chrome&amp;amp;source=chrome.ob&amp;amp;ie=UTF-8"&gt;Maker Monday&lt;/a&gt;, what with it being the serious business of Back to School and all. Here’s a tutorial from the latest issue of &lt;/em&gt;&lt;a href="https://magazine.raspberrypi.com/"&gt;Raspberry Pi Official Magazine&lt;/a&gt;&lt;em&gt; that shows you how to keep everything in your brain organised and searchable, &lt;strong&gt;&lt;em&gt;from notes &lt;/em&gt;&lt;/strong&gt;to&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;&lt;strong&gt;&lt;em&gt;appointments &lt;/em&gt;&lt;/strong&gt;and&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;&lt;strong&gt;&lt;em&gt;ideas&lt;/em&gt;&lt;/strong&gt;.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;A while ago, we read the book &lt;em&gt;&lt;a href="https://www.buildingasecondbrain.com/"&gt;Building a Second Brain&lt;/a&gt;&lt;/em&gt; by Tiago Forte (&lt;strong&gt;Figure 1&lt;/strong&gt;). It starts with the idea that we are all a little overloaded and bombarded with information, and how overwhelming it can be to not be able to retrieve the information that we need at a later time. The foundation of a &lt;em&gt;Second Brain&lt;/em&gt; is to increase the capture and cataloguing rate of information, and the book delves into lots of custom systems to tag, organise, prioritise, and retrieve information. This Personal Knowledge Management (PKM) idea feels similar to the ideas of David Allen and the older, but still brilliant, ‘Getting Things Done’ system. &lt;/p&gt;






&lt;div class="wp-block-image"&gt;
&lt;figure&gt;&lt;img src="https://www.raspberrypi.com/app/uploads/2026/09/logseq_graph_uconsole-Large-1024x768.jpeg" alt="" class="wp-image-139907"&gt;&lt;figcaption class="wp-element-caption"&gt;Logseq running on Raspberry Pi OS; this is a ‘graph view’ that shows all your tagged information, along with the connections between the tags&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;


&lt;p class="wp-block-paragraph"&gt;At the heart of these PKM systems is the concept of ubiquitous capture: the idea that, as a start, we need to capture the information of interest. This is also an idea in journalling and is basically being proactive about note-taking. The question then is how to store these bits of information. There are lots of options. &lt;/p&gt;



&lt;h2 class="wp-block-heading"&gt;The end of sticky notes&lt;/h2&gt;



&lt;p class="wp-block-paragraph"&gt;There’s nothing wrong with a pile of sticky notes (or index cards, which are the author’s favourite) and a pen; however, you can quickly end up with a huge pile of notes which need to be physically stored and searched. Of course, lots of people have moved to software applications. Many of the popular operating systems for mobile devices will include some form of note-taking application, and there are many that require you to make an account and store your data in the cloud, so it’s a nice challenge to build a system that doesn’t store your information on another person’s computer. Finally, the solutions we explore here use open source projects so we can examine the code should we need to maximise our privacy and security. &lt;/p&gt;






&lt;div class="wp-block-image"&gt;
&lt;figure&gt;&lt;img src="https://www.raspberrypi.com/app/uploads/2026/09/main-800x600.jpg" alt="" class="wp-image-139909"&gt;&lt;figcaption class="wp-element-caption"&gt;&lt;strong&gt;Figure 1: &lt;/strong&gt;Inspired by the Tiago Forte book &lt;em&gt;Building a Second Brain&lt;/em&gt;, it’s quite possible to create a personal knowledge management system across Raspberry Pi devices&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;


&lt;p class="wp-block-paragraph"&gt;The solution we are going to explore is centred around &lt;a href="https://logseq.com/"&gt;Logseq&lt;/a&gt;, a privacy-focused open source knowledge management system. It’s a really rich piece of software with more features than most people will use, and we’ll only really cover the basics, although the basics are incredibly powerful. Later in the article we’ll use &lt;a href="https://syncthing.net/"&gt;Syncthing&lt;/a&gt;, another open source project, to synchronise our second brain created in Logseq across multiple devices. Syncthing is a peer-to-peer continuous file synchronisation tool, which means that it can sync data without passing that data through the cloud/a company’s server. It also encrypts and decrypts the data in transit, so you are nice and secure whilst the data is moving. It’s totally OK to just install Logseq on one machine and have a play with it to see if it can meet your needs.&lt;/p&gt;






&lt;div class="wp-block-image"&gt;
&lt;figure&gt;&lt;img src="https://www.raspberrypi.com/app/uploads/2026/09/pi500_logseq-1024x768.jpg" alt="" class="wp-image-139910"&gt;&lt;figcaption class="wp-element-caption"&gt;&lt;strong&gt;Figure 2:&lt;/strong&gt; A Raspberry Pi 500 is a good target for your first attempt at installing Logseq and Syncthing&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;


&lt;p class="wp-block-paragraph"&gt;We began by installing Logseq on a &lt;a href="https://www.raspberrypi.com/products/raspberry-pi-500/"&gt;Raspberry Pi 500&lt;/a&gt; (&lt;strong&gt;Figure 2&lt;/strong&gt;), but note that the procedure outlined below is the same for any Raspberry Pi board; so, for example, we have this system currently set up on the Raspberry Pi 500, a &lt;a href="https://www.raspberrypi.com/products/raspberry-pi-zero/"&gt;Raspberry Pi Zero&lt;/a&gt;, and our Uconsole, which contains the &lt;a href="https://www.raspberrypi.com/products/compute-module-4/?variant=raspberry-pi-cm4001000"&gt;Raspberry Pi Compute Module 4 board&lt;/a&gt;.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;Currently, the only option to install a build of Logseq is via Snap. So, if working from a fresh Raspberry Pi OS installation, we’ll need to install the Snap package manager. If you already have Snap installed on your system, then you can skip to installing Logseq. &lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;To install Snap and then Logseq, open the Terminal application and, at the prompt, enter: &lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;$ sudo apt update&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;We can then install the latest version of Snap using: &lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;$ sudo apt install snapd&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;We then need to reboot Raspberry Pi 500:&lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;$ sudo reboot&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;Once the machine has rebooted, open the Terminal application; we need to install a Snap package called ‘snapd’:&lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;$ sudo snap install snapd&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;With Snap installed and set up, we can then go ahead and install Logseq using:&lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;$ sudo snap install logseq –channel =og&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;With Logseq now installed, you will be able to launch it using the desktop applications menu at Accessories &amp;gt; Logseq Desktop. On launch, Logseq will open a demo page showing a journal-style entry for the current date (&lt;strong&gt;Figure 3&lt;/strong&gt;). In this entry is a basic tutorial on how Logseq works and some of its functions. &lt;/p&gt;






&lt;div class="wp-block-image"&gt;
&lt;figure&gt;&lt;img src="https://www.raspberrypi.com/app/uploads/2026/09/logseq_open_first-1024x576.png" alt="" class="wp-image-139908"&gt;&lt;figcaption class="wp-element-caption"&gt;&lt;strong&gt;Figure 3: &lt;/strong&gt;When you first open Logseq, you land on an informative demo page&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;


&lt;p class="wp-block-paragraph"&gt;In Logseq terminology, when you create a collection of notes and potentially other assets, you insert them into a ‘graph’. Scrolling on the current demo page, you should find a section where Logseq wants us to specify a folder it can use to contain all our information. You can use an existing folder, but it’s probably better to create a new folder. Click to ‘add a graph’ and then give your folder the title that you want your second brain system to be called; we went with ‘2nd_Brain’, rather unimaginatively. This OG version of Logseq will, in this specified folder, store all the data you input into the system. It essentially creates Markdown files and also stores any uploaded assets (PDFs, pictures, audio, etc.) to an assets subfolder. This means that you could manually copy this entire folder, move it to another machine, and open it in another installation of Logseq. &lt;/p&gt;



&lt;h2 class="wp-block-heading"&gt;Filling the brain with information&lt;/h2&gt;



&lt;p class="wp-block-paragraph"&gt;With the file specified and a new graph created, let’s put some basic information into our second brain system. Whenever you open Logseq, it will create an empty journal page for the current day. Each of these journal dates will become a page item in our system. So let’s type something in the currently empty journal page. &lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;As an example, we typed a note: ‘Spotted a book I’d like to read today, called “How to Build a Second Brain” by Tiago Forte’ (&lt;strong&gt;Figure 4&lt;/strong&gt;). Now imagine we wrote a note about a different book every day. Currently the only way to return to this piece of information in the future is if we remember the date or use the search function. If we’d forgotten the name of the book, however, we could only really search for terms like ‘book’, which might generate a lot of returned results. What we need is to tag our information. Tagging is the secret sauce of Logseq that makes it incredibly useful. &lt;/p&gt;






&lt;div class="wp-block-image"&gt;
&lt;figure&gt;&lt;img src="https://www.raspberrypi.com/app/uploads/2026/09/untagged_entry-1024x576.png" alt="" class="wp-image-139911"&gt;&lt;figcaption class="wp-element-caption"&gt;&lt;br&gt;&lt;strong&gt;Figure 4: &lt;/strong&gt;Adding a note without a tag is fine, but the only way to rediscover that tag is to either remember the date it was made or the exact contents&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;


&lt;p class="wp-block-paragraph"&gt;To add a tag to a piece of information, we place the tag term inside two pairs of square brackets. Let’s return to the line we entered earlier and write at the end of the line (though tags can be placed anywhere in text) ‘[[Books To Read]]’ and then press &lt;strong&gt;ENTER&lt;/strong&gt;. Note that the tag text will become a clickable link (&lt;strong&gt;Figure 5&lt;/strong&gt;). If we click that tag, the link then takes us to a new page, as every tag we add to Logseq becomes a new page. On the new &lt;strong&gt;[[Books To Read]]&lt;/strong&gt; page, we will see the reference to our line, ‘… “How to Build a Second Brain” by Tiago Forte’. If we click the back arrow in the upper toolbar, we can navigate back to our journal. Let’s add a second line, ‘Another book I’d like to read, “Design an RP2040 Board with KiCad” by Jo Hinchliffe’. This line will always be listed in the current journal page, but again we can add the &lt;strong&gt;[[Books To Read]]&lt;/strong&gt; tag as we did previously and it will be populated to that tag page. If we write a book title every day into separate journal entries, so long as we use the &lt;strong&gt;[[Books To Read]]&lt;/strong&gt; tag, they will all appear on the tag page (&lt;strong&gt;Figure 6&lt;/strong&gt;). Note that in the image we made a typo in the book title; in a way, this doesn’t matter — just be sure to spell your tags correctly! &lt;/p&gt;






&lt;div class="wp-block-image"&gt;
&lt;figure&gt;&lt;img src="https://www.raspberrypi.com/app/uploads/2026/09/tagged_info-1024x576.png" alt="" class="wp-image-139913"&gt;&lt;figcaption class="wp-element-caption"&gt;&lt;strong&gt;Figure 5: &lt;/strong&gt;Adding a tag creates a new page for each tag in which tagged information is listed and linked&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;


&lt;p class="wp-block-paragraph"&gt;However, tags are free and unlimited, so it’s worth tagging information widely with lots of tags, thinking about how you might remember them in the future. As an example, in our second brain Logseq setup, we have some Customs and Excise tariff numbers; these numbers are unique for different types of goods that you sell, and it can be quite hard when faced with a new item to research and identify the correct tariff number as they are quite complex and there are a lot of them. It’s definitely the type of information you want to keep note of rather than have to discover every time. If we consider how we might think about these numbers, it’s pretty clear to tag them as &lt;strong&gt;[[Tariff]]&lt;/strong&gt;, but sometimes we might think &lt;strong&gt;[[Customs]]&lt;/strong&gt; or might want the tariff number for a particular product or project, so we might use the &lt;strong&gt;[[Product Name]]&lt;/strong&gt; as a tag. Adding multiple tags to items of information increases your chances of finding your way back to the info when needed, as the same piece of information will appear regardless of which tag you remember or use in the future. &lt;/p&gt;






&lt;div class="wp-block-image"&gt;
&lt;figure&gt;&lt;img src="https://www.raspberrypi.com/app/uploads/2026/09/books_to_read-1024x576.png" alt="" class="wp-image-139904"&gt;&lt;figcaption class="wp-element-caption"&gt;&lt;br&gt;&lt;strong&gt; Figure 6: &lt;/strong&gt;The ‘Books To Read’ tag page begins to collect all the inputs listed with that tag&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;


&lt;p class="wp-block-paragraph"&gt;We need to move on to other aspects of the system, but it’s worth noting that you can type ‘/Upload an asset’ (or rather start by writing ‘/U’, which will autocomplete) to add files of pretty much any type to an entry. You can then tag these in the usual way, and the asset will be copied to the assets folder in your designated directory. This means you don’t have to worry about copying and pasting items, or losing links to items, as they are all stored in the folder. Finally, note that you can embed links and videos, and even highlight and annotate PDFs, in Logseq, tagging the exact part of a PDF to return to later. &lt;/p&gt;






&lt;div class="wp-block-image"&gt;
&lt;figure&gt;&lt;img src="https://www.raspberrypi.com/app/uploads/2026/09/syncthing1-1024x576.png" alt="" class="wp-image-139912"&gt;&lt;figcaption class="wp-element-caption"&gt;&lt;br&gt;&lt;strong&gt;Figure 7: &lt;/strong&gt;Syncthing Web UI is the admin console for Syncthing, allowing you to connect devices and share directories and files&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;


&lt;p class="wp-block-paragraph"&gt;So we have the basis of our second brain setup, but we’d like it to be accessible on more than one device. Our approach for this will be to install Logseq on another device, and then use Syncthing to synchronise folders so that our second brain folder is on both devices and up to date (&lt;strong&gt;Figure 7&lt;/strong&gt;). &lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;To install Syncthing on Raspberry Pi, it’s best to first run:&lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;$ sudo apt update

$ sudo apt upgrade&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;…to make sure our system is up to date. Then we need to add the ‘apt-transport-https’ package to the apt service by running:&lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;$ sudo apt install apt-transport-https&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;We then need to store the Syncthing GPG keys on Raspberry Pi in our &lt;strong&gt;keyrings&lt;/strong&gt; directory. In a nutshell, this means that the Syncthing packages we install are legitimate and are sourced from the right place. To do this, run the following command:&lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;$ curl -s https://syncthing.net/release-key.txt | gpg --dearmor | sudo tee /usr/share/keyrings/syncthing-archive-keyring.gpg &amp;gt;/dev/null&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;We can now add the Syncthing repository with: &lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;$ echo "deb [signed-by=/usr/share/keyrings/syncthing-archive-keyring.gpg] https://apt.syncthing.net/ syncthing stable" | sudo tee /etc/apt/sources.list.d/syncthing.list&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;We can then update the packages once again with: &lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;$ sudo apt update&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;And then finally install Syncthing with: &lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;$ sudo apt install syncthing&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;Once it’s installed, you will find two Syncthing items in the applications menu: one is a simple ‘Start Syncthing’ which sets the service running; the other is ‘Syncthing Web UI’. First, double-click the ‘Start Syncthing’ application, then double-click the ‘Syncthing Web UI’. This should now launch the default web browser and will land on a default Syncthing admin page for the device. From here, we can set up files and directories for synchronisation. &lt;/p&gt;






&lt;div class="wp-block-image"&gt;
&lt;figure&gt;&lt;img src="https://www.raspberrypi.com/app/uploads/2026/09/add_folder-1024x576.png" alt="" class="wp-image-139903"&gt;&lt;figcaption class="wp-element-caption"&gt;&lt;strong&gt;Figure 8:&lt;/strong&gt; Adding folders you want to synchronise in the Syncthing Web UI&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;


&lt;p class="wp-block-paragraph"&gt;First, let’s click the ‘Add Folder’ button. In the ‘Add Folder’ dialog box (&lt;strong&gt;Figure 8&lt;/strong&gt;), we can add a folder label — for example, ‘second_brain_directory’, as we are going to share the directory we made for our Logseq second brain files. Leave the Folder ID with the default generated identifier string, and then in the last input, Folder Path, set the exact folder path to match where the Logseq folder you made is. So for example, if you made your Logseq folder in the default user directory of a Raspberry Pi and you called it ‘2nd_Brain’, you can input the tilde (~) character (which means the same as /home/username) and write &lt;strong&gt;~/2nd_Brain&lt;/strong&gt;. Clicking Save sets this file to be shared. &lt;/p&gt;






&lt;div class="wp-block-image"&gt;
&lt;figure&gt;&lt;img src="https://www.raspberrypi.com/app/uploads/2026/09/add_device-1024x576.png" alt="" class="wp-image-139902"&gt;&lt;figcaption class="wp-element-caption"&gt;&lt;strong&gt;Figure 9:&lt;/strong&gt; Adding devices to your Syncthing setup is straightforward&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;


&lt;p class="wp-block-paragraph"&gt;On another device with Logseq and Syncthing installed (we used our Uconsole), on the same Web UI page you can click on the short name identification in the ‘This Device’ area. Clicking this launches a dialog box with a QR code and a long-form ID string for the device (&lt;strong&gt;Figure 9&lt;/strong&gt;). This is a 56-digit string with dashes. Back on the Raspberry Pi 500, you can click the ‘Add Remote Device’ button and type in the long device ID; note that you don’t need to add the dashes or spaces, just the long string. If you leave the Device Name section empty, it will populate with the random generated device name on the other device; or you can add a more descriptive name such as ‘Uconsole’ or ‘Pi Zero’ here. &lt;/p&gt;



&lt;blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"&gt;
&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Quick tip&lt;/strong&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;Note that you don’t have to be on the same network for Syncthing to synchronise your files. &lt;/p&gt;
&lt;/blockquote&gt;



&lt;p class="wp-block-paragraph"&gt;Finally, by clicking on the folder we created to share earlier on the Raspberry Pi 500, and then clicking the Edit button, we can move over to the Sharing tab in the dialog box, where we should be able to see the remote device we just added (&lt;strong&gt;Figure 10&lt;/strong&gt;). If we tick the box to share with that device, we can then click Save. Give everything a couple of minutes to work through the networks and then check on the other device; you should see a notification that the other device wants to share a folder with you. Accept the folder and follow the instructions to set up a path to a directory to synchronise that folder to. All that then remains is to open Logseq on the second device and click ‘add graph’, navigating to the synchronised folder we just made. This means if we add or change anything in Logseq on our first device (Raspberry Pi 500) or our second device (Uconsole), then these changes appear on both devices, keeping our Logseq second brain system up to date. You can of course repeat this process, sharing and synchronising your &lt;strong&gt;2nd_Brain&lt;/strong&gt; Logseq folder onto as many devices as you want.&lt;/p&gt;






&lt;div class="wp-block-image"&gt;
&lt;figure&gt;&lt;img src="https://www.raspberrypi.com/app/uploads/2026/09/edit_folder_share-1024x576.png" alt="" class="wp-image-139906"&gt;&lt;figcaption class="wp-element-caption"&gt;&lt;strong&gt;Figure 10: &lt;/strong&gt;Editing a file setting to share with specified devices&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;






&lt;h2 class="wp-block-heading"&gt;&lt;strong&gt;Careful now!&lt;/strong&gt;&lt;/h2&gt;



&lt;p class="wp-block-paragraph"&gt;Note that synchronising folders is not a backup! If you accidentally delete part or all of your Logseq folder content, this will propagate through Syncthing and delete it everywhere! A solution to this is to, on one or more devices, set up a system that periodically backs up your folder to another folder. &lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;To do this, we will create a small Bash script that will copy a target folder to a different folder that we won’t sync. We’ll then use the cron service to make this script run periodically so that we don’t have to think about it. You can do this on any of the Raspberry Pi devices, or indeed more than one; however, it would be most useful on a device which can be left always on. In our system, we have it set up on a Raspberry Pi Zero W which runs headless and is an ‘always on’ device in our workspace. However, we’ll set this up here on the Raspberry Pi 500, as it’s easy to do by typing commands into the terminal. &lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;To begin, launch the Terminal application, and in the root directory of your Raspberry Pi device, launch the nano text editor to write a Bash script:&lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;$ sudo nano&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;In nano, type the following small Bash script, replacing &lt;code&gt;LOGSEQ_Directory_Path&lt;/code&gt; with the path to your Logseq folder. If your folder is in the home directory, you don’t need any leading edge slashes; so, backing up our ‘2nd_brain’ folder in the home directory to ‘backup_2nd_brain’ is really straightforward to input.  &lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;#! /bin/sh 

rsync -avh LOGSEQ_Directory_Path
Backup_Directory_Path&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;Save this file with a name like &lt;strong&gt;backup_script.sh&lt;/strong&gt;. &lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;We need to set this file as executable, so in the same working directory as the script, we need to run:&lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;$ sudo chmod +x backup_script.sh&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;We can now test the &lt;strong&gt;backup_script.sh&lt;/strong&gt; script by, in the same directory, executing it with:&lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;$./backup_script.sh&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;You can then use the File Manager (or the Terminal) to check if the files have been copied to the newly created backup folder. Finally, we can automate this script using a cronjob. We can do this by editing a file called &lt;strong&gt;crontab&lt;/strong&gt;. Type:&lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;$ crontab -e&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;…in the terminal. If this is the first time you have done this, it might ask which editor you would like to use; select nano from the list. In the now-open &lt;strong&gt;crontab&lt;/strong&gt; file, there is a long section of comments that give a great overview of how the cron system works (&lt;strong&gt;Figure 11&lt;/strong&gt;). At the end of the file, we can add a line that has the following structure: minutes, hour, day of month, month, day of week, followed by the command. You can leave slots empty using an asterisk (&lt;code&gt;*&lt;/code&gt;) so you can construct values for different automated periods. So, for example: &lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;46 10 * * 1 /home/concretedog/backup_script.sh&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;…would execute the &lt;strong&gt;backup_script.sh&lt;/strong&gt; script every Monday at 10.46am. Note that if you wanted to run the script once a day, you’d simply leave the day of week value empty, such as: &lt;/p&gt;



&lt;pre class="wp-block-code"&gt;&lt;code&gt;30 10 * * * /home/concretedog/backup_script.sh&lt;/code&gt;&lt;/pre&gt;



&lt;p class="wp-block-paragraph"&gt;Of course, you can extend and enhance the system; it would be great to back up the folders to an attached SSD, or you might like to explore how to add monitoring and logging to your cron system. Logseq and Syncthing are available for most desktop distributions, and they are also ported to smartphone operating systems, meaning you can carry your private, secure, and backed-up second brain with you always!&lt;/p&gt;






&lt;div class="wp-block-image"&gt;
&lt;figure&gt;&lt;img src="https://www.raspberrypi.com/app/uploads/2026/09/crontab_e-1024x576.png" alt="" class="wp-image-139905"&gt;&lt;figcaption class="wp-element-caption"&gt;&lt;strong&gt;Figure 11: &lt;/strong&gt;Setting up crontab to create a periodically repeating job&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;






&lt;h2 class="wp-block-heading"&gt;Find more Raspberry Pi projects in &lt;em&gt;Raspberry Pi Official Magazine&lt;/em&gt;&lt;/h2&gt;



&lt;p class="wp-block-paragraph"&gt;This article appears in &lt;a href="https://magazine.raspberrypi.com/issues/169"&gt;issue 169&lt;/a&gt; of &lt;em&gt;Raspberry Pi Official Magazine&lt;/em&gt;, which you can &lt;a href="https://magazine.raspberrypi.com/issues/169"&gt;access online&lt;/a&gt;. You can also &lt;a href="https://magazine.raspberrypi.com/subscribe"&gt;subscribe&lt;/a&gt; to the print version of our magazine. Not only do we deliver worldwide, but those who sign up to the six- or twelve-month print subscription will receive a FREE &lt;a href="https://www.raspberrypi.com/products/raspberry-pi-pico-2/"&gt;Raspberry Pi Pico 2 W&lt;/a&gt;!&lt;/p&gt;





&lt;p class="wp-block-paragraph"&gt;You can find &lt;em&gt;Raspberry Pi Official Magazine&lt;/em&gt; on &lt;a href="https://www.facebook.com/rpimag/"&gt;Facebook&lt;/a&gt;, &lt;a href="https://x.com/rpimagazine"&gt;X&lt;/a&gt;, &lt;a href="https://www.threads.com/@rpimag"&gt;Threads&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/rpimag/posts/?feedView=all"&gt;LinkedIn&lt;/a&gt;, and &lt;a href="https://mastodon.social/@rpimag"&gt;Mastodon&lt;/a&gt;. You can also contact the team via email: magazine@raspberrypi.com&lt;/p&gt;
    &lt;/div&gt;

    
&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name>Jo Hinchliffe</name>
        </author>
        <media:content medium="image" url="https://www.raspberrypi.com/app/uploads/2026/09/pi500_logseq-1024x768.jpg"/>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19ff777f553:fc6b11:10e6f064</id>
        <title type="html">AI Software Development - What Does The Data Say? - Codemanship's Blog</title>
        <published>2026-08-12T19:34:21Z</published>
        <updated>2026-08-12T19:34:25Z</updated>
        <link href="https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/" rel="alternate" type="text/html"/>
        <summary type="html">I’m currently pulling together a bunch of sources – that are mostly recent – on the topic of LLMs and their use in software development. Some are peer-reviewed studies. Some are i…</summary>
        <content type="html">&lt;html lang="en" class="js wf-alegreyasans-n7-active wf-alegreyasans-n4-active wf-alegreyasans-i7-active wf-alegreyasans-i4-active wf-active" data-lt-installed="true"&gt;&lt;head&gt;
	&lt;meta charset="UTF-8"&gt;
	&lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
	&lt;link rel="profile" href="https://gmpg.org/xfn/11"&gt;
		&lt;link rel="pingback" href="https://codemanship.wordpress.com/xmlrpc.php"&gt;
		&lt;script src="/wp-content/plugins/custom-fonts/js/webfont.js" type="text/javascript" async=""&gt;&lt;/script&gt;&lt;script&gt;(function(html){html.className = html.className.replace(/\bno-js\b/,'js')})(document.documentElement);&lt;/script&gt;
&lt;title&gt;AI Software Development – What Does The Data Say? – Codemanship's Blog&lt;/title&gt;
&lt;script type="text/javascript"&gt;
  WebFontConfig = {"google":{"families":["Alegreya+Sans:r:latin,latin-ext","Alegreya+Sans:r,i,b,bi:latin,latin-ext"]},"api_url":"https:\/\/fonts-api.wp.com\/css"};
  (function() {
    var wf = document.createElement('script');
    wf.src = '/wp-content/plugins/custom-fonts/js/webfont.js';
    wf.type = 'text/javascript';
    wf.async = 'true';
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(wf, s);
	})();
&lt;/script&gt;&lt;style id="jetpack-custom-fonts-css"&gt;.wf-active body, .wf-active button, .wf-active input, .wf-active select, .wf-active textarea{font-family:"Alegreya Sans",sans-serif}.wf-active ::-webkit-input-placeholder{font-family:"Alegreya Sans",sans-serif}.wf-active :-moz-placeholder{font-family:"Alegreya Sans",sans-serif}.wf-active ::-moz-placeholder{font-family:"Alegreya Sans",sans-serif}.wf-active :-ms-input-placeholder{font-family:"Alegreya Sans",sans-serif}.wf-active button, .wf-active button[disabled]:hover, .wf-active button[disabled]:focus, .wf-active input[type="button"], .wf-active input[type="button"][disabled]:hover, .wf-active input[type="button"][disabled]:focus, .wf-active input[type="reset"], .wf-active input[type="reset"][disabled]:hover, .wf-active input[type="reset"][disabled]:focus, .wf-active input[type="submit"], .wf-active input[type="submit"][disabled]:hover, .wf-active input[type="submit"][disabled]:focus, .wf-active .main-navigation, .wf-active .no-comments, .wf-active .tagcloud a, .wf-active .wp_widget_tag_cloud a, .wf-active .top_rated div &gt; p, .wf-active .comments-area #respond .comment-form-fields p.comment-form-posting-as, .wf-active .comments-area #respond .comment-form-fields p.comment-form-log-out, .wf-active .comments-area #respond #comment-form-identity #comment-form-nascar p, .wf-active .comments-area #respond #comment-form-subscribe p, .wf-active .comments-area #respond p.form-submit input#comment-submit, .wf-active .comment-content p.comment-likes span.comment-like-feedback, .wf-active .jp-relatedposts-post-date, .wf-active .jp-relatedposts-post-context{font-family:"Alegreya Sans",sans-serif}.wf-active .post-password-form label, .wf-active .comment-form label, .wf-active .entry-content .contact-form label, .wf-active .post-navigation .meta-nav, .wf-active .image-navigation, .wf-active .comment-navigation, .wf-active .menu-toggle, .wf-active .widget_recent_entries .post-date, .wf-active .widget_rss .rss-date, .wf-active .widget_rss cite, .wf-active .sticky-post, .wf-active .entry-footer, .wf-active .comment-metadata, .wf-active .pingback .edit-link, .wf-active .comment-reply-link, .wf-active .page-links, .wf-active .page-links a, .wf-active .page-links &gt; span, .wf-active .pd-rating, .wf-active .entry-content .portfolio-entry-meta, .wf-active .entry-content #jp-relatedposts h3.jp-relatedposts-headline, .wf-active .hentry div.sharedaddy h3.sd-title, .wf-active .hentry h3.sd-title{font-family:"Alegreya Sans",sans-serif}.wf-active .pagination{font-family:"Alegreya Sans",sans-serif}.wf-active body:not(.logged-in) .site .skip-link{font-family:"Alegreya Sans",sans-serif}.wf-active .site-description, .wf-active .author-bio, .wf-active .taxonomy-description, .wf-active .entry-caption, .wf-active .comment-notes, .wf-active .comment-awaiting-moderation, .wf-active .logged-in-as, .wf-active .form-allowed-tags, .wf-active .site-info, .wf-active .wp-caption .wp-caption-text, .wf-active .gallery-caption, .wf-active .entry-content .portfolio-entry-content p, .wf-active .entry-content .testimonial-entry-title, .wf-active .entry-content .testimonial-entry-content p, .wf-active .entry-content #jp-relatedposts .jp-relatedposts-items p, .wf-active .entry-content #jp-relatedposts .jp-relatedposts-items-visual h4.jp-relatedposts-post-title{font-family:"Alegreya Sans",sans-serif}.wf-active .site-title, .wf-active .post-navigation .post-title, .wf-active .comments-title, .wf-active .comment-reply-title, .wf-active .page-title{font-family:"Alegreya Sans",sans-serif;font-style:normal;font-weight:400}.wf-active .widget .widget-title, .wf-active .entry-content .portfolio-entry-title{font-family:"Alegreya Sans",sans-serif;font-style:normal;font-weight:400}.wf-active .entry-title{font-family:"Alegreya Sans",sans-serif;font-style:normal;font-weight:400}.wf-active .format-aside .entry-title, .wf-active .format-image .entry-title, .wf-active .format-video .entry-title, .wf-active .format-quote .entry-title, .wf-active .format-gallery .entry-title, .wf-active .format-status .entry-title, .wf-active .format-link .entry-title, .wf-active .format-audio .entry-title, .wf-active .format-chat .entry-title{font-style:normal;font-weight:400}@media screen and (min-width: 44.375em){.wf-active .site-title, .wf-active .post-navigation .post-title{font-style:normal;font-weight:400}}@media screen and (min-width: 44.375em){.wf-active .entry-title{font-style:normal;font-weight:400}}@media screen and (min-width: 61.5625em){.wf-active .entry-title{font-style:normal;font-weight:400}}@media screen and (min-width: 61.5625em){.wf-active .format-aside .entry-title, .wf-active .format-image .entry-title, .wf-active .format-video .entry-title, .wf-active .format-quote .entry-title, .wf-active .format-gallery .entry-title, .wf-active .format-status .entry-title, .wf-active .format-link .entry-title, .wf-active .format-audio .entry-title, .wf-active .format-chat .entry-title{font-style:normal;font-weight:400}}&lt;/style&gt;
&lt;meta name="robots" content="max-image-preview:large"&gt;
&lt;link rel="dns-prefetch" href="//s0.wp.com"&gt;
&lt;link rel="dns-prefetch" href="//widgets.wp.com"&gt;
&lt;link rel="alternate" type="application/rss+xml" title="Codemanship's Blog » Feed" href="https://codemanship.wordpress.com/feed/"&gt;
&lt;link rel="alternate" type="application/rss+xml" title="Codemanship's Blog » Comments Feed" href="https://codemanship.wordpress.com/comments/feed/"&gt;
&lt;link rel="alternate" type="application/rss+xml" title="Codemanship's Blog » AI Software Development – What Does The Data&amp;nbsp;Say? Comments Feed" href="https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/feed/"&gt;
	&lt;script type="text/javascript"&gt;
		/* &lt;![CDATA[ */
		function addLoadEvent(func) {
			var oldonload = window.onload;
			if (typeof window.onload != 'function') {
				window.onload = func;
			} else {
				window.onload = function () {
					oldonload();
					func();
				}
			}
		}
		/* ]]&gt; */
	&lt;/script&gt;
	&lt;style id="wp-img-auto-sizes-contain-inline-css"&gt;
img:is([sizes=auto i],[sizes^="auto," i]){contain-intrinsic-size:3000px 1500px}
/*# sourceURL=wp-img-auto-sizes-contain-inline-css */
&lt;/style&gt;
&lt;link crossorigin="anonymous" rel="stylesheet" id="all-css-2-1" href="/_static/??-eJyVjMEKwjAQRH/IuOrB4kH8lpAOmjabXdwNrX9vK4LgQZCBgQczjyYNSaqjOnELWto1V6MBrjGNbyYWWatvBUZ+AyO4SDFKwhqdfFr+D8uzA3WbzDb0v/eOEh19UDH/ol/Kkkd8xC9a5xc+77tud1pyOA5PXPBXBA==&amp;amp;cssminify=yes" type="text/css" media="all"&gt;
&lt;style id="wp-emoji-styles-inline-css"&gt;

	img.wp-smiley, img.emoji {
		display: inline !important;
		border: none !important;
		box-shadow: none !important;
		height: 1em !important;
		width: 1em !important;
		margin: 0 0.07em !important;
		vertical-align: -0.1em !important;
		background: none !important;
		padding: 0 !important;
	}
/*# sourceURL=wp-emoji-styles-inline-css */
&lt;/style&gt;
&lt;style id="wp-block-library-inline-css"&gt;
:root{--wp-block-synced-color:#7a00df;--wp-block-synced-color--rgb:122,0,223;--wp-bound-block-color:var(--wp-block-synced-color);--wp-editor-canvas-background:#ddd;--wp-admin-theme-color:#007cba;--wp-admin-theme-color--rgb:0,124,186;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-10--rgb:0,107,160.5;--wp-admin-theme-color-darker-20:#005a87;--wp-admin-theme-color-darker-20--rgb:0,90,135;--wp-admin-border-width-focus:2px}@media (min-resolution:192dpi){:root{--wp-admin-border-width-focus:1.5px}}.wp-element-button{cursor:pointer}:root .has-very-light-gray-background-color{background-color:#eee}:root .has-very-dark-gray-background-color{background-color:#313131}:root .has-very-light-gray-color{color:#eee}:root .has-very-dark-gray-color{color:#313131}:root .has-vivid-green-cyan-to-vivid-cyan-blue-gradient-background{background:linear-gradient(135deg,#00d084,#0693e3)}:root .has-purple-crush-gradient-background{background:linear-gradient(135deg,#34e2e4,#4721fb 50%,#ab1dfe)}:root .has-hazy-dawn-gradient-background{background:linear-gradient(135deg,#faaca8,#dad0ec)}:root .has-subdued-olive-gradient-background{background:linear-gradient(135deg,#fafae1,#67a671)}:root .has-atomic-cream-gradient-background{background:linear-gradient(135deg,#fdd79a,#004a59)}:root .has-nightshade-gradient-background{background:linear-gradient(135deg,#330968,#31cdcf)}:root .has-midnight-gradient-background{background:linear-gradient(135deg,#020381,#2874fc)}:root{--wp--preset--font-size--normal:16px;--wp--preset--font-size--huge:42px}.has-regular-font-size{font-size:1em}.has-larger-font-size{font-size:2.625em}.has-normal-font-size{font-size:var(--wp--preset--font-size--normal)}.has-huge-font-size{font-size:var(--wp--preset--font-size--huge)}:root .has-text-align-center{text-align:center}:root .has-text-align-left{text-align:left}:root .has-text-align-right{text-align:right}.has-fit-text{white-space:nowrap!important}#end-resizable-editor-section{display:none}.aligncenter{clear:both}.items-justified-left{justify-content:flex-start}.items-justified-center{justify-content:center}.items-justified-right{justify-content:flex-end}.items-justified-space-between{justify-content:space-between}.screen-reader-text{word-wrap:normal!important;border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;word-break:normal!important}.screen-reader-text:focus{background-color:#ddd;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}html :where(.has-border-color){border-style:solid}html :where([style^=border-color],[style*=";border-color"],[style*="; border-color"]){border-style:solid}html :where([style^=border-top-color],[style*=";border-top-color"],[style*="; border-top-color"]){border-top-style:solid}html :where([style^=border-right-color],[style*=";border-right-color"],[style*="; border-right-color"]){border-right-style:solid}html :where([style^=border-bottom-color],[style*=";border-bottom-color"],[style*="; border-bottom-color"]){border-bottom-style:solid}html :where([style^=border-left-color],[style*=";border-left-color"],[style*="; border-left-color"]){border-left-style:solid}html :where([style^=border-width],[style*=";border-width"],[style*="; border-width"]){border-style:solid}html :where([style^=border-top-width],[style*=";border-top-width"],[style*="; border-top-width"]){border-top-style:solid}html :where([style^=border-right-width],[style*=";border-right-width"],[style*="; border-right-width"]){border-right-style:solid}html :where([style^=border-bottom-width],[style*=";border-bottom-width"],[style*="; border-bottom-width"]){border-bottom-style:solid}html :where([style^=border-left-width],[style*=";border-left-width"],[style*="; border-left-width"]){border-left-style:solid}html :where(img[class*=wp-image-]){height:auto;max-width:100%}:where(figure){margin:0 0 1em}html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:var(--wp-admin--admin-bar--height,0px)}@media screen and (max-width:600px){html :where(.is-position-sticky){--wp-admin--admin-bar--position-offset:0px}}

/*# sourceURL=/wp-content/plugins/gutenberg-core/v23.7.1/build/styles/block-library/common.min.css */
&lt;/style&gt;
&lt;style id="wp-block-paragraph-inline-css"&gt;
.is-small-text{font-size:.875em}.is-regular-text{font-size:1em}.is-large-text{font-size:2.25em}.is-larger-text{font-size:3em}.has-drop-cap:not(:focus):first-letter{float:left;font-size:8.4em;font-style:normal;font-weight:100;line-height:.68;margin:.05em .1em 0 0;text-transform:uppercase}body.rtl .has-drop-cap:not(:focus):first-letter{float:none;margin-left:.1em}p.has-drop-cap.has-background{overflow:hidden}:root :where(p.has-background){padding:1.25em 2.375em}:where(p.has-text-color:not(.has-link-color)) a{color:inherit}p.has-text-align-left[style*="writing-mode:vertical-lr"],p.has-text-align-right[style*="writing-mode:vertical-rl"]{rotate:180deg}
/*# sourceURL=/wp-content/plugins/gutenberg-core/v23.7.1/build/styles/block-library/paragraph/style.min.css */
&lt;/style&gt;
&lt;style id="wp-block-library-inline-css-extra"&gt;
.has-text-align-justify {
	text-align:justify;
}
.has-text-align-justify{text-align:justify;}
/*# sourceURL=wp-block-library-inline-css */
&lt;/style&gt;

&lt;style id="classic-theme-styles-inline-css"&gt;
.wp-block-button__link{background-color:#32373c;border-radius:9999px;box-shadow:none;color:#fff;font-size:1.125em;padding:calc(.667em + 2px) calc(1.333em + 2px);text-decoration:none}.wp-block-file__button{background:#32373c;color:#fff}.wp-block-accordion-heading{margin:0}.wp-block-accordion-heading__toggle{background-color:inherit!important;color:inherit!important}.wp-block-accordion-heading__toggle:not(:focus-visible){outline:none}.wp-block-accordion-heading__toggle:focus,.wp-block-accordion-heading__toggle:hover{background-color:inherit!important;border:none;box-shadow:none;color:inherit;padding:var(--wp--preset--spacing--20,1em) 0;text-decoration:none}.wp-block-accordion-heading__toggle:focus-visible{outline:auto;outline-offset:0}.wp-block-tab:not(.has-text-color){color:inherit!important}.wp-block-tab:not(.has-background){background-color:inherit!important}.wp-block-tab:focus,.wp-block-tab:hover{text-decoration:none}.wp-block-tab:focus-visible{outline:auto;outline-offset:0}
/*# sourceURL=/wp-content/plugins/gutenberg-core/v23.7.1/build/styles/block-library/classic.min.css */
&lt;/style&gt;

&lt;style id="coblocks-inline-format-inline-css"&gt;
.uppercase{text-transform:uppercase}
/*# sourceURL=coblocks-inline-format-inline-css */
&lt;/style&gt;
&lt;link crossorigin="anonymous" rel="stylesheet" id="all-css-12-1" href="/_static/??-eJx9jEEOAiEQBD/kQECjeDC+ZWEnijIwYWA3/l72op689KFTVXplCCU3zE1z6reYRYfiUwlP0VYZpwxIJE4IFRd10HOU9iFA2iuhCiI7/ROiDt9WxfETT20jCOc4YUIa2D9t5eGA91xRBMZS7ATtPkTZvCtdzNEZt7f2fHq8Afl7R7w=&amp;amp;cssminify=yes" type="text/css" media="all"&gt;
&lt;style id="global-styles-inline-css"&gt;
:root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #fff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--color--dark-gray: #1a1a1a;--wp--preset--color--medium-gray: #686868;--wp--preset--color--light-gray: #e5e5e5;--wp--preset--color--blue-gray: #4d545c;--wp--preset--color--bright-blue: #007acc;--wp--preset--color--light-blue: #9adffd;--wp--preset--color--dark-brown: #402b30;--wp--preset--color--medium-brown: #774e24;--wp--preset--color--dark-red: #640c1f;--wp--preset--color--bright-red: #ff675f;--wp--preset--color--yellow: #ffef8e;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgb(252,185,0) 0%,rgb(255,105,0) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgb(255,105,0) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--font-size--small: 13px;--wp--preset--font-size--medium: 20px;--wp--preset--font-size--large: 36px;--wp--preset--font-size--x-large: 42px;--wp--preset--font-family--albert-sans: 'Albert Sans', sans-serif;--wp--preset--font-family--alegreya: Alegreya, serif;--wp--preset--font-family--arvo: Arvo, serif;--wp--preset--font-family--bodoni-moda: 'Bodoni Moda', serif;--wp--preset--font-family--bricolage-grotesque: 'Bricolage Grotesque', sans-serif;--wp--preset--font-family--cabin: Cabin, sans-serif;--wp--preset--font-family--chivo: Chivo, sans-serif;--wp--preset--font-family--commissioner: Commissioner, sans-serif;--wp--preset--font-family--cormorant: Cormorant, serif;--wp--preset--font-family--courier-prime: 'Courier Prime', monospace;--wp--preset--font-family--crimson-pro: 'Crimson Pro', serif;--wp--preset--font-family--dm-mono: 'DM Mono', monospace;--wp--preset--font-family--dm-sans: 'DM Sans', sans-serif;--wp--preset--font-family--dm-serif-display: 'DM Serif Display', serif;--wp--preset--font-family--domine: Domine, serif;--wp--preset--font-family--eb-garamond: 'EB Garamond', serif;--wp--preset--font-family--epilogue: Epilogue, sans-serif;--wp--preset--font-family--fahkwang: Fahkwang, sans-serif;--wp--preset--font-family--figtree: Figtree, sans-serif;--wp--preset--font-family--fira-sans: 'Fira Sans', sans-serif;--wp--preset--font-family--fjalla-one: 'Fjalla One', sans-serif;--wp--preset--font-family--fraunces: Fraunces, serif;--wp--preset--font-family--gabarito: Gabarito, system-ui;--wp--preset--font-family--ibm-plex-mono: 'IBM Plex Mono', monospace;--wp--preset--font-family--ibm-plex-sans: 'IBM Plex Sans', sans-serif;--wp--preset--font-family--ibarra-real-nova: 'Ibarra Real Nova', serif;--wp--preset--font-family--instrument-serif: 'Instrument Serif', serif;--wp--preset--font-family--inter: Inter, sans-serif;--wp--preset--font-family--josefin-sans: 'Josefin Sans', sans-serif;--wp--preset--font-family--jost: Jost, sans-serif;--wp--preset--font-family--libre-baskerville: 'Libre Baskerville', serif;--wp--preset--font-family--libre-franklin: 'Libre Franklin', sans-serif;--wp--preset--font-family--literata: Literata, serif;--wp--preset--font-family--lora: Lora, serif;--wp--preset--font-family--merriweather: Merriweather, serif;--wp--preset--font-family--montserrat: Montserrat, sans-serif;--wp--preset--font-family--newsreader: Newsreader, serif;--wp--preset--font-family--noto-sans-mono: 'Noto Sans Mono', sans-serif;--wp--preset--font-family--nunito: Nunito, sans-serif;--wp--preset--font-family--open-sans: 'Open Sans', sans-serif;--wp--preset--font-family--overpass: Overpass, sans-serif;--wp--preset--font-family--pt-serif: 'PT Serif', serif;--wp--preset--font-family--petrona: Petrona, serif;--wp--preset--font-family--piazzolla: Piazzolla, serif;--wp--preset--font-family--playfair-display: 'Playfair Display', serif;--wp--preset--font-family--plus-jakarta-sans: 'Plus Jakarta Sans', sans-serif;--wp--preset--font-family--poppins: Poppins, sans-serif;--wp--preset--font-family--raleway: Raleway, sans-serif;--wp--preset--font-family--roboto: Roboto, sans-serif;--wp--preset--font-family--roboto-slab: 'Roboto Slab', serif;--wp--preset--font-family--rubik: Rubik, sans-serif;--wp--preset--font-family--rufina: Rufina, serif;--wp--preset--font-family--sora: Sora, sans-serif;--wp--preset--font-family--source-sans-3: 'Source Sans 3', sans-serif;--wp--preset--font-family--source-serif-4: 'Source Serif 4', serif;--wp--preset--font-family--space-mono: 'Space Mono', monospace;--wp--preset--font-family--syne: Syne, sans-serif;--wp--preset--font-family--texturina: Texturina, serif;--wp--preset--font-family--urbanist: Urbanist, sans-serif;--wp--preset--font-family--work-sans: 'Work Sans', sans-serif;--wp--preset--spacing--20: 0.44rem;--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: 1rem;--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: 5.06rem;--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgb(255, 255, 255), 6px 6px rgb(0, 0, 0);--wp--preset--shadow--crisp: 6px 6px 0px rgb(0, 0, 0);}.wp-block-button{--wp--preset--dimension--25: 25%;--wp--preset--dimension--50: 50%;--wp--preset--dimension--75: 75%;--wp--preset--dimension--100: 100%;}:where(body) { margin: 0; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex &gt; :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid &gt; :is(*, div){margin: 0;}body{padding-top: 0px;padding-right: 0px;padding-bottom: 0px;padding-left: 0px;}:root :where(.wp-element-button, .wp-block-button__link){background-color: #32373c;border-width: 0;color: #fff;font-family: inherit;font-size: inherit;font-style: inherit;font-weight: inherit;letter-spacing: inherit;line-height: inherit;padding-top: calc(0.667em + 2px);padding-right: calc(1.333em + 2px);padding-bottom: calc(0.667em + 2px);padding-left: calc(1.333em + 2px);text-decoration: none;text-transform: inherit;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-dark-gray-color{color: var(--wp--preset--color--dark-gray) !important;}.has-medium-gray-color{color: var(--wp--preset--color--medium-gray) !important;}.has-light-gray-color{color: var(--wp--preset--color--light-gray) !important;}.has-blue-gray-color{color: var(--wp--preset--color--blue-gray) !important;}.has-bright-blue-color{color: var(--wp--preset--color--bright-blue) !important;}.has-light-blue-color{color: var(--wp--preset--color--light-blue) !important;}.has-dark-brown-color{color: var(--wp--preset--color--dark-brown) !important;}.has-medium-brown-color{color: var(--wp--preset--color--medium-brown) !important;}.has-dark-red-color{color: var(--wp--preset--color--dark-red) !important;}.has-bright-red-color{color: var(--wp--preset--color--bright-red) !important;}.has-yellow-color{color: var(--wp--preset--color--yellow) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-dark-gray-background-color{background-color: var(--wp--preset--color--dark-gray) !important;}.has-medium-gray-background-color{background-color: var(--wp--preset--color--medium-gray) !important;}.has-light-gray-background-color{background-color: var(--wp--preset--color--light-gray) !important;}.has-blue-gray-background-color{background-color: var(--wp--preset--color--blue-gray) !important;}.has-bright-blue-background-color{background-color: var(--wp--preset--color--bright-blue) !important;}.has-light-blue-background-color{background-color: var(--wp--preset--color--light-blue) !important;}.has-dark-brown-background-color{background-color: var(--wp--preset--color--dark-brown) !important;}.has-medium-brown-background-color{background-color: var(--wp--preset--color--medium-brown) !important;}.has-dark-red-background-color{background-color: var(--wp--preset--color--dark-red) !important;}.has-bright-red-background-color{background-color: var(--wp--preset--color--bright-red) !important;}.has-yellow-background-color{background-color: var(--wp--preset--color--yellow) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-dark-gray-border-color{border-color: var(--wp--preset--color--dark-gray) !important;}.has-medium-gray-border-color{border-color: var(--wp--preset--color--medium-gray) !important;}.has-light-gray-border-color{border-color: var(--wp--preset--color--light-gray) !important;}.has-blue-gray-border-color{border-color: var(--wp--preset--color--blue-gray) !important;}.has-bright-blue-border-color{border-color: var(--wp--preset--color--bright-blue) !important;}.has-light-blue-border-color{border-color: var(--wp--preset--color--light-blue) !important;}.has-dark-brown-border-color{border-color: var(--wp--preset--color--dark-brown) !important;}.has-medium-brown-border-color{border-color: var(--wp--preset--color--medium-brown) !important;}.has-dark-red-border-color{border-color: var(--wp--preset--color--dark-red) !important;}.has-bright-red-border-color{border-color: var(--wp--preset--color--bright-red) !important;}.has-yellow-border-color{border-color: var(--wp--preset--color--yellow) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}.has-albert-sans-font-family{font-family: var(--wp--preset--font-family--albert-sans) !important;}.has-alegreya-font-family{font-family: var(--wp--preset--font-family--alegreya) !important;}.has-arvo-font-family{font-family: var(--wp--preset--font-family--arvo) !important;}.has-bodoni-moda-font-family{font-family: var(--wp--preset--font-family--bodoni-moda) !important;}.has-bricolage-grotesque-font-family{font-family: var(--wp--preset--font-family--bricolage-grotesque) !important;}.has-cabin-font-family{font-family: var(--wp--preset--font-family--cabin) !important;}.has-chivo-font-family{font-family: var(--wp--preset--font-family--chivo) !important;}.has-commissioner-font-family{font-family: var(--wp--preset--font-family--commissioner) !important;}.has-cormorant-font-family{font-family: var(--wp--preset--font-family--cormorant) !important;}.has-courier-prime-font-family{font-family: var(--wp--preset--font-family--courier-prime) !important;}.has-crimson-pro-font-family{font-family: var(--wp--preset--font-family--crimson-pro) !important;}.has-dm-mono-font-family{font-family: var(--wp--preset--font-family--dm-mono) !important;}.has-dm-sans-font-family{font-family: var(--wp--preset--font-family--dm-sans) !important;}.has-dm-serif-display-font-family{font-family: var(--wp--preset--font-family--dm-serif-display) !important;}.has-domine-font-family{font-family: var(--wp--preset--font-family--domine) !important;}.has-eb-garamond-font-family{font-family: var(--wp--preset--font-family--eb-garamond) !important;}.has-epilogue-font-family{font-family: var(--wp--preset--font-family--epilogue) !important;}.has-fahkwang-font-family{font-family: var(--wp--preset--font-family--fahkwang) !important;}.has-figtree-font-family{font-family: var(--wp--preset--font-family--figtree) !important;}.has-fira-sans-font-family{font-family: var(--wp--preset--font-family--fira-sans) !important;}.has-fjalla-one-font-family{font-family: var(--wp--preset--font-family--fjalla-one) !important;}.has-fraunces-font-family{font-family: var(--wp--preset--font-family--fraunces) !important;}.has-gabarito-font-family{font-family: var(--wp--preset--font-family--gabarito) !important;}.has-ibm-plex-mono-font-family{font-family: var(--wp--preset--font-family--ibm-plex-mono) !important;}.has-ibm-plex-sans-font-family{font-family: var(--wp--preset--font-family--ibm-plex-sans) !important;}.has-ibarra-real-nova-font-family{font-family: var(--wp--preset--font-family--ibarra-real-nova) !important;}.has-instrument-serif-font-family{font-family: var(--wp--preset--font-family--instrument-serif) !important;}.has-inter-font-family{font-family: var(--wp--preset--font-family--inter) !important;}.has-josefin-sans-font-family{font-family: var(--wp--preset--font-family--josefin-sans) !important;}.has-jost-font-family{font-family: var(--wp--preset--font-family--jost) !important;}.has-libre-baskerville-font-family{font-family: var(--wp--preset--font-family--libre-baskerville) !important;}.has-libre-franklin-font-family{font-family: var(--wp--preset--font-family--libre-franklin) !important;}.has-literata-font-family{font-family: var(--wp--preset--font-family--literata) !important;}.has-lora-font-family{font-family: var(--wp--preset--font-family--lora) !important;}.has-merriweather-font-family{font-family: var(--wp--preset--font-family--merriweather) !important;}.has-montserrat-font-family{font-family: var(--wp--preset--font-family--montserrat) !important;}.has-newsreader-font-family{font-family: var(--wp--preset--font-family--newsreader) !important;}.has-noto-sans-mono-font-family{font-family: var(--wp--preset--font-family--noto-sans-mono) !important;}.has-nunito-font-family{font-family: var(--wp--preset--font-family--nunito) !important;}.has-open-sans-font-family{font-family: var(--wp--preset--font-family--open-sans) !important;}.has-overpass-font-family{font-family: var(--wp--preset--font-family--overpass) !important;}.has-pt-serif-font-family{font-family: var(--wp--preset--font-family--pt-serif) !important;}.has-petrona-font-family{font-family: var(--wp--preset--font-family--petrona) !important;}.has-piazzolla-font-family{font-family: var(--wp--preset--font-family--piazzolla) !important;}.has-playfair-display-font-family{font-family: var(--wp--preset--font-family--playfair-display) !important;}.has-plus-jakarta-sans-font-family{font-family: var(--wp--preset--font-family--plus-jakarta-sans) !important;}.has-poppins-font-family{font-family: var(--wp--preset--font-family--poppins) !important;}.has-raleway-font-family{font-family: var(--wp--preset--font-family--raleway) !important;}.has-roboto-font-family{font-family: var(--wp--preset--font-family--roboto) !important;}.has-roboto-slab-font-family{font-family: var(--wp--preset--font-family--roboto-slab) !important;}.has-rubik-font-family{font-family: var(--wp--preset--font-family--rubik) !important;}.has-rufina-font-family{font-family: var(--wp--preset--font-family--rufina) !important;}.has-sora-font-family{font-family: var(--wp--preset--font-family--sora) !important;}.has-source-sans-3-font-family{font-family: var(--wp--preset--font-family--source-sans-3) !important;}.has-source-serif-4-font-family{font-family: var(--wp--preset--font-family--source-serif-4) !important;}.has-space-mono-font-family{font-family: var(--wp--preset--font-family--space-mono) !important;}.has-syne-font-family{font-family: var(--wp--preset--font-family--syne) !important;}.has-texturina-font-family{font-family: var(--wp--preset--font-family--texturina) !important;}.has-urbanist-font-family{font-family: var(--wp--preset--font-family--urbanist) !important;}.has-work-sans-font-family{font-family: var(--wp--preset--font-family--work-sans) !important;}
/*# sourceURL=global-styles-inline-css */
&lt;/style&gt;

&lt;link crossorigin="anonymous" rel="stylesheet" id="all-css-14-1" href="/wp-content/mu-plugins/jetpack-mu-wpcom-plugin/moon/jetpack_vendor/automattic/jetpack-mu-wpcom/src/build/verbum-comments/verbum-comments.css?m=1785895867i&amp;amp;cssminify=yes" type="text/css" media="all"&gt;
&lt;link rel="stylesheet" id="verbum-gutenberg-css-css" href="https://widgets.wp.com/verbum-block-editor/block-editor.css?ver=1771405207" media="all"&gt;
&lt;link crossorigin="anonymous" rel="stylesheet" id="all-css-16-1" href="/_static/??-eJyNjbEOgzAQQ3+o1KBK0AXxKVU5nSCQXKLeRcDfN6o6dGDoZlvPNrZUURRjMYRcJZ8nJ4qFLT1p/XqEGAUPJ4SJhV+uNPRcXkn1gp9RmzmwIuURtpXgULcbs0Dt8Pw/XkCMPtL6uRhC33Rd09bt7V4vb1EVSjI=&amp;amp;cssminify=yes" type="text/css" media="all"&gt;
&lt;link crossorigin="anonymous" rel="stylesheet" id="all-css-22-1" href="/_static/??-eJx9y0EOwjAMRNELYUxEK7FBnKWNTAhK7Kh2lPb2pGyADbv5Iz1sBbywERvagzIpljqjtX5sGlcjYvSqqLYlgla85GPvA37BXKGkGiIrLjQnCX2Gt/rkPxRIIImfLAr/BNzTFJed3vLVjW4YT2fnLs8X8DhDqQ==&amp;amp;cssminify=yes" type="text/css" media="all"&gt;
&lt;style id="jetpack-global-styles-frontend-style-inline-css"&gt;
:root { --font-headings: unset; --font-base: unset; --font-headings-default: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif; --font-base-default: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;}
/*# sourceURL=jetpack-global-styles-frontend-style-inline-css */
&lt;/style&gt;
&lt;link crossorigin="anonymous" rel="stylesheet" id="all-css-24-1" href="/_static/??-eJyNjcEKAjEMRH/ImhVlqwfxU6TbhrZrmhTTIv69u+JFvHgZ5sHwBh7VeOGG3KB0U6nHzAoztur87cNQRNYInVBBk7tjcCE83zVz3HrVDfxvumb2MPVMAVR8dmRIougX/DhbwrK8pwNEksnROriU884e9+PJDnaYXxwFS20=&amp;amp;cssminify=yes" type="text/css" media="all"&gt;
&lt;script id="jetpack_related-posts-js-extra"&gt;
var related_posts_js_options = {"post_heading":"h4"};
//# sourceURL=jetpack_related-posts-js-extra
&lt;/script&gt;
&lt;script id="wpcom-actionbar-placeholder-js-extra"&gt;
var actionbardata = {"siteID":"15031761","postID":"4719","siteURL":"https://codemanship.wordpress.com","xhrURL":"https://codemanship.wordpress.com/wp-admin/admin-ajax.php","nonce":"319d689d5a","isLoggedIn":"","statusMessage":"","subsEmailDefault":"instantly","proxyScriptUrl":"https://s0.wp.com/wp-content/js/wpcom-proxy-request.js?m=1513050504i&amp;amp;ver=20211021","shortlink":"https://wp.me/p114rL-1e7","i18n":{"followedText":"New posts from this site will now appear in your \u003Ca href=\"https://wordpress.com/reader\"\u003EReader\u003C/a\u003E","foldBar":"Collapse this bar","unfoldBar":"Expand this bar","shortLinkCopied":"Shortlink copied to clipboard."}};
//# sourceURL=wpcom-actionbar-placeholder-js-extra
&lt;/script&gt;
&lt;script id="jetpack-mu-wpcom-settings-js-before"&gt;
var JETPACK_MU_WPCOM_SETTINGS = {"assetsUrl":"https://s0.wp.com/wp-content/mu-plugins/jetpack-mu-wpcom-plugin/moon/jetpack_vendor/automattic/jetpack-mu-wpcom/src/build/"};
//# sourceURL=jetpack-mu-wpcom-settings-js-before
&lt;/script&gt;
&lt;script crossorigin="anonymous" type="text/javascript" src="/_static/??-eJxdjEsOwjAMRC9E6lIpsEIcBfVj0YTYsWpHbW/fILEAlm9m3sAqbsxsyAZUnKTyDKwQ0aQfXx8GypnhEXiEoYQ0wYKpN5ycZDX9pYYCN1FP8HUc6yaZkyVv+39nMxIqSBnA1hrsGjZD5Lc0GyVfhTvdzlfv2/bSdT4e2plCCA=="&gt;&lt;/script&gt;
&lt;script id="rlt-proxy-js-after"&gt;
	rltInitialize( {"token":null,"iframeOrigins":["https:\/\/widgets.wp.com"]} );
//# sourceURL=rlt-proxy-js-after
&lt;/script&gt;
&lt;link rel="EditURI" type="application/rsd+xml" title="RSD" href="https://codemanship.wordpress.com/xmlrpc.php?rsd"&gt;
&lt;meta name="generator" content="WordPress.com"&gt;
&lt;link rel="canonical" href="https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/"&gt;
&lt;link rel="shortlink" href="https://wp.me/p114rL-1e7"&gt;
&lt;link rel="alternate" type="application/json+oembed" href="https://public-api.wordpress.com/oembed/?format=json&amp;amp;url=https%3A%2F%2Fcodemanship.wordpress.com%2F2026%2F08%2F12%2Fai-software-development-what-does-the-data-say%2F&amp;amp;for=wpcom-auto-discovery"&gt;&lt;link rel="alternate" type="application/xml+oembed" href="https://public-api.wordpress.com/oembed/?format=xml&amp;amp;url=https%3A%2F%2Fcodemanship.wordpress.com%2F2026%2F08%2F12%2Fai-software-development-what-does-the-data-say%2F&amp;amp;for=wpcom-auto-discovery"&gt;
&lt;!-- Jetpack Open Graph Tags --&gt;
&lt;meta property="og:type" content="article"&gt;
&lt;meta property="og:title" content="AI Software Development – What Does The Data Say?"&gt;
&lt;meta property="og:url" content="https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/"&gt;
&lt;meta property="og:description" content="I’m currently pulling together a bunch of sources – that are mostly recent – on the topic of LLMs and their use in software development. Some are peer-reviewed studies. Some are i…"&gt;
&lt;meta property="article:published_time" content="2026-08-12T17:41:50+00:00"&gt;
&lt;meta property="article:modified_time" content="2026-08-12T17:46:26+00:00"&gt;
&lt;meta property="og:site_name" content="Codemanship's Blog"&gt;
&lt;meta property="og:image" content="https://i0.wp.com/codemanship.wordpress.com/wp-content/uploads/2026/08/evidence.png?fit=1200%2C675&amp;amp;ssl=1"&gt;
&lt;meta property="og:image:width" content="1200"&gt;
&lt;meta property="og:image:height" content="675"&gt;
&lt;meta property="og:image:alt" content=""&gt;
&lt;meta property="og:locale" content="en_US"&gt;
&lt;meta property="fb:app_id" content="249643311490"&gt;
&lt;meta property="article:publisher" content="https://www.facebook.com/WordPresscom"&gt;
&lt;meta name="twitter:text:title" content="AI Software Development – What Does The Data&amp;nbsp;Say?"&gt;
&lt;meta name="twitter:image" content="https://i0.wp.com/codemanship.wordpress.com/wp-content/uploads/2026/08/evidence.png?fit=1200%2C675&amp;amp;ssl=1&amp;amp;w=640"&gt;
&lt;meta name="twitter:card" content="summary_large_image"&gt;

&lt;!-- End Jetpack Open Graph Tags --&gt;
&lt;link rel="openid.server" href="https://codemanship.wordpress.com/?openidserver=1"&gt;
&lt;link rel="openid.delegate" href="https://codemanship.wordpress.com/"&gt;
&lt;link rel="search" type="application/opensearchdescription+xml" href="https://codemanship.wordpress.com/osd.xml" title="Codemanship's Blog"&gt;
&lt;link rel="search" type="application/opensearchdescription+xml" href="https://s1.wp.com/opensearch.xml" title="WordPress.com"&gt;
		&lt;style type="text/css"&gt;
			.recentcomments a {
				display: inline !important;
				padding: 0 !important;
				margin: 0 !important;
			}

			table.recentcommentsavatartop img.avatar, table.recentcommentsavatarend img.avatar {
				border: 0px;
				margin: 0;
			}

			table.recentcommentsavatartop a, table.recentcommentsavatarend a {
				border: 0px !important;
				background-color: transparent !important;
			}

			td.recentcommentsavatarend, td.recentcommentsavatartop {
				padding: 0px 0px 1px 0px;
				margin: 0px;
			}

			td.recentcommentstextend {
				border: none !important;
				padding: 0px 0px 2px 10px;
			}

			.rtl td.recentcommentstextend {
				padding: 0px 10px 2px 0px;
			}

			td.recentcommentstexttop {
				border: none;
				padding: 0px 0px 0px 10px;
			}

			.rtl td.recentcommentstexttop {
				padding: 0px 10px 0px 0px;
			}
		&lt;/style&gt;
		&lt;meta name="description" content="I'm currently pulling together a bunch of sources - that are mostly recent - on the topic of LLMs and their use in software development. Some are peer-reviewed studies. Some are industry studies that haven't been peer-reviewed. One is statistical physics. Expect more from that angle. Wanna' know the limits of a technology? Ask a…"&gt;
&lt;link rel="icon" href="https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=32" sizes="32x32"&gt;
&lt;link rel="icon" href="https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=192" sizes="192x192"&gt;
&lt;link rel="apple-touch-icon" href="https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=180"&gt;
&lt;meta name="msapplication-TileImage" content="https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=270"&gt;
&lt;style id="jetpack-block-subscriptions-inline-css"&gt;
.is-style-compact .is-not-subscriber :is(.wp-block-button__link,.wp-block-jetpack-subscriptions__button){border-end-start-radius:0!important;border-start-start-radius:0!important;margin-inline-start:0!important}.is-style-compact .is-not-subscriber .components-text-control__input,.is-style-compact .is-not-subscriber p#subscribe-email input[type=email]{border-end-end-radius:0!important;border-start-end-radius:0!important}.is-style-compact:not(.wp-block-jetpack-subscriptions__use-newline) .components-text-control__input{border-inline-end-width:0!important}.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form-container{display:flex;flex-direction:column}.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline:not(.wp-block-jetpack-subscriptions__use-newline) .is-not-subscriber .wp-block-jetpack-subscriptions__form-elements{align-items:flex-start;display:flex}.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline:not(.wp-block-jetpack-subscriptions__use-newline) p#subscribe-submit{display:flex;justify-content:center}.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form .wp-block-jetpack-subscriptions__button,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form .wp-block-jetpack-subscriptions__textfield .components-text-control__input,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form button,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form input[type=email],.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form .wp-block-jetpack-subscriptions__button,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form .wp-block-jetpack-subscriptions__textfield .components-text-control__input,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form button,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form input[type=email]{box-sizing:border-box;cursor:pointer;line-height:1.3;min-width:auto!important;white-space:nowrap!important}.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form input[type=email]::placeholder,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form input[type=email]:disabled,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form input[type=email]::placeholder,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form input[type=email]:disabled{color:currentColor;opacity:.5}.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form .wp-block-jetpack-subscriptions__button,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form button,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form .wp-block-jetpack-subscriptions__button,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form button{border-color:#0000;border-style:solid}.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form .wp-block-jetpack-subscriptions__textfield,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form p#subscribe-email,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form .wp-block-jetpack-subscriptions__textfield,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form p#subscribe-email{background:#0000;flex-grow:1}.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form .wp-block-jetpack-subscriptions__textfield .components-base-control__field,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form .wp-block-jetpack-subscriptions__textfield .components-text-control__input,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form .wp-block-jetpack-subscriptions__textfield input[type=email],.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form p#subscribe-email .components-base-control__field,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form p#subscribe-email .components-text-control__input,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form p#subscribe-email input[type=email],.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form .wp-block-jetpack-subscriptions__textfield .components-base-control__field,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form .wp-block-jetpack-subscriptions__textfield .components-text-control__input,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form .wp-block-jetpack-subscriptions__textfield input[type=email],.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form p#subscribe-email .components-base-control__field,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form p#subscribe-email .components-text-control__input,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form p#subscribe-email input[type=email]{height:auto;margin:0;width:100%}.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form p#subscribe-email,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline .wp-block-jetpack-subscriptions__form p#subscribe-submit,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form p#subscribe-email,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline form p#subscribe-submit{line-height:0;margin:0;padding:0}.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline.wp-block-jetpack-subscriptions__show-subs .wp-block-jetpack-subscriptions__subscount{font-size:16px;margin:8px 0;text-align:end}.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline.wp-block-jetpack-subscriptions__use-newline .wp-block-jetpack-subscriptions__form-elements{display:block}.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline.wp-block-jetpack-subscriptions__use-newline .wp-block-jetpack-subscriptions__button,.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline.wp-block-jetpack-subscriptions__use-newline button{display:inline-block;max-width:100%}.wp-block-jetpack-subscriptions.wp-block-jetpack-subscriptions__supports-newline.wp-block-jetpack-subscriptions__use-newline .wp-block-jetpack-subscriptions__subscount{text-align:start}#subscribe-submit.is-link{text-align:center;width:auto!important}#subscribe-submit.is-link a{margin-left:0!important;margin-top:0!important;width:auto!important}@keyframes jetpack-memberships_button__spinner-animation{to{transform:rotate(1turn)}}.jetpack-memberships-spinner{display:none;height:1em;margin:0 0 0 5px;width:1em}.jetpack-memberships-spinner svg{height:100%;margin-bottom:-2px;width:100%}.jetpack-memberships-spinner-rotating{animation:jetpack-memberships_button__spinner-animation .75s linear infinite;transform-origin:center}.is-loading .jetpack-memberships-spinner{display:inline-block}body.jetpack-memberships-modal-open{overflow:hidden}dialog.jetpack-memberships-modal{opacity:1}dialog.jetpack-memberships-modal,dialog.jetpack-memberships-modal iframe{background:#0000;border:0;bottom:0;box-shadow:none;height:100%;left:0;margin:0;padding:0;position:fixed;right:0;top:0;width:100%}dialog.jetpack-memberships-modal::backdrop{background-color:#000;opacity:.7;transition:opacity .2s ease-out}dialog.jetpack-memberships-modal.is-loading,dialog.jetpack-memberships-modal.is-loading::backdrop{opacity:0}
/*# sourceURL=/wp-content/mu-plugins/jetpack-plugin/moon/_inc/blocks/subscriptions/view.css?minify=false */
&lt;/style&gt;&lt;link rel="stylesheet" href="https://fonts-api.wp.com/css?family=Alegreya+Sans:r%7CAlegreya+Sans:r,i,b,bi&amp;amp;subset=latin,latin-ext,latin,latin-ext" media="all"&gt;

&lt;script src="/wp-includes/js/wp-emoji-release.min.js?m=1783090810i&amp;amp;ver=7.1-alpha-62686" defer=""&gt;&lt;/script&gt;&lt;link rel="stylesheet" id="gravatar-card-css" href="https://0.gravatar.com/js/hovercards/hovercards.min.css?ver=202633d1133547e8a1f2801609f51cef12522abe7f6647d869e9fbc974fe356a37e613"&gt;&lt;link href="/wp-content/mu-plugins/actionbar/actionbar.css?v=20250116" type="text/css" rel="stylesheet"&gt;&lt;/head&gt;

&lt;body class="wp-singular post-template-default single single-post postid-4719 single-format-standard wp-embed-responsive wp-theme-pubtwentysixteen customizer-styles-applied jetpack-reblog-enabled"&gt;
&lt;div id="page" class="site"&gt;
	&lt;div class="site-inner"&gt;
		&lt;a class="skip-link screen-reader-text" href="#content"&gt;
			Skip to content		&lt;/a&gt;

		&lt;header id="masthead" class="site-header"&gt;
			&lt;div class="site-header-main"&gt;
				&lt;div class="site-branding"&gt;
					&lt;a href="https://codemanship.wordpress.com/" class="site-logo-link" rel="home" itemprop="url"&gt;&lt;/a&gt;
											&lt;p class="site-title"&gt;&lt;a href="https://codemanship.wordpress.com/" rel="home"&gt;Codemanship's Blog&lt;/a&gt;&lt;/p&gt;
												&lt;p class="site-description"&gt;Musings And Mutterings By Jason Gorman&lt;/p&gt;
									&lt;/div&gt;&lt;!-- .site-branding --&gt;

									&lt;button id="menu-toggle" class="menu-toggle"&gt;Menu&lt;/button&gt;

					&lt;div id="site-header-menu" class="site-header-menu"&gt;
													&lt;nav id="site-navigation" class="main-navigation" aria-label="Primary Menu"&gt;
								&lt;div class="menu-codemanship-container"&gt;&lt;ul id="menu-codemanship" class="primary-menu"&gt;&lt;li id="menu-item-65" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-home menu-item-65"&gt;&lt;a href="https://codemanship.wordpress.com"&gt;Home&lt;/a&gt;&lt;/li&gt;
&lt;li id="menu-item-66" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-66"&gt;&lt;a href="https://codemanship.wordpress.com/about/"&gt;About&lt;/a&gt;&lt;/li&gt;
&lt;li id="menu-item-67" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-67"&gt;&lt;a href="https://www.codemanship.co.uk"&gt;Training&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;							&lt;/nav&gt;&lt;!-- .main-navigation --&gt;
						
											&lt;/div&gt;&lt;!-- .site-header-menu --&gt;
							&lt;/div&gt;&lt;!-- .site-header-main --&gt;

											&lt;div class="header-image"&gt;
					&lt;a href="https://codemanship.wordpress.com/" rel="home"&gt;
						&lt;img src="https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_wp_header1-1.jpg" srcset="" sizes="" width="1200" height="280" alt="Codemanship's Blog"&gt;
					&lt;/a&gt;
				&lt;/div&gt;&lt;!-- .header-image --&gt;
					&lt;/header&gt;&lt;!-- .site-header --&gt;

		&lt;div id="content" class="site-content"&gt;

&lt;div id="primary" class="content-area"&gt;
	&lt;main id="main" class="site-main"&gt;
		
&lt;article id="post-4719" class="post-4719 post type-post status-publish format-standard has-post-thumbnail hentry category-a-i"&gt;
	&lt;header class="entry-header"&gt;
		&lt;h1 class="entry-title"&gt;AI Software Development – What Does The Data&amp;nbsp;Say?&lt;/h1&gt;	&lt;/header&gt;&lt;!-- .entry-header --&gt;

	
	
		&lt;div class="post-thumbnail"&gt;
			&lt;img width="1200" height="675" src="https://codemanship.wordpress.com/wp-content/uploads/2026/08/evidence.png?w=1200" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="" decoding="async" srcset="https://codemanship.wordpress.com/wp-content/uploads/2026/08/evidence.png?w=1200 1200w, https://codemanship.wordpress.com/wp-content/uploads/2026/08/evidence.png?w=150 150w, https://codemanship.wordpress.com/wp-content/uploads/2026/08/evidence.png?w=300 300w, https://codemanship.wordpress.com/wp-content/uploads/2026/08/evidence.png?w=768 768w, https://codemanship.wordpress.com/wp-content/uploads/2026/08/evidence.png?w=1024 1024w, https://codemanship.wordpress.com/wp-content/uploads/2026/08/evidence.png?w=1440 1440w, https://codemanship.wordpress.com/wp-content/uploads/2026/08/evidence.png 1672w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 60vw, (max-width: 1362px) 62vw, 840px" data-attachment-id="4725" data-permalink="https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/evidence/" data-orig-file="https://codemanship.wordpress.com/wp-content/uploads/2026/08/evidence.png" data-orig-size="1672,941" data-comments-opened="1" data-image-meta="{&amp;quot;aperture&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;credit&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;camera&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;caption&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;created_timestamp&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;copyright&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;focal_length&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;iso&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;shutter_speed&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;title&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;orientation&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;alt&amp;quot;:&amp;quot;&amp;quot;}" data-image-title="evidence" data-image-description="" data-image-caption="" data-large-file="https://codemanship.wordpress.com/wp-content/uploads/2026/08/evidence.png?w=840"&gt;	&lt;/div&gt;&lt;!-- .post-thumbnail --&gt;

	
	&lt;div class="entry-content"&gt;
		
&lt;p class="wp-block-paragraph"&gt;I’m currently pulling together a bunch of sources – that are mostly recent – on the topic of LLMs and their use in software development.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;Some are peer-reviewed studies. Some are industry studies that haven’t been peer-reviewed.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;One is statistical physics. Expect more from that angle. Wanna’ know the limits of a technology? Ask a physicist.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;One is just a blog post, but very useful information about the effect of context size.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;Most are corroborated by personal experiments and also observations on teams. As time goes on and more data comes in, my picture comes more into focus.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Code Evolution &amp;amp; Long-Horizon Agentic Workflows&lt;/strong&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;SWE-CI: Evaluating Agent Capabilities in Maintaining Codebases via Continuous Integration&lt;br&gt;&lt;a href="https://arxiv.org/abs/2603.03823"&gt;https://arxiv.org/abs/2603.03823&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;SlopCodeBench: Benchmarking How Coding Agents Degrade Over Long-Horizon Iterative Tasks&lt;br&gt;&lt;a href="https://arxiv.org/html/2603.24755v1"&gt;https://arxiv.org/html/2603.24755v1&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;SWE-Milestone: Evaluating AI Agents on Continuous Software Evolution&lt;br&gt;&lt;a href="https://arxiv.org/abs/2603.13428"&gt;https://arxiv.org/abs/2603.13428&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Context Engineering&lt;/strong&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;Context Is What You Need: The Maximum Effective Context Window for Real World Limits of LLMs&lt;br&gt;&lt;a href="https://arxiv.org/abs/2509.21361"&gt;https://arxiv.org/abs/2509.21361&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;Beyond RAG vs. Long-Context: Learning Distraction-Aware Retrieval for Efficient Knowledge Grounding&lt;br&gt;&lt;a href="https://arxiv.org/abs/2509.21865"&gt;https://arxiv.org/abs/2509.21865&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;Evaluating AGENTS.md: Are Repository-Level Context Files Helpful for Coding Agents?&lt;br&gt;&lt;a href="https://arxiv.org/abs/2602.11988"&gt;https://arxiv.org/abs/2602.11988&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;(blog post – informational, not peer-reviewed research)&lt;br&gt;The Hidden Science Behind LLM Token Limits (And How Million-Token Models Actually Work)&lt;br&gt;&lt;a href="https://www.ashisharora.ai/post/the-hidden-science-behind-llm-token-limits-and-how-million-token-models-actually-work"&gt;https://www.ashisharora.ai/post/the-hidden-science-behind-llm-token-limits-and-how-million-token-models-actually-work&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;Language models are not naysayers: An analysis of language models on negation benchmarks&lt;br&gt;&lt;a href="https://arxiv.org/abs/2602.11988"&gt;https://arxiv.org/abs/2602.11988&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;Rethinking the Role of Demonstrations: What Makes In-Context Learning Work?&lt;br&gt;&lt;a href="https://aclanthology.org/2022.emnlp-main.759"&gt;https://aclanthology.org/2022.emnlp-main.759&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;STALE: Can LLM Agents Know When Their Memories Are No Longer Valid?&lt;br&gt;&lt;a href="https://arxiv.org/abs/2605.06527"&gt;https://arxiv.org/abs/2605.06527&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;Task Matters: Knowledge Requirements Shape LLM Responses to&lt;br&gt;Context–Memory Conflict&lt;br&gt;&lt;a href="https://aclanthology.org/2026.findings-acl.202"&gt;https://aclanthology.org/2026.findings-acl.202&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;(I call these “dominant priors”, when information in the model overrides that provided in the context)&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Large-scale Industry Studies in Software Engineering&lt;/strong&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;What 28 million workflows reveal about AI coding’s biggest risk (CircleCI)&lt;br&gt;&lt;a href="https://www.linkedin.com/pulse/what-28-million-workflows-reveal-ai-codings-biggest-risk-circleci-j9syc/"&gt;https://www.linkedin.com/pulse/what-28-million-workflows-reveal-ai-codings-biggest-risk-circleci-j9syc/&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;The Acceleration Whiplash – AI Engineering Report 2026 (Faros)&lt;br&gt;&lt;a href="https://www.faros.ai/research/ai-acceleration-whiplash"&gt;https://www.faros.ai/research/ai-acceleration-whiplash&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;State of AI-assisted Software Development 2025 (DORA)&lt;br&gt;&lt;a href="https://dora.dev/research/2025/dora-report/"&gt;https://dora.dev/research/2025/dora-report/&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Psychology, Cognition &amp;amp; Learning&lt;/strong&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;Super-intelligence or Superstition? Exploring Psychological Factors Influencing Belief in AI Predictions about Personal Behavior&lt;br&gt;&lt;a href="https://arxiv.org/html/2408.06602v3"&gt;https://arxiv.org/html/2408.06602v3&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;The Impact of Generative AI on Critical Thinking: Self-Reported Reductions in Cognitive Effort and Confidence Effects From a Survey of Knowledge Workers&lt;br&gt;&lt;a href="https://arxiv.org/html/2408.06602v3"&gt;https://arxiv.org/html/2408.06602v3&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;Experimental evidence of the effects of large language models versus web search on depth of learning&lt;br&gt;&lt;a href="https://arxiv.org/html/2408.06602v3"&gt;https://arxiv.org/html/2408.06602v3&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;At What Cost? Software Developers’ Well-Being in the Age of GenAI&lt;br&gt;&lt;a href="https://ourarchive.otago.ac.nz/esploro/outputs/preprint/At-What-Cost-Software-Developers-Well-Being/9926870122801891"&gt;https://ourarchive.otago.ac.nz/esploro/outputs/preprint/At-What-Cost-Software-Developers-Well-Being/9926870122801891&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Limits of LLMs &amp;amp; Deep Learning&lt;/strong&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;The wall confronting large language models – (statistical mechanics study)&lt;br&gt;&lt;a href="https://arxiv.org/abs/2507.19703"&gt;https://arxiv.org/abs/2507.19703&lt;/a&gt;&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;Learning long-term dependencies with gradient descent is difficult&lt;br&gt;&lt;a href="https://pubmed.ncbi.nlm.nih.gov/18267787/"&gt;https://pubmed.ncbi.nlm.nih.gov/18267787/&lt;/a&gt;&lt;br&gt;(Why deep neural networks can’t see the bigger picture, at any scale of model)&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;&lt;em&gt;What are the implications for teams using this technology? If you’re interested in a hype-free, evidence-based take about why the technical practices of Agile Software Development are so closely aligned with AI-assisted and agentic software engineering, join me on October 6th at 18:45 BST&lt;/em&gt;.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;&lt;em&gt;Register: &lt;a href="https://www.tickettailor.com/events/codemanship/2324138"&gt;https://www.tickettailor.com/events/codemanship/2324138&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;div id="jp-post-flair" class="sharedaddy sd-like-enabled sd-sharing-enabled"&gt;&lt;div class="sharedaddy sd-sharing-enabled"&gt;&lt;div class="robots-nocontent sd-block sd-social sd-social-icon-text sd-sharing"&gt;&lt;h3 class="sd-title"&gt;Share this:&lt;/h3&gt;&lt;div class="sd-content"&gt;&lt;ul&gt;&lt;li class="share-twitter"&gt;&lt;a rel="nofollow noopener noreferrer" data-shared="sharing-twitter-4719" class="share-twitter sd-button share-icon" href="https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/?share=twitter" target="_blank" aria-labelledby="sharing-twitter-4719"&gt;
				&lt;span id="sharing-twitter-4719" hidden=""&gt;Share on X (Opens in new window)&lt;/span&gt;
				&lt;span&gt;X&lt;/span&gt;
			&lt;/a&gt;&lt;/li&gt;&lt;li class="share-facebook"&gt;&lt;a rel="nofollow noopener noreferrer" data-shared="sharing-facebook-4719" class="share-facebook sd-button share-icon" href="https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/?share=facebook" target="_blank" aria-labelledby="sharing-facebook-4719"&gt;
				&lt;span id="sharing-facebook-4719" hidden=""&gt;Share on Facebook (Opens in new window)&lt;/span&gt;
				&lt;span&gt;Facebook&lt;/span&gt;
			&lt;/a&gt;&lt;/li&gt;&lt;li class="share-linkedin"&gt;&lt;a rel="nofollow noopener noreferrer" data-shared="sharing-linkedin-4719" class="share-linkedin sd-button share-icon" href="https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/?share=linkedin" target="_blank" aria-labelledby="sharing-linkedin-4719"&gt;
				&lt;span id="sharing-linkedin-4719" hidden=""&gt;Share on LinkedIn (Opens in new window)&lt;/span&gt;
				&lt;span&gt;LinkedIn&lt;/span&gt;
			&lt;/a&gt;&lt;/li&gt;&lt;li class="share-reddit"&gt;&lt;a rel="nofollow noopener noreferrer" data-shared="sharing-reddit-4719" class="share-reddit sd-button share-icon" href="https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/?share=reddit" target="_blank" aria-labelledby="sharing-reddit-4719"&gt;
				&lt;span id="sharing-reddit-4719" hidden=""&gt;Share on Reddit (Opens in new window)&lt;/span&gt;
				&lt;span&gt;Reddit&lt;/span&gt;
			&lt;/a&gt;&lt;/li&gt;&lt;li class="share-email"&gt;&lt;a rel="nofollow noopener noreferrer" data-shared="sharing-email-4719" class="share-email sd-button share-icon" href="mailto:?subject=%5BShared%20Post%5D%20AI%20Software%20Development%20-%20What%20Does%20The%20Data%20Say%3F&amp;amp;body=https%3A%2F%2Fcodemanship.wordpress.com%2F2026%2F08%2F12%2Fai-software-development-what-does-the-data-say%2F&amp;amp;share=email" target="_blank" aria-labelledby="sharing-email-4719" data-email-share-error-title="Do you have email set up?" data-email-share-error-text="If you're having problems sharing via email, you might not have email set up for your browser. You may need to create a new email yourself." data-email-share-nonce="f7adcc12fb" data-email-share-track-url="https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/?share=email"&gt;
				&lt;span id="sharing-email-4719" hidden=""&gt;Email a link to a friend (Opens in new window)&lt;/span&gt;
				&lt;span&gt;Email&lt;/span&gt;
			&lt;/a&gt;&lt;/li&gt;&lt;li class="share-end"&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="sharedaddy sd-block sd-like jetpack-likes-widget-wrapper jetpack-likes-widget-unloaded" id="like-post-wrapper-15031761-4719-6a7cc741583e4" data-src="//widgets.wp.com/likes/index.html?ver=20260812#blog_id=15031761&amp;amp;post_id=4719&amp;amp;origin=codemanship.wordpress.com&amp;amp;obj_id=15031761-4719-6a7cc741583e4" data-name="like-post-frame-15031761-4719-6a7cc741583e4" data-title="Like or Reblog"&gt;&lt;div class="likes-widget-placeholder post-likes-widget-placeholder" style="height: 55px;"&gt;&lt;span class="button"&gt;&lt;span&gt;Like&lt;/span&gt;&lt;/span&gt; &lt;span class="loading"&gt;Loading...&lt;/span&gt;&lt;/div&gt;&lt;span class="sd-text-color"&gt;&lt;/span&gt;&lt;a class="sd-link-color"&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div id="jp-relatedposts" class="jp-relatedposts" style="display: block;"&gt;
	&lt;h3 class="jp-relatedposts-headline"&gt;&lt;em&gt;Related&lt;/em&gt;&lt;/h3&gt;
&lt;div class="jp-relatedposts-items jp-relatedposts-items-minimal jp-relatedposts-grid "&gt;&lt;p class="jp-relatedposts-post jp-relatedposts-post0" data-post-id="4537" data-post-format="false"&gt;&lt;span class="jp-relatedposts-post-title"&gt;&lt;a class="jp-relatedposts-post-a" href="https://codemanship.wordpress.com/2026/05/28/what-if-the-real-key-to-ai-coding-is-old-fashioned-boring/" title="What If The Real Key To AI Coding Is Old-Fashioned &amp;amp;amp;&amp;amp;nbsp;Boring?" data-origin="4719" data-position="0"&gt;What If The Real Key To AI Coding Is Old-Fashioned &amp;amp;&amp;nbsp;Boring?&lt;/a&gt;&lt;/span&gt;&lt;time class="jp-relatedposts-post-date" datetime="May 28, 2026" style="display: block;"&gt;May 28, 2026&lt;/time&gt;&lt;span class="jp-relatedposts-post-context"&gt;In "a.i."&lt;/span&gt;&lt;/p&gt;&lt;p class="jp-relatedposts-post jp-relatedposts-post1" data-post-id="3470" data-post-format="false"&gt;&lt;span class="jp-relatedposts-post-title"&gt;&lt;a class="jp-relatedposts-post-a" href="https://codemanship.wordpress.com/2026/01/21/finally-proof-that-agentic-ai-scales-for-creating-broken-software/" title="Finally! Proof That Agentic AI Scales (For Creating Broken&amp;amp;nbsp;Software)" data-origin="4719" data-position="1"&gt;Finally! Proof That Agentic AI Scales (For Creating Broken&amp;nbsp;Software)&lt;/a&gt;&lt;/span&gt;&lt;time class="jp-relatedposts-post-date" datetime="January 21, 2026" style="display: block;"&gt;January 21, 2026&lt;/time&gt;&lt;span class="jp-relatedposts-post-context"&gt;In "a.i."&lt;/span&gt;&lt;/p&gt;&lt;p class="jp-relatedposts-post jp-relatedposts-post2" data-post-id="3943" data-post-format="false"&gt;&lt;span class="jp-relatedposts-post-title"&gt;&lt;a class="jp-relatedposts-post-a" href="https://codemanship.wordpress.com/2026/03/11/the-ai-ready-software-developer-21-stuck-in-a-doom-loop-drop-a-gear/" title="The AI-Ready Software Developer #21 &amp;amp;#8211; Stuck In A &amp;amp;#8220;Doom Loop&amp;amp;#8221;? Drop A&amp;amp;nbsp;Gear" data-origin="4719" data-position="2"&gt;The AI-Ready Software Developer #21 – Stuck In A “Doom Loop”? Drop A&amp;nbsp;Gear&lt;/a&gt;&lt;/span&gt;&lt;time class="jp-relatedposts-post-date" datetime="March 11, 2026" style="display: block;"&gt;March 11, 2026&lt;/time&gt;&lt;span class="jp-relatedposts-post-context"&gt;In "a.i."&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="author-info"&gt;
	&lt;div class="author-avatar"&gt;
		&lt;img referrerpolicy="no-referrer" alt="Unknown's avatar" src="https://1.gravatar.com/avatar/aadcfbd208e65a0829f5d259192a5886454439a708e425500c59c535e28ae081?s=42&amp;amp;d=identicon&amp;amp;r=G" srcset="https://1.gravatar.com/avatar/aadcfbd208e65a0829f5d259192a5886454439a708e425500c59c535e28ae081?s=42&amp;amp;d=identicon&amp;amp;r=G 1x, https://1.gravatar.com/avatar/aadcfbd208e65a0829f5d259192a5886454439a708e425500c59c535e28ae081?s=63&amp;amp;d=identicon&amp;amp;r=G 1.5x, https://1.gravatar.com/avatar/aadcfbd208e65a0829f5d259192a5886454439a708e425500c59c535e28ae081?s=84&amp;amp;d=identicon&amp;amp;r=G 2x, https://1.gravatar.com/avatar/aadcfbd208e65a0829f5d259192a5886454439a708e425500c59c535e28ae081?s=126&amp;amp;d=identicon&amp;amp;r=G 3x, https://1.gravatar.com/avatar/aadcfbd208e65a0829f5d259192a5886454439a708e425500c59c535e28ae081?s=168&amp;amp;d=identicon&amp;amp;r=G 4x" class="avatar avatar-42 wp-hovercard-attachment grav-hashed grav-hijack" height="42" width="42" decoding="async"&gt;	&lt;/div&gt;&lt;!-- .author-avatar --&gt;

	&lt;div class="author-description"&gt;
		&lt;h2 class="author-title"&gt;&lt;span class="author-heading"&gt;Author:&lt;/span&gt; codemanship&lt;/h2&gt;

		&lt;p class="author-bio"&gt;
			Founder of Codemanship Ltd and code craft coach and trainer			&lt;a class="author-link" href="https://codemanship.wordpress.com/author/codemanship/" rel="author"&gt;
				View all posts by codemanship			&lt;/a&gt;
		&lt;/p&gt;&lt;!-- .author-bio --&gt;
	&lt;/div&gt;&lt;!-- .author-description --&gt;
&lt;/div&gt;&lt;!-- .author-info --&gt;
	&lt;/div&gt;&lt;!-- .entry-content --&gt;

	&lt;footer class="entry-footer"&gt;
		&lt;span class="byline"&gt;&lt;span class="author vcard"&gt;&lt;img referrerpolicy="no-referrer" alt="Unknown's avatar" src="https://1.gravatar.com/avatar/aadcfbd208e65a0829f5d259192a5886454439a708e425500c59c535e28ae081?s=49&amp;amp;d=identicon&amp;amp;r=G" srcset="https://1.gravatar.com/avatar/aadcfbd208e65a0829f5d259192a5886454439a708e425500c59c535e28ae081?s=49&amp;amp;d=identicon&amp;amp;r=G 1x, https://1.gravatar.com/avatar/aadcfbd208e65a0829f5d259192a5886454439a708e425500c59c535e28ae081?s=74&amp;amp;d=identicon&amp;amp;r=G 1.5x, https://1.gravatar.com/avatar/aadcfbd208e65a0829f5d259192a5886454439a708e425500c59c535e28ae081?s=98&amp;amp;d=identicon&amp;amp;r=G 2x, https://1.gravatar.com/avatar/aadcfbd208e65a0829f5d259192a5886454439a708e425500c59c535e28ae081?s=147&amp;amp;d=identicon&amp;amp;r=G 3x, https://1.gravatar.com/avatar/aadcfbd208e65a0829f5d259192a5886454439a708e425500c59c535e28ae081?s=196&amp;amp;d=identicon&amp;amp;r=G 4x" class="avatar avatar-49 wp-hovercard-attachment grav-hashed grav-hijack" height="49" width="49" decoding="async"&gt;&lt;span class="screen-reader-text"&gt;Author &lt;/span&gt; &lt;a class="url fn n" href="https://codemanship.wordpress.com/author/codemanship/"&gt;codemanship&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="posted-on"&gt;&lt;span class="screen-reader-text"&gt;Posted on &lt;/span&gt;&lt;a href="https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/" rel="bookmark"&gt;&lt;time class="entry-date published" datetime="2026-08-12T18:41:50+01:00"&gt;August 12, 2026&lt;/time&gt;&lt;time class="updated" datetime="2026-08-12T18:46:26+01:00"&gt;August 12, 2026&lt;/time&gt;&lt;/a&gt;&lt;/span&gt;&lt;span class="cat-links"&gt;&lt;span class="screen-reader-text"&gt;Categories &lt;/span&gt;&lt;a href="https://codemanship.wordpress.com/category/a-i/" rel="category tag"&gt;a.i.&lt;/a&gt;&lt;/span&gt;			&lt;/footer&gt;&lt;!-- .entry-footer --&gt;
&lt;/article&gt;&lt;!-- #post-4719 --&gt;

&lt;div id="comments" class="comments-area"&gt;

	
	
		&lt;div id="respond" class="comment-respond"&gt;
		&lt;h2 id="reply-title" class="comment-reply-title"&gt;Leave a comment &lt;small&gt;&lt;a rel="nofollow" id="cancel-comment-reply-link" href="/2026/08/12/ai-software-development-what-does-the-data-say/#respond" style="display:none;"&gt;Cancel reply&lt;/a&gt;&lt;/small&gt;&lt;/h2&gt;&lt;form action="https://codemanship.wordpress.com/wp-comments-post.php" method="post" id="commentform" class="comment-form"&gt;


&lt;div class="comment-form__verbum transparent"&gt;&lt;/div&gt;&lt;div class="verbum-form-meta"&gt;&lt;input type="hidden" name="comment_post_ID" value="4719" id="comment_post_ID"&gt;
&lt;input type="hidden" name="comment_parent" id="comment_parent" value="0"&gt;

			&lt;input type="hidden" name="highlander_comment_nonce" id="highlander_comment_nonce" value="78997a4899"&gt;
			&lt;input type="hidden" name="verbum_show_subscription_modal" value=""&gt;&lt;/div&gt;&lt;p style="display: none;"&gt;&lt;input type="hidden" id="akismet_comment_nonce" name="akismet_comment_nonce" value="26b062dc38"&gt;&lt;/p&gt;&lt;p style="display: none !important;" class="akismet-fields-container" data-prefix="ak_"&gt;&lt;label&gt;Δ&lt;textarea name="ak_hp_textarea" cols="45" rows="8" maxlength="100"&gt;&lt;/textarea&gt;&lt;/label&gt;&lt;input type="hidden" id="ak_js_1" name="ak_js" value="1786563258901"&gt;&lt;script&gt;
document.getElementById( "ak_js_1" ).setAttribute( "value", ( new Date() ).getTime() );
&lt;/script&gt;
&lt;/p&gt;&lt;/form&gt;	&lt;/div&gt;&lt;!-- #respond --&gt;
	
&lt;/div&gt;&lt;!-- .comments-area --&gt;

	&lt;nav class="navigation post-navigation" aria-label="Posts"&gt;
		&lt;h2 class="screen-reader-text"&gt;Post navigation&lt;/h2&gt;
		&lt;div class="nav-links"&gt;&lt;div class="nav-previous"&gt;&lt;a href="https://codemanship.wordpress.com/2026/08/01/do-your-quality-gates-see-the-brown-mms-in-the-bowl/" rel="prev"&gt;&lt;span class="meta-nav" aria-hidden="true"&gt;Previous&lt;/span&gt; &lt;span class="screen-reader-text"&gt;Previous post:&lt;/span&gt; &lt;span class="post-title"&gt;Do Your Quality Gates See The Brown M&amp;amp;Ms In The&amp;nbsp;Bowl?&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
	&lt;/nav&gt;
	&lt;/main&gt;&lt;!-- .site-main --&gt;

	
&lt;/div&gt;&lt;!-- .content-area --&gt;


	&lt;aside id="secondary" class="sidebar widget-area"&gt;
		&lt;section id="search-2" class="widget widget_search"&gt;
&lt;form role="search" method="get" class="search-form" action="https://codemanship.wordpress.com/"&gt;
	&lt;label&gt;
		&lt;span class="screen-reader-text"&gt;
			Search for:		&lt;/span&gt;
		&lt;input type="search" class="search-field" placeholder="Search …" value="" name="s"&gt;
	&lt;/label&gt;
	&lt;button type="submit" class="search-submit"&gt;&lt;span class="screen-reader-text"&gt;
		Search	&lt;/span&gt;&lt;/button&gt;
&lt;/form&gt;
&lt;/section&gt;&lt;section id="block-5" class="widget widget_block widget_text"&gt;
&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://codemanship.co.uk/"&gt;Visit my site&lt;/a&gt; for details of Training &amp;amp; Coaching in Technical Practices for rapid, reliable and sustainable software delivery, with and without AI&lt;/p&gt;
&lt;/section&gt;&lt;section id="block-3" class="widget widget_block"&gt;&lt;div class="wp-block-jetpack-subscriptions__supports-newline wp-block-jetpack-subscriptions"&gt;
		&lt;div&gt;
			&lt;div&gt;
				&lt;div&gt;
					&lt;p&gt;
						&lt;a href="https://codemanship.wordpress.com/?post_type=post&amp;amp;p=4719" style="font-size: 16px;padding: 15px 23px 15px 23px;margin: 0; margin-left: 10px;border-radius: 0px;border-width: 1px; background-color: #113AF5; color: #FFFFFF; text-decoration: none; white-space: nowrap; margin-left: 0"&gt;Subscribe&lt;/a&gt;
					&lt;/p&gt;
				&lt;/div&gt;
			&lt;/div&gt;
		&lt;/div&gt;
	&lt;/div&gt;&lt;/section&gt;
		&lt;section id="recent-posts-2" class="widget widget_recent_entries"&gt;
		&lt;h2 class="widget-title"&gt;Recent Posts&lt;/h2&gt;&lt;nav aria-label="Recent Posts"&gt;
		&lt;ul&gt;
											&lt;li&gt;
					&lt;a href="https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/" aria-current="page"&gt;AI Software Development – What Does The Data&amp;nbsp;Say?&lt;/a&gt;
									&lt;/li&gt;
											&lt;li&gt;
					&lt;a href="https://codemanship.wordpress.com/2026/08/01/do-your-quality-gates-see-the-brown-mms-in-the-bowl/"&gt;Do Your Quality Gates See The Brown M&amp;amp;Ms In The&amp;nbsp;Bowl?&lt;/a&gt;
									&lt;/li&gt;
											&lt;li&gt;
					&lt;a href="https://codemanship.wordpress.com/2026/07/19/the-power-of-why/"&gt;The Power Of “Why?”&lt;/a&gt;
									&lt;/li&gt;
											&lt;li&gt;
					&lt;a href="https://codemanship.wordpress.com/2026/07/15/feedback-latency-learning-productivity/"&gt;Feedback Latency, Learning &amp;amp; “Productivity”&lt;/a&gt;
									&lt;/li&gt;
											&lt;li&gt;
					&lt;a href="https://codemanship.wordpress.com/2026/06/24/even-if-we-could-extend-the-horizons-of-autonomous-coding-agents-does-that-mean-we-should/"&gt;Even If We Could Extend The Horizons of Autonomous Coding Agents, Does That Mean We&amp;nbsp;Should?&lt;/a&gt;
									&lt;/li&gt;
					&lt;/ul&gt;

		&lt;/nav&gt;&lt;/section&gt;&lt;section id="archives-2" class="widget widget_archive"&gt;&lt;h2 class="widget-title"&gt;Archives&lt;/h2&gt;&lt;nav aria-label="Archives"&gt;
			&lt;ul&gt;
					&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2026/08/"&gt;August 2026&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2026/07/"&gt;July 2026&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2026/06/"&gt;June 2026&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2026/05/"&gt;May 2026&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2026/04/"&gt;April 2026&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2026/03/"&gt;March 2026&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2026/02/"&gt;February 2026&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2026/01/"&gt;January 2026&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2025/12/"&gt;December 2025&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2025/11/"&gt;November 2025&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2025/10/"&gt;October 2025&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2025/09/"&gt;September 2025&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2025/08/"&gt;August 2025&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2025/07/"&gt;July 2025&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2025/06/"&gt;June 2025&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2025/05/"&gt;May 2025&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2025/04/"&gt;April 2025&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2025/03/"&gt;March 2025&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2025/02/"&gt;February 2025&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2025/01/"&gt;January 2025&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2024/12/"&gt;December 2024&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2023/12/"&gt;December 2023&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2023/11/"&gt;November 2023&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2023/09/"&gt;September 2023&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2021/09/"&gt;September 2021&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2021/08/"&gt;August 2021&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2021/07/"&gt;July 2021&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2021/05/"&gt;May 2021&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2021/04/"&gt;April 2021&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2021/03/"&gt;March 2021&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2021/02/"&gt;February 2021&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2021/01/"&gt;January 2021&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2020/12/"&gt;December 2020&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2020/10/"&gt;October 2020&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2020/09/"&gt;September 2020&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2020/08/"&gt;August 2020&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2020/07/"&gt;July 2020&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2020/06/"&gt;June 2020&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2020/05/"&gt;May 2020&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2020/04/"&gt;April 2020&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2020/03/"&gt;March 2020&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2020/02/"&gt;February 2020&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2020/01/"&gt;January 2020&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2019/12/"&gt;December 2019&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2019/11/"&gt;November 2019&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2019/10/"&gt;October 2019&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2019/09/"&gt;September 2019&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2019/08/"&gt;August 2019&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2019/07/"&gt;July 2019&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2019/06/"&gt;June 2019&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2019/05/"&gt;May 2019&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2019/04/"&gt;April 2019&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="https://codemanship.wordpress.com/2019/03/"&gt;March 2019&lt;/a&gt;&lt;/li&gt;
			&lt;/ul&gt;

			&lt;/nav&gt;&lt;/section&gt;&lt;section id="categories-2" class="widget widget_categories"&gt;&lt;h2 class="widget-title"&gt;Categories&lt;/h2&gt;&lt;nav aria-label="Categories"&gt;
			&lt;ul&gt;
					&lt;li class="cat-item cat-item-670085548"&gt;&lt;a href="https://codemanship.wordpress.com/category/nobacklogs/"&gt;#NoBacklogs&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-2735261"&gt;&lt;a href="https://codemanship.wordpress.com/category/a-i/"&gt;a.i.&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-3562"&gt;&lt;a href="https://codemanship.wordpress.com/category/agility/"&gt;agility&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-594837"&gt;&lt;a href="https://codemanship.wordpress.com/category/apprenticeships/"&gt;apprenticeships&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-2290"&gt;&lt;a href="https://codemanship.wordpress.com/category/architecture/"&gt;architecture&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-330761"&gt;&lt;a href="https://codemanship.wordpress.com/category/capability/"&gt;capability&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-34898103"&gt;&lt;a href="https://codemanship.wordpress.com/category/code-quality/code-comprehension/"&gt;code comprehension&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-52290118"&gt;&lt;a href="https://codemanship.wordpress.com/category/code-craft/"&gt;code craft&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-637195"&gt;&lt;a href="https://codemanship.wordpress.com/category/code-quality/"&gt;code quality&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-214618"&gt;&lt;a href="https://codemanship.wordpress.com/category/concurrency/"&gt;concurrency&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-331916954"&gt;&lt;a href="https://codemanship.wordpress.com/category/context-engineering/"&gt;context engineering&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-32361710"&gt;&lt;a href="https://codemanship.wordpress.com/category/continuous-delivery/"&gt;continuous delivery&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-45037571"&gt;&lt;a href="https://codemanship.wordpress.com/category/continuous-inspection/"&gt;continuous inspection&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-878714"&gt;&lt;a href="https://codemanship.wordpress.com/category/context-engineering/cress/"&gt;cress&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-11379259"&gt;&lt;a href="https://codemanship.wordpress.com/category/designprinciples/"&gt;designprinciples&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-787605825"&gt;&lt;a href="https://codemanship.wordpress.com/category/feedback-loops/feedback-latency/"&gt;feedback latency&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-2190936"&gt;&lt;a href="https://codemanship.wordpress.com/category/feedback-loops/"&gt;feedback loops&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-116486"&gt;&lt;a href="https://codemanship.wordpress.com/category/functional-programming/"&gt;functional programming&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-11870"&gt;&lt;a href="https://codemanship.wordpress.com/category/goals/"&gt;goals&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-8855747"&gt;&lt;a href="https://codemanship.wordpress.com/category/high-integrity/"&gt;high-integrity&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-7885"&gt;&lt;a href="https://codemanship.wordpress.com/category/leadership/"&gt;leadership&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-264"&gt;&lt;a href="https://codemanship.wordpress.com/category/learning/"&gt;learning&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-33253"&gt;&lt;a href="https://codemanship.wordpress.com/category/mentoring/"&gt;mentoring&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-660"&gt;&lt;a href="https://codemanship.wordpress.com/category/people/"&gt;people&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-2704"&gt;&lt;a href="https://codemanship.wordpress.com/category/productivity/"&gt;productivity&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-54712"&gt;&lt;a href="https://codemanship.wordpress.com/category/refactoring/"&gt;refactoring&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-16577"&gt;&lt;a href="https://codemanship.wordpress.com/category/requirements/"&gt;requirements&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-17266"&gt;&lt;a href="https://codemanship.wordpress.com/category/simplicity/"&gt;simplicity&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-2301"&gt;&lt;a href="https://codemanship.wordpress.com/category/software-development/"&gt;software development&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-76604"&gt;&lt;a href="https://codemanship.wordpress.com/category/tdd/"&gt;tdd&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-6228"&gt;&lt;a href="https://codemanship.wordpress.com/category/teams/"&gt;teams&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-12"&gt;&lt;a href="https://codemanship.wordpress.com/category/testing/"&gt;testing&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-943"&gt;&lt;a href="https://codemanship.wordpress.com/category/training/"&gt;training&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-518"&gt;&lt;a href="https://codemanship.wordpress.com/category/trends/"&gt;trends&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-57610"&gt;&lt;a href="https://codemanship.wordpress.com/category/trust/"&gt;trust&lt;/a&gt;
&lt;/li&gt;
	&lt;li class="cat-item cat-item-1"&gt;&lt;a href="https://codemanship.wordpress.com/category/uncategorized/"&gt;Uncategorized&lt;/a&gt;
&lt;/li&gt;
			&lt;/ul&gt;

			&lt;/nav&gt;&lt;/section&gt;	&lt;/aside&gt;&lt;!-- .sidebar .widget-area --&gt;

		&lt;/div&gt;&lt;!-- .site-content --&gt;

		&lt;footer id="colophon" class="site-footer"&gt;
							&lt;nav class="main-navigation" aria-label="Footer Primary Menu"&gt;
					&lt;div class="menu-codemanship-container"&gt;&lt;ul id="menu-codemanship-1" class="primary-menu"&gt;&lt;li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-home menu-item-65"&gt;&lt;a href="https://codemanship.wordpress.com"&gt;Home&lt;/a&gt;&lt;/li&gt;
&lt;li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-66"&gt;&lt;a href="https://codemanship.wordpress.com/about/"&gt;About&lt;/a&gt;&lt;/li&gt;
&lt;li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-67"&gt;&lt;a href="https://www.codemanship.co.uk"&gt;Training&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;				&lt;/nav&gt;&lt;!-- .main-navigation --&gt;
			
			
			&lt;div class="site-info"&gt;
								&lt;span class="site-title"&gt;&lt;a href="https://codemanship.wordpress.com/" rel="home"&gt;Codemanship's Blog&lt;/a&gt;&lt;/span&gt;
								&lt;a href="https://wordpress.com/?ref=footer_blog" rel="nofollow"&gt;Blog at WordPress.com.&lt;/a&gt;
			&lt;/div&gt;&lt;!-- .site-info --&gt;
		&lt;/footer&gt;&lt;!-- .site-footer --&gt;
	&lt;/div&gt;&lt;!-- .site-inner --&gt;
&lt;/div&gt;&lt;!-- .site --&gt;

&lt;!--  --&gt;
&lt;script type="speculationrules"&gt;
{"prefetch":[{"source":"document","where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":["/wp-*.php","/wp-admin/*","/files/*","/wp-content/*","/wp-content/plugins/*","/wp-content/themes/pub/twentysixteen/*","/*\\?(.+)"]}},{"not":{"selector_matches":"a[rel~=\"nofollow\"]"}},{"not":{"selector_matches":".no-prefetch, .no-prefetch a"}}]},"eagerness":"conservative"}]}
&lt;/script&gt;
&lt;script id="grofiles-cards-js" src="//0.gravatar.com/js/hovercards/hovercards.min.js?ver=202633d1133547e8a1f2801609f51cef12522abe7f6647d869e9fbc974fe356a37e613"&gt;&lt;/script&gt;
&lt;script id="wpgroho-js-extra"&gt;
var WPGroHo = {"my_hash":""};
//# sourceURL=wpgroho-js-extra
&lt;/script&gt;
&lt;script crossorigin="anonymous" type="text/javascript" src="/wp-content/mu-plugins/gravatar-hovercards/wpgroho.js?m=1610363240i"&gt;&lt;/script&gt;

	&lt;script&gt;
		// Initialize and attach hovercards to all gravatars
		( function() {
			function init() {
				if ( typeof Gravatar === 'undefined' ) {
					return;
				}

				if ( typeof Gravatar.init !== 'function' ) {
					return;
				}

				Gravatar.profile_cb = function ( hash, id ) {
					WPGroHo.syncProfileData( hash, id );
				};

				Gravatar.my_hash = WPGroHo.my_hash;
				Gravatar.init(
					'body',
					'#wp-admin-bar-my-account',
					{
						i18n: {
							'Edit your profile →': 'Edit your profile →',
							'View profile →': 'View profile →',
							'Contact': 'Contact',
							'Send money': 'Send money',
							'Sorry, we are unable to load this Gravatar profile.': 'Sorry, we are unable to load this Gravatar profile.',
							'Gravatar not found.': 'Gravatar not found.',
							'Too Many Requests.': 'Too Many Requests.',
							'Internal Server Error.': 'Internal Server Error.',
							'Is this you?': 'Is this you?',
							'Claim your free profile.': 'Claim your free profile.',
							'Email': 'Email',
							'Home Phone': 'Home Phone',
							'Work Phone': 'Work Phone',
							'Cell Phone': 'Cell Phone',
							'Contact Form': 'Contact Form',
							'Calendar': 'Calendar',
						},
					}
				);
			}

			if ( document.readyState !== 'loading' ) {
				init();
			} else {
				document.addEventListener( 'DOMContentLoaded', init );
			}
		} )();
	&lt;/script&gt;

		&lt;div style="display:none"&gt;
	&lt;div class="grofile-hash-map-f0e9e91d8664a0e62c1d48d044b8825d"&gt;
	&lt;/div&gt;
	&lt;/div&gt;
		&lt;div id="actionbar" dir="ltr" class="actnbr-pub-twentysixteen actnbr-has-follow actnbr-has-actions"&gt;
		&lt;ul&gt;
								&lt;li class="actnbr-btn actnbr-hidden"&gt;
						&lt;a class="actnbr-action actnbr-actn-comment" href="https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/#respond"&gt;
							&lt;svg class="gridicon gridicons-comment" height="20" width="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;g&gt;&lt;path d="M12 16l-5 5v-5H5c-1.1 0-2-.9-2-2V5c0-1.1.9-2 2-2h14c1.1 0 2 .9 2 2v9c0 1.1-.9 2-2 2h-7z"&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;							&lt;span&gt;Comment						&lt;/span&gt;
						&lt;/a&gt;
					&lt;/li&gt;
									&lt;li class="actnbr-btn actnbr-hidden"&gt;
						&lt;a class="actnbr-action actnbr-actn-reblog" href=""&gt;
							&lt;svg class="gridicon gridicons-reblog" height="20" width="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;g&gt;&lt;path d="M22.086 9.914L20 7.828V18c0 1.105-.895 2-2 2h-7v-2h7V7.828l-2.086 2.086L14.5 8.5 19 4l4.5 4.5-1.414 1.414zM6 16.172V6h7V4H6c-1.105 0-2 .895-2 2v10.172l-2.086-2.086L.5 15.5 5 20l4.5-4.5-1.414-1.414L6 16.172z"&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;span&gt;Reblog&lt;/span&gt;
						&lt;/a&gt;
					&lt;/li&gt;
									&lt;li class="actnbr-btn actnbr-hidden"&gt;
								&lt;a class="actnbr-action actnbr-actn-follow " href=""&gt;
			&lt;svg class="gridicon" height="20" width="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"&gt;&lt;path clip-rule="evenodd" d="m4 4.5h12v6.5h1.5v-6.5-1.5h-1.5-12-1.5v1.5 10.5c0 1.1046.89543 2 2 2h7v-1.5h-7c-.27614 0-.5-.2239-.5-.5zm10.5 2h-9v1.5h9zm-5 3h-4v1.5h4zm3.5 1.5h-1v1h1zm-1-1.5h-1.5v1.5 1 1.5h1.5 1 1.5v-1.5-1-1.5h-1.5zm-2.5 2.5h-4v1.5h4zm6.5 1.25h1.5v2.25h2.25v1.5h-2.25v2.25h-1.5v-2.25h-2.25v-1.5h2.25z" fill-rule="evenodd"&gt;&lt;/path&gt;&lt;/svg&gt;
			&lt;span&gt;Subscribe&lt;/span&gt;
		&lt;/a&gt;
		&lt;a class="actnbr-action actnbr-actn-following  no-display" href=""&gt;
			&lt;svg class="gridicon" height="20" width="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"&gt;&lt;path fill-rule="evenodd" clip-rule="evenodd" d="M16 4.5H4V15C4 15.2761 4.22386 15.5 4.5 15.5H11.5V17H4.5C3.39543 17 2.5 16.1046 2.5 15V3H17.5V12.5H16V4.5ZM5.5 6.5H14.5V8H5.5V6.5ZM5.5 9.5H9.5V11H5.5V9.5ZM12 11H13V12H12V11ZM10.5 9.5H14.5V13.5H10.5V9.5ZM5.5 12H9.5V13.5H5.5V12Z" fill="#008A20"&gt;&lt;/path&gt;&lt;path d="M19.5303 15.0303L15.5 19.0605L12.9697 16.5303L14.0303 15.4697L15.5 16.9395L18.4697 13.9697L19.5303 15.0303Z" fill="#008A20"&gt;&lt;/path&gt;&lt;/svg&gt;
			&lt;span&gt;Subscribed&lt;/span&gt;
		&lt;/a&gt;
							&lt;div class="actnbr-popover tip tip-top-left actnbr-notice" id="follow-bubble"&gt;
							&lt;div class="tip-arrow"&gt;&lt;/div&gt;
							&lt;div class="tip-inner actnbr-follow-bubble"&gt;
															&lt;ul&gt;
											&lt;li class="actnbr-sitename"&gt;
			&lt;a href="https://codemanship.wordpress.com"&gt;
				&lt;img loading="lazy" alt="" src="https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=50" srcset="https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=50 1x, https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=75 1.5x, https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=100 2x, https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=150 3x, https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=200 4x" class="avatar avatar-50" height="50" width="50"&gt;				Codemanship's Blog			&lt;/a&gt;
		&lt;/li&gt;
										&lt;div class="actnbr-message no-display"&gt;&lt;/div&gt;
									&lt;form method="post" action="https://subscribe.wordpress.com" accept-charset="utf-8" style="display: none;"&gt;
																						&lt;div class="actnbr-follow-count"&gt;Join 126 other subscribers&lt;/div&gt;
																					&lt;div&gt;
										&lt;input type="email" name="email" placeholder="Enter your email address" class="actnbr-email-field" aria-label="Enter your email address"&gt;
										&lt;/div&gt;
										&lt;input type="hidden" name="action" value="subscribe"&gt;
										&lt;input type="hidden" name="blog_id" value="15031761"&gt;
										&lt;input type="hidden" name="source" value="https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/"&gt;
										&lt;input type="hidden" name="sub-type" value="actionbar-follow"&gt;
										&lt;input type="hidden" id="_wpnonce" name="_wpnonce" value="b4e41ffb1a"&gt;										&lt;div class="actnbr-button-wrap"&gt;
											&lt;button type="submit" value="Sign me up"&gt;
												Sign me up											&lt;/button&gt;
										&lt;/div&gt;
									&lt;/form&gt;
									&lt;li class="actnbr-login-nudge"&gt;
										&lt;div&gt;
											Already have a WordPress.com account? &lt;a href="https://wordpress.com/log-in?redirect_to=https%3A%2F%2Fcodemanship.wordpress.com%2F2026%2F08%2F12%2Fai-software-development-what-does-the-data-say%2F&amp;amp;signup_flow=account"&gt;Log in now.&lt;/a&gt;										&lt;/div&gt;
									&lt;/li&gt;
								&lt;/ul&gt;
															&lt;/div&gt;
						&lt;/div&gt;
					&lt;/li&gt;
									&lt;li class="actnbr-btn actnbr-hidden no-display" onclick="javascript:__tcfapi( 'showUi' );"&gt;
						&lt;a class="actnbr-action actnbr-actn-privacy" href="#"&gt;
							&lt;svg class="gridicon gridicons-info-outline" height="20" width="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;g&gt;&lt;path d="M13 9h-2V7h2v2zm0 2h-2v6h2v-6zm-1-7c-4.41 0-8 3.59-8 8s3.59 8 8 8 8-3.59 8-8-3.59-8-8-8m0-2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2z"&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;							&lt;span&gt;Privacy						&lt;/span&gt;
						&lt;/a&gt;
					&lt;/li&gt;
							&lt;li class="actnbr-ellipsis actnbr-hidden"&gt;
				&lt;svg class="gridicon gridicons-ellipsis" height="24" width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;g&gt;&lt;path d="M7 12c0 1.104-.896 2-2 2s-2-.896-2-2 .896-2 2-2 2 .896 2 2zm12-2c-1.104 0-2 .896-2 2s.896 2 2 2 2-.896 2-2-.896-2-2-2zm-7 0c-1.104 0-2 .896-2 2s.896 2 2 2 2-.896 2-2-.896-2-2-2z"&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;				&lt;div class="actnbr-popover tip tip-top-left actnbr-more"&gt;
					&lt;div class="tip-arrow"&gt;&lt;/div&gt;
					&lt;div class="tip-inner"&gt;
						&lt;ul&gt;
								&lt;li class="actnbr-sitename"&gt;
			&lt;a href="https://codemanship.wordpress.com"&gt;
				&lt;img loading="lazy" alt="" src="https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=50" srcset="https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=50 1x, https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=75 1.5x, https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=100 2x, https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=150 3x, https://codemanship.wordpress.com/wp-content/uploads/2017/08/cropped-codemanship3_twitter_avatar.jpg?w=200 4x" class="avatar avatar-50" height="50" width="50"&gt;				Codemanship's Blog			&lt;/a&gt;
		&lt;/li&gt;
								&lt;li class="actnbr-folded-follow"&gt;
										&lt;a class="actnbr-action actnbr-actn-follow " href=""&gt;
			&lt;svg class="gridicon" height="20" width="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"&gt;&lt;path clip-rule="evenodd" d="m4 4.5h12v6.5h1.5v-6.5-1.5h-1.5-12-1.5v1.5 10.5c0 1.1046.89543 2 2 2h7v-1.5h-7c-.27614 0-.5-.2239-.5-.5zm10.5 2h-9v1.5h9zm-5 3h-4v1.5h4zm3.5 1.5h-1v1h1zm-1-1.5h-1.5v1.5 1 1.5h1.5 1 1.5v-1.5-1-1.5h-1.5zm-2.5 2.5h-4v1.5h4zm6.5 1.25h1.5v2.25h2.25v1.5h-2.25v2.25h-1.5v-2.25h-2.25v-1.5h2.25z" fill-rule="evenodd"&gt;&lt;/path&gt;&lt;/svg&gt;
			&lt;span&gt;Subscribe&lt;/span&gt;
		&lt;/a&gt;
		&lt;a class="actnbr-action actnbr-actn-following  no-display" href=""&gt;
			&lt;svg class="gridicon" height="20" width="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"&gt;&lt;path fill-rule="evenodd" clip-rule="evenodd" d="M16 4.5H4V15C4 15.2761 4.22386 15.5 4.5 15.5H11.5V17H4.5C3.39543 17 2.5 16.1046 2.5 15V3H17.5V12.5H16V4.5ZM5.5 6.5H14.5V8H5.5V6.5ZM5.5 9.5H9.5V11H5.5V9.5ZM12 11H13V12H12V11ZM10.5 9.5H14.5V13.5H10.5V9.5ZM5.5 12H9.5V13.5H5.5V12Z" fill="#008A20"&gt;&lt;/path&gt;&lt;path d="M19.5303 15.0303L15.5 19.0605L12.9697 16.5303L14.0303 15.4697L15.5 16.9395L18.4697 13.9697L19.5303 15.0303Z" fill="#008A20"&gt;&lt;/path&gt;&lt;/svg&gt;
			&lt;span&gt;Subscribed&lt;/span&gt;
		&lt;/a&gt;
								&lt;/li&gt;
														&lt;li class="actnbr-signup"&gt;&lt;a href="https://wordpress.com/start/"&gt;Sign up&lt;/a&gt;&lt;/li&gt;
							&lt;li class="actnbr-login"&gt;&lt;a href="https://wordpress.com/log-in?redirect_to=https%3A%2F%2Fcodemanship.wordpress.com%2F2026%2F08%2F12%2Fai-software-development-what-does-the-data-say%2F&amp;amp;signup_flow=account"&gt;Log in&lt;/a&gt;&lt;/li&gt;
																&lt;li class="actnbr-shortlink"&gt;
										&lt;a href="https://wp.me/p114rL-1e7"&gt;
											&lt;span class="actnbr-shortlink__text"&gt;Copy shortlink&lt;/span&gt;
											&lt;span class="actnbr-shortlink__icon"&gt;&lt;svg class="gridicon gridicons-checkmark" height="16" width="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;g&gt;&lt;path d="M9 19.414l-6.707-6.707 1.414-1.414L9 16.586 20.293 5.293l1.414 1.414"&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/span&gt;
										&lt;/a&gt;
									&lt;/li&gt;
																&lt;li class="flb-report"&gt;
									&lt;a href="https://wordpress.com/abuse/?report_url=https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/" target="_blank" rel="noopener noreferrer"&gt;
										Report this content									&lt;/a&gt;
								&lt;/li&gt;
															&lt;li class="actnbr-reader"&gt;
									&lt;a href="https://wordpress.com/reader/blogs/15031761/posts/4719"&gt;
										View post in Reader									&lt;/a&gt;
								&lt;/li&gt;
															&lt;li class="actnbr-subs"&gt;
									&lt;a href="https://subscribe.wordpress.com/"&gt;Manage subscriptions&lt;/a&gt;
								&lt;/li&gt;
																&lt;li class="actnbr-fold"&gt;&lt;a href=""&gt;Collapse this bar&lt;/a&gt;&lt;/li&gt;
														&lt;/ul&gt;
					&lt;/div&gt;
				&lt;/div&gt;
			&lt;/li&gt;
		&lt;/ul&gt;
	&lt;/div&gt;
	
&lt;script&gt;
window.addEventListener( "DOMContentLoaded", function( event ) {
	var link = document.createElement( "link" );
	link.href = "/wp-content/mu-plugins/actionbar/actionbar.css?v=20250116";
	link.type = "text/css";
	link.rel = "stylesheet";
	document.head.appendChild( link );

	var script = document.createElement( "script" );
	script.src = "/wp-content/mu-plugins/actionbar/actionbar.js?v=20250204";
	document.body.appendChild( script );
} );
&lt;/script&gt;

	
	&lt;script type="text/javascript"&gt;
		window.WPCOM_sharing_counts = {"https://codemanship.wordpress.com/2026/08/12/ai-software-development-what-does-the-data-say/":4719};
	&lt;/script&gt;
				
	&lt;script type="text/javascript"&gt;
		(function () {
			var wpcom_reblog = {
				source: 'toolbar',

				toggle_reblog_box_flair: function (obj_id, post_id) {

					// Go to site selector. This will redirect to their blog if they only have one.
					const postEndpoint = `https://wordpress.com/post`;

					// Ideally we would use the permalink here, but fortunately this will be replaced with the 
					// post permalink in the editor.
					const originalURL = `${ document.location.href }?page_id=${ post_id }`; 
					
					const url =
						postEndpoint +
						'?url=' +
						encodeURIComponent( originalURL ) +
						'&amp;is_post_share=true' +
						'&amp;v=5';

					const redirect = function () {
						if (
							! window.open( url, '_blank' )
						) {
							location.href = url;
						}
					};

					if ( /Firefox/.test( navigator.userAgent ) ) {
						setTimeout( redirect, 0 );
					} else {
						redirect();
					}
				},
			};

			window.wpcom_reblog = wpcom_reblog;
		})();
	&lt;/script&gt;
&lt;script crossorigin="anonymous" type="text/javascript" src="/wp-content/mu-plugins/likes/queuehandler.js?m=1741961244i"&gt;&lt;/script&gt;
&lt;script id="wp-hooks-js" src="/wp-content/plugins/gutenberg-core/v23.7.1/build/scripts/hooks/index.min.js?m=1786118922i&amp;amp;ver=7496969728ca0f95732d"&gt;&lt;/script&gt;
&lt;script id="wp-i18n-js" src="/wp-content/plugins/gutenberg-core/v23.7.1/build/scripts/i18n/index.min.js?m=1786118922i&amp;amp;ver=125448662852c5e18937"&gt;&lt;/script&gt;
&lt;script id="wp-i18n-js-after"&gt;
wp.i18n.setLocaleData( { 'text direction\u0004ltr': [ 'ltr' ] } );
//# sourceURL=wp-i18n-js-after
&lt;/script&gt;
&lt;script id="verbum-settings-js-before"&gt;
window.VerbumComments = {"Log in or provide your name and email to leave a reply.":"Log in or provide your name and email to leave a reply.","Log in or provide your name and email to leave a comment.":"Log in or provide your name and email to leave a comment.","Receive web and mobile notifications for posts on this site.":"Receive web and mobile notifications for posts on this site.","Name":"Name","Email (address never made public)":"Email (address never made public)","Website (optional)":"Website (optional)","Leave a reply. (log in optional)":"Leave a reply. (log in optional)","Leave a comment. (log in optional)":"Leave a comment. (log in optional)","Log in to leave a reply.":"Log in to leave a reply.","Log in to leave a comment.":"Log in to leave a comment.","Logged in via %s":"Logged in via %s","Log out":"Log out","Your browser is blocking the cookies needed to log in and comment here. Allow cookies in your privacy settings, then reload the page.":"Your browser is blocking the cookies needed to log in and comment here. Allow cookies in your privacy settings, then reload the page.","Email":"Email","(Address never made public)":"(Address never made public)","Instantly":"Instantly","Daily":"Daily","Reply":"Reply","Comment":"Comment","WordPress":"WordPress","Weekly":"Weekly","Notify me of new posts":"Notify me of new posts","Email me new posts":"Email me new posts","Email me new comments":"Email me new comments","Cancel":"Cancel","Write a comment...":"Write a comment...","Write a reply...":"Write a reply...","Website":"Website","Optional":"Optional","We'll keep you in the loop!":"We'll keep you in the loop!","Loading your comment...":"Loading your comment...","Discover more from":"Discover more from Codemanship's Blog","Subscribe now to keep reading and get access to the full archive.":"Subscribe now to keep reading and get access to the full archive.","Continue reading":"Continue reading","Never miss a beat!":"Never miss a beat!","Interested in getting blog post updates? Simply click the button below to stay in the loop!":"Interested in getting blog post updates? Simply click the button below to stay in the loop!","Enter your email address":"Enter your email address","Subscribe":"Subscribe","Comment sent successfully":"Comment sent successfully","Save my name, email, and website in this browser for the next time I comment.":"Save my name, email, and website in this browser for the next time I comment.","hovercardi18n":{"Edit your profile \u2192":"Edit your profile \u2192","View profile \u2192":"View profile \u2192","Contact":"Contact","Send money":"Send money","Gravatar not found.":"Gravatar not found.","This profile is private.":"This profile is private.","Too Many Requests.":"Too Many Requests.","Internal Server Error.":"Internal Server Error.","Sorry, we are unable to load this Gravatar profile.":"Sorry, we are unable to load this Gravatar profile."},"siteId":15031761,"postId":4719,"mustLogIn":false,"requireNameEmail":true,"commentRegistration":false,"connectURL":"https://codemanship.wordpress.com/public.api/connect/?action=request\u0026from_comments=yes","logoutURL":"https://codemanship.wordpress.com/wp-login.php?action=logout\u0026_wpnonce=530c037544","homeURL":"https://codemanship.wordpress.com/","subscribeToBlog":true,"subscribeToComment":true,"isJetpackCommentsLoggedIn":false,"jetpackUsername":"","jetpackUserId":0,"jetpackSignature":"","jetpackAvatar":"https://0.gravatar.com/avatar/?s=96\u0026amp;d=identicon\u0026amp;r=G","enableBlocks":true,"enableSubscriptionModal":true,"currentLocale":"en","isJetpackComments":false,"allowedBlocks":["core/paragraph","core/list","core/code","core/list-item","core/quote","core/image","core/embed","core/quote","core/code"],"embedNonce":"43382505bf","verbumBundleUrl":"/wp-content/mu-plugins/jetpack-mu-wpcom-plugin/moon/jetpack_vendor/automattic/jetpack-mu-wpcom/src/features/verbum-comments/dist/index.js","isRTL":false,"vbeCacheBuster":1771405207,"iframeUniqueId":0,"colorScheme":false}
//# sourceURL=verbum-settings-js-before
&lt;/script&gt;
&lt;script data-wp-strategy="defer" defer="" id="verbum-dynamic-loader-js" src="/wp-content/mu-plugins/jetpack-mu-wpcom-plugin/moon/jetpack_vendor/automattic/jetpack-mu-wpcom/src/build/verbum-comments/assets/dynamic-loader.js?m=1772518873i&amp;amp;minify=false&amp;amp;ver=8b5296f9195db92c42e4"&gt;&lt;/script&gt;
&lt;script id="twentysixteen-script-js-extra"&gt;
var screenReaderText = {"expand":"expand child menu","collapse":"collapse child menu"};
//# sourceURL=twentysixteen-script-js-extra
&lt;/script&gt;
&lt;script id="jetpack-script-data-js-before"&gt;
window.JetpackScriptData = {"site":{"host":"wpcom","is_wpcom_platform":true}};
//# sourceURL=jetpack-script-data-js-before
&lt;/script&gt;
&lt;script id="jetpack-blocks-assets-base-url-js-before"&gt;
var Jetpack_Block_Assets_Base_Url="https://s0.wp.com/wp-content/mu-plugins/jetpack-plugin/moon/_inc/blocks/";
//# sourceURL=jetpack-blocks-assets-base-url-js-before
&lt;/script&gt;
&lt;script crossorigin="anonymous" type="text/javascript" src="/_static/??-eJyNj9FOwzAMRX8I16BpQB8QnzJliQtuEyeqnXX7eyzE+sAD4vX4Hh0ZtwaxipEY2icVUmz9jLY5uClfjUhwVtSFG2SWBaYau8LE12HWB3SdJeaeXPRZrKW4CSu1fBsKy330n8bUJRpX0d9S6dBy/2DxBlkLcQFHW/PazwFLrXI/ni4kqa4YutUSzDjuWlAlUzx3zmmHGlduBilY+KMcFtZCBofhEU/+8w6m9Xuc3H0vb08vr8fxMB7H5/kLh6CChg=="&gt;&lt;/script&gt;
&lt;script id="wp-dom-ready-js" src="/wp-content/plugins/gutenberg-core/v23.7.1/build/scripts/dom-ready/index.min.js?m=1786118922i&amp;amp;ver=a06281ae5cf5500e9317"&gt;&lt;/script&gt;
&lt;script crossorigin="anonymous" type="text/javascript" src="/wp-includes/js/dist/vendor/wp-polyfill.min.js?m=1786440387i"&gt;&lt;/script&gt;
&lt;script data-wp-strategy="defer" defer="" id="jetpack-block-subscriptions-js" src="/wp-content/mu-plugins/jetpack-plugin/moon/_inc/blocks/subscriptions/view.js?m=1782389597i&amp;amp;minify=false&amp;amp;ver=16.2-a.0"&gt;&lt;/script&gt;
&lt;script id="sharing-js-js-extra"&gt;
var sharing_js_options = {"lang":"en","counts":"1","is_stats_active":"1"};
//# sourceURL=sharing-js-js-extra
&lt;/script&gt;
&lt;script crossorigin="anonymous" type="text/javascript" src="/wp-content/mu-plugins/jetpack-plugin/moon/_inc/build/sharedaddy/sharing.min.js?m=1755006225i"&gt;&lt;/script&gt;
&lt;script id="sharing-js-js-after"&gt;
var windowOpen;
			( function () {
				function matches( el, sel ) {
					return !! (
						el.matches &amp;&amp; el.matches( sel ) ||
						el.msMatchesSelector &amp;&amp; el.msMatchesSelector( sel )
					);
				}

				document.body.addEventListener( 'click', function ( event ) {
					if ( ! event.target ) {
						return;
					}

					var el;
					if ( matches( event.target, 'a.share-twitter' ) ) {
						el = event.target;
					} else if ( event.target.parentNode &amp;&amp; matches( event.target.parentNode, 'a.share-twitter' ) ) {
						el = event.target.parentNode;
					}

					if ( el ) {
						event.preventDefault();

						// If there's another sharing window open, close it.
						if ( typeof windowOpen !== 'undefined' ) {
							windowOpen.close();
						}
						windowOpen = window.open( el.getAttribute( 'href' ), 'wpcomtwitter', 'menubar=1,resizable=1,width=600,height=350' );
						return false;
					}
				} );
			} )();
var windowOpen;
			( function () {
				function matches( el, sel ) {
					return !! (
						el.matches &amp;&amp; el.matches( sel ) ||
						el.msMatchesSelector &amp;&amp; el.msMatchesSelector( sel )
					);
				}

				document.body.addEventListener( 'click', function ( event ) {
					if ( ! event.target ) {
						return;
					}

					var el;
					if ( matches( event.target, 'a.share-facebook' ) ) {
						el = event.target;
					} else if ( event.target.parentNode &amp;&amp; matches( event.target.parentNode, 'a.share-facebook' ) ) {
						el = event.target.parentNode;
					}

					if ( el ) {
						event.preventDefault();

						// If there's another sharing window open, close it.
						if ( typeof windowOpen !== 'undefined' ) {
							windowOpen.close();
						}
						windowOpen = window.open( el.getAttribute( 'href' ), 'wpcomfacebook', 'menubar=1,resizable=1,width=600,height=400' );
						return false;
					}
				} );
			} )();
var windowOpen;
			( function () {
				function matches( el, sel ) {
					return !! (
						el.matches &amp;&amp; el.matches( sel ) ||
						el.msMatchesSelector &amp;&amp; el.msMatchesSelector( sel )
					);
				}

				document.body.addEventListener( 'click', function ( event ) {
					if ( ! event.target ) {
						return;
					}

					var el;
					if ( matches( event.target, 'a.share-linkedin' ) ) {
						el = event.target;
					} else if ( event.target.parentNode &amp;&amp; matches( event.target.parentNode, 'a.share-linkedin' ) ) {
						el = event.target.parentNode;
					}

					if ( el ) {
						event.preventDefault();

						// If there's another sharing window open, close it.
						if ( typeof windowOpen !== 'undefined' ) {
							windowOpen.close();
						}
						windowOpen = window.open( el.getAttribute( 'href' ), 'wpcomlinkedin', 'menubar=1,resizable=1,width=580,height=450' );
						return false;
					}
				} );
			} )();
//# sourceURL=sharing-js-js-after
&lt;/script&gt;
&lt;script id="wp-emoji-settings" type="application/json"&gt;
{"baseUrl":"https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/","ext":".png","svgUrl":"https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/","svgExt":".svg","source":{"concatemoji":"/wp-includes/js/wp-emoji-release.min.js?m=1783090810i&amp;ver=7.1-alpha-62686"}}
&lt;/script&gt;
&lt;script type="module"&gt;
/*! This file is auto-generated */
var e="script#wp-emoji-settings",t=document.querySelector(e);if(!(t instanceof HTMLScriptElement))throw new Error("Element missing: "+e);const r=JSON.parse(t.text),s=(window._wpemojiSettings=r,"wpEmojiSettingsSupports"),o=["flag","emoji"];function i(e){try{var t={supportTests:e,timestamp:(new Date).valueOf()};sessionStorage.setItem(s,JSON.stringify(t))}catch(e){}}function c(e,t,n){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);t=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(n,0,0);const r=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);return t.every((e,t)=&gt;e===r[t])}function p(e,t){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);var n=e.getImageData(16,16,1,1);for(let e=0;e&lt;n.data.length;e++)if(0!==n.data[e])return!1;return!0}function u(e,t,n,r){switch(t){case"flag":return n(e,"\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f","\ud83c\udff3\ufe0f\u200b\u26a7\ufe0f")?!1:!n(e,"\ud83c\udde8\ud83c\uddf6","\ud83c\udde8\u200b\ud83c\uddf6")&amp;&amp;!n(e,"\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f","\ud83c\udff4\u200b\udb40\udc67\u200b\udb40\udc62\u200b\udb40\udc65\u200b\udb40\udc6e\u200b\udb40\udc67\u200b\udb40\udc7f");case"emoji":return!r(e,"\ud83e\u1fac8")}return!1}function f(e,t,n,r){let a;const s=(a="undefined"!=typeof WorkerGlobalScope&amp;&amp;self instanceof WorkerGlobalScope?new OffscreenCanvas(300,150):document.createElement("canvas")).getContext("2d",{willReadFrequently:!0}),o=(s.textBaseline="top",s.font="600 32px Arial",{});return e.forEach(e=&gt;{o[e]=t(s,e,n,r)}),o}function a(e){var t=document.createElement("script");t.src=e,t.defer=!0,document.head.appendChild(t)}r.supports={everything:!0,everythingExceptFlag:!0},new Promise(t=&gt;{let n=function(){try{var e=JSON.parse(sessionStorage.getItem(s));if("object"==typeof e&amp;&amp;"number"==typeof e.timestamp&amp;&amp;(new Date).valueOf()&lt;e.timestamp+604800&amp;&amp;"object"==typeof e.supportTests)return e.supportTests}catch(e){}return null}();if(!n){if("undefined"!=typeof Worker&amp;&amp;"undefined"!=typeof OffscreenCanvas&amp;&amp;"undefined"!=typeof URL&amp;&amp;URL.createObjectURL&amp;&amp;"undefined"!=typeof Blob)try{var e="postMessage("+f.toString()+"("+[JSON.stringify(o),u.toString(),c.toString(),p.toString()].join(",")+"));",r=new Blob([e],{type:"text/javascript"});const a=new Worker(URL.createObjectURL(r),{name:"wpTestEmojiSupports"});return void(a.onmessage=e=&gt;{i(n=e.data),a.terminate(),t(n)})}catch(e){}i(n=f(o,u,c,p))}t(n)}).then(e=&gt;{for(const n in e)r.supports[n]=e[n],r.supports.everything=r.supports.everything&amp;&amp;r.supports[n],"flag"!==n&amp;&amp;(r.supports.everythingExceptFlag=r.supports.everythingExceptFlag&amp;&amp;r.supports[n]);var t;r.supports.everythingExceptFlag=r.supports.everythingExceptFlag&amp;&amp;!r.supports.flag,r.supports.everything||((t=r.source||{}).concatemoji?a(t.concatemoji):t.wpemoji&amp;&amp;t.twemoji&amp;&amp;(a(t.twemoji),a(t.wpemoji)))});
//# sourceURL=/wp-includes/js/wp-emoji-loader.min.js
&lt;/script&gt;
	&lt;iframe src="https://widgets.wp.com/likes/master.html?ver=20260812#ver=20260812" scrolling="no" id="likes-master" name="likes-master" style="display:none;"&gt;&lt;/iframe&gt;
	&lt;div id="likes-other-gravatars" role="dialog" aria-hidden="true" tabindex="-1"&gt;&lt;div class="likes-text"&gt;&lt;span&gt;%d&lt;/span&gt;&lt;/div&gt;&lt;ul class="wpl-avatars sd-like-gravatars"&gt;&lt;/ul&gt;&lt;/div&gt;
	&lt;script src="//stats.wp.com/w.js?69" defer=""&gt;&lt;/script&gt; &lt;script type="text/javascript"&gt;
_tkq = window._tkq || [];
_stq = window._stq || [];
_tkq.push(['storeContext', {'blog_id':'15031761','blog_tz':'1','user_lang':'en','blog_lang':'en','user_id':'0'}]);
		// Prevent sending pageview tracking from WP-Admin pages.
		_stq.push(['view', {'blog':'15031761','v':'wpcom','tz':'1','user_id':'0','post':'4719','subd':'codemanship'}]);
		_stq.push(['extra', {'crypt':'UE5tW3cvZGQ/JUs1UEo9WXFZM2k9fDlzc2JWP3hETzFRNDFNOT11fjNqYVJdS3BRJnMxSTNjVkF4Z3QwRS95dHdsZFNVZS40fF1iOS4zWGNiSi8vU1FpWnFQMTloOC9uY1Y1aiZDX3wwW01zVHR1WzFhQ19sPVFNNldtUVRPJUhPaF1EVzNLSiwwMSUsT1NdbjdZTSx+cWp4emg/LzlNdy1XSC1FU2FRP2IyZ0ptVEZLQkksaGViZUpPeTl4UDRCcFZJVE9K'}]);
_stq.push([ 'clickTrackerInit', '15031761', '4719' ]);
&lt;/script&gt;
&lt;noscript&gt;&lt;img src="https://pixel.wp.com/b.gif?v=noscript" style="height:1px;width:1px;overflow:hidden;position:absolute;bottom:1px;" alt="" /&gt;&lt;/noscript&gt;
&lt;meta id="bilmur" property="bilmur:data" content="" data-provider="wordpress.com" data-service="simple" data-site-tz="Europe/London" data-custom-props="{&amp;quot;enq_wp-polyfill&amp;quot;:&amp;quot;1&amp;quot;,&amp;quot;logged_in&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;wptheme&amp;quot;:&amp;quot;pub\/twentysixteen&amp;quot;,&amp;quot;wptheme_is_block&amp;quot;:&amp;quot;0&amp;quot;}"&gt;
		&lt;script defer="" src="/wp-content/js/bilmur.min.js?i=17&amp;amp;m=202633"&gt;&lt;/script&gt; 	
&lt;script&gt;
(function() {
	'use strict';

	const fetches = {};
	const promises = {};
	const urls = {
		'verbum': '/wp-content/mu-plugins/jetpack-mu-wpcom-plugin/moon/jetpack_vendor/automattic/jetpack-mu-wpcom/src/build/verbum-comments/verbum-comments.js?m=1785895867i&amp;minify=false&amp;ver=ecf5cfe8827907d3ea28'
	};
	const loaders = {
		'verbum': () =&gt; {
			fetchExternalScript('verbum');
			promises['verbum'] = promises['verbum'] || loadWPScript('verbum');
			return promises['verbum'];
		},
		
	};
	const scriptExtras = {
		
	};

	window.WP_Enqueue_Dynamic_Script = {
		loadScript: (handle) =&gt; {
			if (!loaders[handle]) {
				console.error('WP_Enqueue_Dynamic_Script: unregistered script `' + handle + '`.');
			}
			return loaders[handle]();
		}
	};

	function fetchExternalScript(handle) {
		if (!urls[handle]) {
			return Promise.resolve();
		}

		fetches[handle] = fetches[handle] || fetch(urls[handle], { mode: 'no-cors' });
		return fetches[handle];
	}

	function runExtraScript(handle, type, index) {
		const id = 'wp-enqueue-dynamic-script:' + handle + ':' + type + ':' + (index + 1);
		const template = document.getElementById(id);
		if (!template) {
			return Promise.reject();
		}

		const script = document.createElement( 'script' );
		script.innerHTML = template.innerHTML;
		document.body.appendChild( script );
		return Promise.resolve();
	}

	function loadExternalScript(handle) {
		if (!urls[handle]) {
			return Promise.resolve();
		}

		return fetches[handle].then(() =&gt; {
			return new Promise((resolve, reject) =&gt; {
				const script = document.createElement('script');
				script.onload = () =&gt; resolve();
				script.onerror = (e) =&gt; reject(e);
				script.src = urls[handle];
				document.body.appendChild(script);
			});
		});
	}

	function loadExtra(handle, pos) {
		const count = (scriptExtras[handle] &amp;&amp; scriptExtras[handle][pos]) || 0;
		let promise = Promise.resolve();

		for (let i = 0; i &lt; count; i++) {
			promise = promise.then(() =&gt; runExtraScript(handle, pos, i));
		}

		return promise;
	}

	function loadWPScript(handle) {
		// Core loads scripts in this order. See: https://github.com/WordPress/WordPress/blob/a59eb9d39c4fcba834b70c9e8dfd64feeec10ba6/wp-includes/class-wp-scripts.php#L428.
		return loadExtra(handle, 'translations')
			.then(() =&gt; loadExtra(handle, 'before'))
			.then(() =&gt; loadExternalScript(handle))
			.then(() =&gt; loadExtra(handle, 'after'));
	}
} )();
&lt;/script&gt;



&lt;style&gt;img#wpstats {
			position: absolute !important;
			clip: rect(0, 0, 0, 0);
			padding: 0 !important;
			border: 0 !important;
			height: 0 !important;
			width: 0 !important;
			overflow: hidden;
		}&lt;/style&gt;&lt;img src="https://pixel.wp.com/g.gif?blog=15031761&amp;amp;v=wpcom&amp;amp;tz=1&amp;amp;user_id=0&amp;amp;post=4719&amp;amp;subd=codemanship&amp;amp;host=codemanship.wordpress.com&amp;amp;ref=https%3A%2F%2Fgo.bsky.app%2F&amp;amp;rand=0.4942255589579757" alt="" id="wpstats"&gt;&lt;script src="/wp-content/mu-plugins/actionbar/actionbar.js?v=20250204"&gt;&lt;/script&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name>View all posts by codemanship</name>
        </author>
        <media:content medium="image" url="https://i0.wp.com/codemanship.wordpress.com/wp-content/uploads/2026/08/evidence.png?fit=1200%2C675&amp;ssl=1&amp;w=640"/>
        <link href="https://i0.wp.com/codemanship.wordpress.com/wp-content/uploads/2026/08/evidence.png?fit=1200%2C675&amp;ssl=1" rel="enclosure" type="image/png"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://codemanship.wordpress.com/feed/</id>
            <title type="html">Codemanship's Blog</title>
            <link href="https://codemanship.wordpress.com" rel="alternate" type="text/html"/>
            <updated>2026-08-12T19:34:25Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19feafa1f4a:6eca0d:2eb0c877</id>
        <title type="html">Cloudflare OS is an architecture of distrust</title>
        <published>2026-08-10T09:21:28Z</published>
        <updated>2026-08-10T09:21:32Z</updated>
        <link href="https://lord.technology/2026/08/05/cloudflare-os-is-an-architecture-of-distrust.html" rel="alternate" type="text/html"/>
        <summary type="html">The strangest thing in the Cloudflare OS source code took me a while to understand.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;
        
        &lt;p&gt;The strangest thing in the &lt;a href="https://github.com/cloudflare/cloudflare-os"&gt;Cloudflare OS source code&lt;/a&gt; took me a while to understand.&lt;/p&gt;

&lt;p&gt;When an agent inside Cloudflare OS wants to do something with a side effect (merge a pull request, send an email, write a row to a system of record), it goes through a Gatekeeper, a small service that holds the credential and mediates the action. So far, that’s just a well-built MCP server. But read the contract a Gatekeeper is written against (&lt;code&gt;packages/workshop-shared/src/gatekeeper.ts&lt;/code&gt;, &lt;a href="https://github.com/cloudflare/cloudflare-os/blob/e1ab8fbd4f609aff7ede9d490bafe1bcf9b2a682/packages/workshop-shared/src/gatekeeper.ts#L617"&gt;around line 617&lt;/a&gt;) and you find this instruction to the author:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It is suggested that the gatekeeper “simulate” actions that have not been approved yet, that is, the &lt;code&gt;Session&lt;/code&gt; interface should reflect the state of the resource as if all actions had been applied.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sit with that. The agent asks to merge the PR. The human hasn’t approved it. So the Gatekeeper tells the agent the PR is merged, and if the agent reads the branch back to check its work, hands it a fabricated reality in which the merge happened. The agent, satisfied, queues the next three steps that depend on it. None of it is real. Later a human looks at the batch and either commits it or bins it, and if they bin it, everything the agent built on the fiction goes too.&lt;/p&gt;

&lt;p&gt;The first time I traced this I thought it was a hack. It’s the philosophy of the whole system, compressed into one method signature. The Gatekeeper lies to the agent on purpose, because the alternative (letting an agent’s actions touch the world the moment it decides to take them) assumes the agent’s decisions are sound. Cloudflare OS is built from end to end on the assumption that they are not.&lt;/p&gt;

&lt;p&gt;The name is a distraction, so set it aside. The Hacker News thread spent most of its energy arguing about whether “OS” is a permitted word for the thing, and that’s a dead end. What’s actually interesting is that a team led by Kenton Varda, the people who built the Workers runtime, sat down to design a platform for AI agents doing real work inside a company, and the organising principle they landed on was this: the agent cannot be trusted, so build so that its mistakes cannot matter. Every load-bearing part of the system is a variation on that sentence.&lt;/p&gt;

&lt;p&gt;Last week I &lt;a href="https://lord.technology/2026/07/29/opus-5-gets-things-wrong-more-quietly.html"&gt;wrote about Opus 5&lt;/a&gt; getting things confidently, quietly wrong: shipping a change that reported success while doing the opposite, caught only on a second pass. This is what it looks like to take that failure mode not as a grievance but as a permanent design constraint, and pour concrete on top of it.&lt;/p&gt;

&lt;h2 id="make-the-code-irrelevant-to-safety"&gt;Make the code irrelevant to safety&lt;/h2&gt;

&lt;p&gt;Start with the sandbox, because everything else stands on it.&lt;/p&gt;

&lt;p&gt;When you make a slide deck in Cloudflare OS, you aren’t using one shared slide-deck app. The system spins up a private instance of the slide-deck code, a “gadget”, that belongs only to you. It runs in its own Dynamic Worker, with its own SQLite database behind a Durable Object Facet, outbound networking switched off. Every document is its own sandboxed process.&lt;/p&gt;

&lt;p&gt;This is Sandstorm, Kenton’s startup from a decade ago, reborn. What Sandstorm got right and could never make cheap was fine-grained instancing: every document in its own isolation boundary. Containers made that far too expensive, seconds of cold start and hundreds of megabytes per grain, so the idea sat on a shelf for ten years until V8 isolates made it roughly a hundred times cheaper to run. I wrote &lt;a href="https://architectingoncloudflare.com/"&gt;a book about this platform&lt;/a&gt; and called Durable Objects its most underappreciated primitive; the thing they were waiting to enable, it turns out, was this.&lt;/p&gt;

&lt;p&gt;Per-document instancing changes where security lives. For twenty-five years the boundary in multi-tenant SaaS has run straight through the application code. One shared service holds everyone’s data, and a single mistake in a &lt;code&gt;WHERE&lt;/code&gt; clause, one missing &lt;code&gt;tenant_id&lt;/code&gt;, leaks customer B’s records to customer A. The code is load-bearing for security. It has to be correct, and a junior engineer’s off-day is a breach.&lt;/p&gt;

&lt;p&gt;Cloudflare OS moves the boundary out of the code and into the platform. If every user has their own instance, and the platform controls who can reach an instance at all, a bug in the gadget can only hurt the one person who owns it. Kenton puts it flatly: the AI cannot introduce a significant security bug. He’s right, in the sense that matters. A gadget can’t leak to another user however badly it’s written, because there is no other user in its sandbox to leak to.&lt;/p&gt;

&lt;p&gt;This is the first and boldest act of distrust. They didn’t make the AI’s code trustworthy. They gave up on that and made it irrelevant, arranging things so the correctness of what the agent writes is no longer a security property at all. You can let a non-technical colleague vibe-code an app and share it, and the reason the security team sleeps is not that the app is good. It’s that a bad app can’t reach them.&lt;/p&gt;

&lt;h2 id="never-let-the-agent-hold-the-key"&gt;Never let the agent hold the key&lt;/h2&gt;

&lt;p&gt;The cleverest move is the quiet one.&lt;/p&gt;

&lt;p&gt;The obvious way to give an agent access to GitHub is to give it a GitHub token. Anyone who has wired up an MCP server has done some version of this, and felt the small cold moment of realising a token in a prompt can end up anywhere the prompt’s output goes.&lt;/p&gt;

&lt;p&gt;Cloudflare OS never gives the agent the credential. The Gatekeeper holds it. What the agent gets is a capability: in the generated code it appears as a binding, &lt;code&gt;env.PROJECT&lt;/code&gt;, an object it can call methods on. Not the key itself, but permission to perform a specific, narrowed set of operations the Gatekeeper will carry out on its behalf. Give the GitHub Gatekeeper a single repository and it can let the agent read issues but not source, mask fields, refuse to merge without approval. The agent can call &lt;code&gt;listIssues()&lt;/code&gt;. It cannot see the token, widen its own access, or reach the network except through capabilities it was explicitly handed.&lt;/p&gt;

&lt;p&gt;None of this is theoretical to me. I built exactly this credential-broker pattern for CDS, and it has since rolled out across the engineering practice there: the broker holds the secret, the agent gets a handle and never the key itself.&lt;/p&gt;

&lt;p&gt;This is the object-capability model, and it isn’t decoration. It’s the Cap’n Proto lineage Sandstorm was built on, resurfacing as Cap’n Web RPC, because a capability is the only kind of authority you can safely hand an actor you’ve decided not to trust. A key is bearer authority: whoever holds it wields it. A capability is a leash. It does one thing, it can be revoked, it stays attached to whatever granted it. If your agent might be confidently wrong, or prompt-injected, or just careless about what it writes into an app’s source, the credential is the one thing it must never touch. So it never touches it.&lt;/p&gt;

&lt;h2 id="let-it-act-but-never-let-it-commit"&gt;Let it act, but never let it commit&lt;/h2&gt;

&lt;p&gt;Now the simulated merge from the opening makes sense: the same idea, moved from access into control flow.&lt;/p&gt;

&lt;p&gt;The problem it solves is one I’ve felt every day for a year. Synchronous approval is miserable. The agent stops on step one, you’ve wandered off to make coffee, you come back to no progress. So people cave and turn on auto-approve, or &lt;code&gt;--dangerously-skip-permissions&lt;/code&gt;, and the safety mechanism they installed to sleep at night is the first thing they switch off to get anything done.&lt;/p&gt;

&lt;p&gt;Cloudflare OS splits acting from committing. Reads flow: a read is authorised against the capability and recorded, but it doesn’t stop for a human. Writes are provisional. The Gatekeeper accepts the action, simulates the result, and lets the agent race ahead building whatever depends on it, while the real effect sits in a queue for a person to release in a batch, later, when it suits them. The agent gets the throughput of auto-approve; the human keeps the veto of manual review. The human is no longer an interruption in the loop. The human is the commit.&lt;/p&gt;

&lt;p&gt;It’s optimistic concurrency for actions in the real world, with a person as the transaction monitor, and it has the property optimistic concurrency always has: the agent spends part of its life operating on a state that isn’t true yet and may never be. It reads back its own un-happened writes and reasons about them. The architecture’s answer to “isn’t that dangerous?” is a shrug. It doesn’t matter what the agent believes about a world no human has ratified. Belief is free. Only the commit is real, and the agent doesn’t hold the commit.&lt;/p&gt;

&lt;h2 id="distrust-the-outputs-not-just-the-actions"&gt;Distrust the outputs, not just the actions&lt;/h2&gt;

&lt;p&gt;The subtlest move is the one &lt;a href="https://blog.cloudflare.com/cloudflare-os/"&gt;the blog post&lt;/a&gt; almost entirely hides.&lt;/p&gt;

&lt;p&gt;The first three mechanisms distrust what the agent does. This one distrusts what it produces. An agent reads a sensitive revenue table and builds a live dashboard from it. The dashboard is a new artefact with no access list of its own. Share it, and you may have just handed the table to everyone who can see the dashboard: a leak no single component did anything wrong to cause.&lt;/p&gt;

&lt;p&gt;So Cloudflare OS records every observation a gadget makes. The mechanism lives in &lt;a href="https://github.com/cloudflare/cloudflare-os/blob/e1ab8fbd4f609aff7ede9d490bafe1bcf9b2a682/docs/observers.md"&gt;&lt;code&gt;docs/observers.md&lt;/code&gt;&lt;/a&gt;, and it is, in essence, decentralised information-flow control: the academic dream of the mid-2000s, the Jif and HiStar and Flume line of work that never shipped commercially because labelling every piece of data by hand cost more than it returned. When Alice shares a gadget with Bob, every Gatekeeper the gadget has touched is asked, independently, whether Bob may directly read everything the gadget has already read through it. If he can’t, he’s refused. From then on, any new read the gadget makes that Bob isn’t cleared for is blocked outright.&lt;/p&gt;

&lt;p&gt;The detail I keep turning over is that the kernel refuses to understand identity. The overseer, the “kernel” in the repo’s own OS analogy, does not know what a GitHub user is, or a Google user, or what any vendor’s permissions mean. It hands each Gatekeeper an opaque token minted by the observer’s own account and lets the Gatekeeper be the sole authority on its own resource. The code goes out of its way to make that handle a random string, so no Gatekeeper author is tempted to parse identity out of it. Authority is decentralised on purpose. The centre is built to know as little as it can.&lt;/p&gt;

&lt;p&gt;Where the policy engine can’t yet express the nuance, it falls back to raw distrust. A flag, &lt;code&gt;prohibitAllSharing&lt;/code&gt;, marks data so sensitive it must never leave the owner. Once a gadget reads something wearing that flag, it drops into “lockdown mode”: it can still read, but it can no longer take a single action, through any Gatekeeper, ever, in case the action is what smuggles the secret out. A taint bit. A one-way door. The bluntest instrument available, and the comment above it says so and calls it a stopgap. What DIFC never had a good enough reason to ship for, an autonomous agent supplies: you cannot hand-audit what a thing read when it reads a thousand times an hour and reasons about all of it at once.&lt;/p&gt;

&lt;h2 id="a-tell-in-the-contributing-guide"&gt;A tell in the contributing guide&lt;/h2&gt;

&lt;p&gt;The worldview surfaces in an odd place. &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; asks you, politely, not to send pull requests longer than a dozen lines or so, and says why: AI has made writing code cheap, so an outside contribution donates the easy part while creating the expensive part, which is review and keeping the whole thing coherent. They would rather you didn’t.&lt;/p&gt;

&lt;p&gt;There’s a real claim buried there. The scarce resource in software has inverted. Producing code is no longer the bottleneck; vouching for it is. And it’s the same reflex as everything else in the system. An unreviewed contribution is untrusted input, and it barely matters whether a careless human or a confident model produced it: the instinct, now written into the project’s own governance, is to keep unvetted work out until someone accountable has looked at it. They’ve generalised their distrust of the agent into a distrust of the contribution, and then into a policy about you.&lt;/p&gt;

&lt;h2 id="is-this-wisdom-or-a-cage"&gt;Is this wisdom or a cage?&lt;/h2&gt;

&lt;p&gt;I’ve read a lot of agent frameworks this year, and most are optimistic in a way that embarrasses them within a month. This one isn’t, and I admire it for that. It’s the first agent platform I’ve seen that treats the failure mode as real instead of writing a longer system prompt and hoping. If you believe, as I’ve come to, that the models are confidently wrong often enough that their self-reports can’t be load-bearing, then an architecture that assumes exactly that isn’t cynicism. It’s honesty.&lt;/p&gt;

&lt;p&gt;But an architecture built on the premise that the worker can’t be trusted also caps what the worker is allowed to become. An agent that can never hold a credential can never do the job that truly needs one. A write that is always provisional means a human is always, structurally, in the loop: the precise bottleneck agents were meant to remove. Every act of distrust here is also a leash, and the leash that keeps the animal safe is the one that stops it pulling the cart very far. You can’t design for maximum autonomy and maximum containment at once. Cloudflare has chosen containment, hard, betting that a caged agent doing real work beats a free one you daren’t deploy.&lt;/p&gt;

&lt;p&gt;And there’s one thing the distrust never turns on. The agent is untrusted. The user’s code is untrusted. The human contributor is untrusted. Every principal is held at arm’s length and made to earn each move through a capability. Every principal except Cloudflare, whose primitives are the arm. The observation log, the Gatekeepers, the sandbox that makes the whole thing safe are all Workers, Durable Objects, Facets, some added to the runtime for this and existing nowhere else. It runs on &lt;code&gt;workerd&lt;/code&gt;, which is open source, and you can self-host it, which is more than most of its rivals can say. But the architecture that trusts no one rests entirely on trusting the one party that poured the ground beneath it.&lt;/p&gt;

&lt;p&gt;Last week a model told me it had fixed something, and it hadn’t, and I only found out because I went and looked. Cloudflare has built a whole operating system on the certainty that it always will (that the thing you delegate to will report a success it hasn’t earned), and then arranged matters so that when it does, a human holds the only pen that writes to the world. Whether that’s the shape of all serious agent infrastructure from here, or a very elegant set of walls we’ll spend three years learning to resent, I can’t tell. Probably both. The kernel table in the README has a row for processes, a row for users, and a row for agents left blank, marked only &lt;code&gt;???&lt;/code&gt;. They know it’s a new kind of thing. They just don’t trust it yet.&lt;/p&gt;


        
      &lt;/div&gt;
    &lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name>Jamie Lord</name>
        </author>
        <media:content medium="image" url="https://lord.technology/assets/images/og-image.png"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://lord.technology/feed.xml</id>
            <title type="html">Jamie Lord</title>
            <link href="https://lord.technology" rel="alternate" type="text/html"/>
            <updated>2026-08-10T09:21:32Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19fdbe0191a:6b17df:e727fc99</id>
        <title type="html">SSD Streaming for AI Models: How to Turn RAM from a Wall into a Dial</title>
        <published>2026-08-07T10:58:44Z</published>
        <updated>2026-08-07T10:58:48Z</updated>
        <link href="https://www.mindstudio.ai/blog/ssd-streaming-ai-models-ram-dial" rel="alternate" type="text/html"/>
        <summary type="html">Dwarf Star's SSD streaming stores expert weights on disk and loads them on demand, eliminating the binary 'fits or doesn't run' problem for large local models.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;section&gt;&lt;h2 id="s-1"&gt;When RAM Becomes the Bottleneck, Not the Solution&lt;/h2&gt;
&lt;p&gt;If you’ve ever tried running a large language model locally, you’ve probably hit the same wall. You find a model you want to use — maybe a Mixtral variant, a DeepSeek model, or something else with real capability — check the requirements, look at your available RAM, and discover you’re about 20GB short. The model doesn’t run at a reduced quality. It just doesn’t run.&lt;/p&gt;
&lt;p&gt;That binary outcome is the core frustration of local AI deployment. RAM has always functioned as an on/off switch: either the whole model fits, or nothing works. SSD streaming flips that logic entirely. By storing model weights on disk and loading them on demand, it turns RAM from a fixed ceiling into an adjustable dial. Dwarf Star is one of the more interesting implementations of this idea, specifically targeting the expert weights in Mixture of Experts (MoE) models.&lt;/p&gt;
&lt;p&gt;This article explains how SSD streaming works, why MoE architectures make it practical, what trade-offs you’re accepting, and who actually benefits from it.&lt;/p&gt;
&lt;hr&gt;&lt;/section&gt;&lt;section&gt;&lt;h2 id="s-2"&gt;The Real Problem with Running Large Models Locally&lt;/h2&gt;
&lt;p&gt;Most people frame the local AI problem as “my computer isn’t powerful enough.” That’s not quite right. The more precise framing is: model weights have to live somewhere your processor can access them quickly, and RAM is the fastest accessible space in that chain.&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;
    
    &lt;h2&gt;
      &lt;span&gt;Plans first.&lt;/span&gt;
      &lt;span&gt;&lt;em&gt;Then code.&lt;/em&gt;&lt;/span&gt;
    &lt;/h2&gt;
    &lt;div&gt;
      &lt;p&gt;&lt;span&gt;PROJECT&lt;/span&gt;&lt;span&gt;YOUR APP&lt;/span&gt;&lt;/p&gt;
      &lt;p&gt;&lt;span&gt;SCREENS&lt;/span&gt;&lt;span&gt;12&lt;/span&gt;&lt;/p&gt;
      &lt;p&gt;&lt;span&gt;DB TABLES&lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;/p&gt;
      &lt;p&gt;&lt;span&gt;BUILT BY&lt;/span&gt;&lt;span&gt;REMY&lt;/span&gt;&lt;/p&gt;
    &lt;/div&gt;
    &lt;div&gt;
      
      &lt;span&gt;1280 px · TYP.&lt;/span&gt;
      
      &lt;span&gt;A · UI · FRONT END&lt;/span&gt;
    &lt;/div&gt;
    &lt;p&gt;Remy writes the spec, manages the build, and ships the app.&lt;/p&gt;
  &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When you load a model, its weights get copied into RAM. Every inference — every token generated — reads from those weights repeatedly. If the weights don’t fit in RAM, the system starts reading from swap space (disk), and inference slows to a crawl or crashes entirely.&lt;/p&gt;
&lt;p&gt;For smaller models (7B parameters, quantized), this is manageable. A 4-bit quantized 7B model runs comfortably in 6–8GB of RAM. But the models that people actually want to run locally — the ones with strong reasoning, broad knowledge, and real-world utility — tend to be 30B, 70B, or larger. A 70B model at 4-bit quantization still needs around 40GB of RAM.&lt;/p&gt;
&lt;p&gt;Most consumer machines top out at 16–32GB. That gap isn’t small.&lt;/p&gt;
&lt;h3 id="the-scale-problem-gets-worse-with-capability"&gt;The Scale Problem Gets Worse with Capability&lt;/h3&gt;
&lt;p&gt;There’s an uncomfortable relationship between model capability and size. Broadly speaking, more parameters means better performance on complex tasks. The models people reach for when they want serious results — multi-step reasoning, code generation, nuanced writing — are the ones that won’t fit.&lt;/p&gt;
&lt;p&gt;This creates a practical tiering problem. You can run the models you need for simple tasks, but the moment you need something harder, you’re looking at API calls, cloud costs, or a hardware upgrade.&lt;/p&gt;
&lt;p&gt;SSD streaming is an attempt to break that relationship by changing where model weights live, not by shrinking the model itself.&lt;/p&gt;
&lt;hr&gt;&lt;/section&gt;&lt;section&gt;&lt;h2 id="s-3"&gt;Mixture of Experts: Why Not All Weights Are Created Equal&lt;/h2&gt;
&lt;p&gt;To understand why SSD streaming is practical at all, you need to understand Mixture of Experts (MoE) architecture. It’s the key that makes on-demand weight loading viable.&lt;/p&gt;
&lt;p&gt;In a standard dense model, every layer processes every input. If a model has 70 billion parameters, all 70 billion are involved in generating each token. That’s why large dense models need so much RAM — everything has to be loaded and accessible.&lt;/p&gt;
&lt;p&gt;MoE models work differently. Instead of one large feedforward network in each layer, they have multiple smaller networks called “experts.” A routing mechanism looks at each input token and decides which experts to activate — typically just two or four out of eight, sixteen, or more available.&lt;/p&gt;
&lt;h3 id="what-this-means-practically"&gt;What This Means Practically&lt;/h3&gt;
&lt;p&gt;A model like Mixtral 8x7B has eight expert networks per layer. For any given token, only two of them fire. The other six sit idle.&lt;/p&gt;
&lt;p&gt;Total parameter count is high — around 46 billion parameters — but active parameters per forward pass are much lower, closer to 12–13 billion. That’s why MoE models can punch above their inference weight: you get the parameter breadth of a large model with the compute cost of a smaller one.&lt;/p&gt;
&lt;p&gt;But here’s the implication that makes SSD streaming interesting: if only two experts are active at any given moment, you don’t need all eight experts in RAM simultaneously. You only need the two that are about to be used.&lt;/p&gt;
&lt;p&gt;This is the architectural opening that Dwarf Star exploits.&lt;/p&gt;
&lt;hr&gt;&lt;/section&gt;&lt;section&gt;&lt;h2 id="s-4"&gt;How SSD Streaming Actually Works&lt;/h2&gt;
&lt;p&gt;The core idea is straightforward: keep the expert weights on your SSD instead of in RAM, and load them into memory only when the router selects them.&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;
    
    &lt;p&gt;
      &lt;/p&gt;&lt;h3&gt;Everyone else built a construction worker.&lt;br&gt;&lt;span&gt;We built the contractor.&lt;/span&gt;&lt;/h3&gt;
    
    &lt;div&gt;
      &lt;div&gt;
        &lt;p&gt;&#129466;&lt;/p&gt;
        &lt;p&gt;CODING AGENT&lt;/p&gt;
        &lt;p&gt;Types the code you tell it to.&lt;br&gt;One file at a time.&lt;/p&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;p&gt;&#129504;&lt;/p&gt;
        &lt;p&gt;CONTRACTOR · REMY&lt;/p&gt;
        &lt;p&gt;Runs the entire build.&lt;br&gt;UI, API, database, deploy.&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Dwarf Star stores the expert weight tensors on disk. The non-expert components of the model — the attention layers, layer normalization, routing weights — stay in RAM because they’re used for every token and need to be fast. But the expert weights, which represent a large fraction of total parameters, get offloaded to disk.&lt;/p&gt;
&lt;p&gt;When inference runs and the router selects experts for a given layer, the system fetches those expert weights from the SSD, runs the computation, and moves on. The weights don’t need to stay loaded — they’ll only be needed again if that expert is selected for a future token.&lt;/p&gt;
&lt;h3 id="the-math-behind-it"&gt;The Math Behind It&lt;/h3&gt;
&lt;p&gt;Take Mixtral 8x7B as an example. Total weights are roughly 90GB in full precision, around 26GB at 4-bit quantization. A large chunk of that is expert weights.&lt;/p&gt;
&lt;p&gt;If you store experts on disk and only pull the active two per layer per token, your RAM requirement drops dramatically. The non-expert weights — the part that actually needs to stay in memory — can fit comfortably on a machine with 16–24GB of RAM.&lt;/p&gt;
&lt;p&gt;You’re not running a smaller model. You’re running the same model, with the same output quality, but with a much smaller RAM footprint.&lt;/p&gt;
&lt;h3 id="prefetching-matters"&gt;Prefetching Matters&lt;/h3&gt;
&lt;p&gt;A naive implementation would have noticeable latency: pick an expert, wait for the disk read, compute, repeat. That would be slow enough to make the approach impractical.&lt;/p&gt;
&lt;p&gt;Better implementations use prefetching. Because the routing decision for layer N is known before layer N+1 executes, the system can start loading the next layer’s experts from disk while the current computation runs. With a fast NVMe SSD (which can sustain 5–7+ GB/s sequential reads), and with careful prefetching logic, the disk reads can largely happen in parallel with compute.&lt;/p&gt;
&lt;p&gt;The result is that SSD streaming doesn’t feel like constant disk thrashing — it feels like slightly slower inference than native RAM, not like reading from a hard drive.&lt;/p&gt;
&lt;hr&gt;&lt;/section&gt;&lt;section&gt;&lt;h2 id="s-5"&gt;SSD Speed Is Not Optional&lt;/h2&gt;
&lt;p&gt;This only works if your storage is fast. A traditional SATA SSD tops out around 550 MB/s sequential read. A modern NVMe SSD using PCIe 4.0 hits 5,000–7,000 MB/s. That’s a 10x difference, and it’s the difference between a streaming approach that’s workable and one that makes inference painfully slow.&lt;/p&gt;
&lt;p&gt;If you’re considering running Dwarf Star or similar expert-streaming setups, your storage tier matters as much as your RAM:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;NVMe PCIe 4.0 or 5.0&lt;/strong&gt;: This is where streaming inference becomes genuinely usable. Read speeds are fast enough that prefetching keeps the pipeline fed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NVMe PCIe 3.0&lt;/strong&gt;: Still viable, but you’ll notice the latency more. Tokens per second will be lower.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SATA SSD&lt;/strong&gt;: Too slow for comfortable inference on most MoE models. You’ll likely bottleneck here before you bottleneck on compute.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HDD&lt;/strong&gt;: Not a realistic option. Seek times alone make this unworkable.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;This is worth flagging because “runs on your laptop” can mean very different things. A machine with a fast NVMe drive and 16GB RAM can have a genuinely good experience. The same RAM with a slower drive won’t.&lt;/p&gt;
&lt;hr&gt;&lt;/section&gt;&lt;section&gt;&lt;h2 id="s-6"&gt;The Trade-off Triangle: RAM, Speed, and Model Quality&lt;/h2&gt;
&lt;p&gt;SSD streaming doesn’t give you something for free. It trades one resource (RAM) for another (SSD bandwidth and some latency). Here’s how the trade-offs stack up:&lt;/p&gt;
&lt;h3 id="what-you-gain"&gt;What You Gain&lt;/h3&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Dramatically lower RAM requirement&lt;/strong&gt;: Models that would need 40–90GB of RAM can run on 16–24GB.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access to genuinely capable models&lt;/strong&gt;: You’re not compromising down to a smaller model — you’re running the full thing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No quality degradation&lt;/strong&gt;: The output is identical to running with full RAM. The model doesn’t know its weights are coming from disk.&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;
    &lt;h2&gt;
      &lt;span&gt;Other agents ship a demo.&lt;/span&gt;
      &lt;span&gt;Remy ships an app.&lt;/span&gt;
    &lt;/h2&gt;
    &lt;div&gt;
      &lt;div&gt;
        &lt;p&gt;&lt;span&gt;UI&lt;/span&gt;&lt;/p&gt;
        &lt;span&gt;React + Tailwind&lt;/span&gt;
        &lt;span&gt;✓ LIVE&lt;/span&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;p&gt;&lt;span&gt;API&lt;/span&gt;&lt;/p&gt;
        &lt;span&gt;REST · typed contracts&lt;/span&gt;
        &lt;span&gt;✓ LIVE&lt;/span&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;p&gt;&lt;span&gt;DATABASE&lt;/span&gt;&lt;/p&gt;
        &lt;span&gt;real SQL, not mocked&lt;/span&gt;
        &lt;span&gt;✓ LIVE&lt;/span&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;p&gt;&lt;span&gt;AUTH&lt;/span&gt;&lt;/p&gt;
        &lt;span&gt;roles · sessions · tokens&lt;/span&gt;
        &lt;span&gt;✓ LIVE&lt;/span&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;p&gt;&lt;span&gt;DEPLOY&lt;/span&gt;&lt;/p&gt;
        &lt;span&gt;git-backed, live URL&lt;/span&gt;
        &lt;span&gt;✓ LIVE&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;p&gt;Real backend. Real database. Real auth. Real plumbing. Remy has it all.&lt;/p&gt;
  &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id="what-you-give-up"&gt;What You Give Up&lt;/h3&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Tokens per second&lt;/strong&gt;: Inference is slower than native RAM loading. How much slower depends on your SSD speed, the model, and how effective the prefetching is. Expect noticeably fewer tokens per second than a fully RAM-loaded run.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSD write endurance (minor)&lt;/strong&gt;: Heavy inference use means lots of reads. SSDs have endurance ratings. For most users, this isn’t a practical concern — but it’s worth knowing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Latency on first token&lt;/strong&gt;: The initial load can take a moment as the first experts get pulled from disk.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;For interactive use — asking questions, generating code, writing — the speed trade-off is usually acceptable. For batch inference or applications that need to process thousands of requests, you’d want native RAM or cloud infrastructure instead.&lt;/p&gt;
&lt;hr&gt;&lt;/section&gt;&lt;section&gt;&lt;h2 id="s-7"&gt;Who This Is Actually For&lt;/h2&gt;
&lt;p&gt;SSD streaming is a specific solution for a specific problem. It’s worth being honest about the audience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It’s a good fit if:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;You want to run a high-capability MoE model locally and have 16–24GB RAM&lt;/li&gt;
&lt;li&gt;You have a fast NVMe SSD with adequate free space&lt;/li&gt;
&lt;li&gt;Your use case is interactive or low-throughput (personal use, development, research)&lt;/li&gt;
&lt;li&gt;You care about privacy or offline access and can’t or don’t want to use cloud APIs&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;It’s probably not the right fit if:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;You need high-throughput inference (many requests per minute)&lt;/li&gt;
&lt;li&gt;Your SSD is slow (SATA or older NVMe)&lt;/li&gt;
&lt;li&gt;You’re building a production system that serves multiple users&lt;/li&gt;
&lt;li&gt;The latency trade-off would visibly degrade your end-user experience&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;It’s also worth noting that SSD streaming is most relevant for MoE architectures. Dense models don’t have the same natural partition — there’s no concept of “active vs. inactive” weights at inference time in the same way. Applying streaming to a dense model would require different approaches and would likely be significantly less efficient.&lt;/p&gt;
&lt;hr&gt;&lt;/section&gt;&lt;section&gt;&lt;h2 id="s-8"&gt;Where Cloud Models Change the Calculation&lt;/h2&gt;
&lt;p&gt;For most people building AI-powered applications, the local hardware constraint is the wrong problem to be solving. Running a model locally makes sense for privacy-critical applications, offline use, or research. But if you’re building something that needs to work reliably, serve multiple users, or access the best available models, cloud inference is the practical path.&lt;/p&gt;
&lt;p&gt;This is where something like &lt;a href="https://mindstudio.ai"&gt;MindStudio&lt;/a&gt; fits into the picture. MindStudio gives you access to 200+ models — Claude, GPT-4o, Gemini, Mistral, and others — without managing API keys, worrying about RAM constraints, or thinking about SSD speeds. You pick the model that fits your task, and the infrastructure is already handled.&lt;/p&gt;
&lt;p&gt;For teams building AI agents and automated workflows, that matters more than most people realize. The bottleneck isn’t usually “can I run this model” — it’s “can I connect this model to the right data, trigger it at the right time, and get the output into the right system.” MindStudio’s visual workflow builder handles exactly that: connecting models to business tools, building multi-step pipelines, and deploying without writing infrastructure code.&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;
    &lt;p&gt;
      &lt;/p&gt;&lt;h2&gt;
        &lt;span&gt;Other agents start typing.&lt;/span&gt;
        &lt;span&gt;Remy starts asking.&lt;/span&gt;
      &lt;/h2&gt;
    
    &lt;p&gt;
      &lt;span&gt;YOU SAID&lt;/span&gt;
      &lt;span&gt;"Build me a sales CRM."&lt;/span&gt;
    &lt;/p&gt;
    &lt;p&gt;REMY ASKS&lt;/p&gt;
    &lt;div&gt;
      &lt;p&gt;
        &lt;span&gt;01&lt;/span&gt;
        &lt;span&gt;DESIGN&lt;/span&gt;
        &lt;span&gt;Should it feel like Linear, or Salesforce?&lt;/span&gt;
      &lt;/p&gt;
      &lt;p&gt;
        &lt;span&gt;02&lt;/span&gt;
        &lt;span&gt;UX&lt;/span&gt;
        &lt;span&gt;How do reps move deals — drag, or dropdown?&lt;/span&gt;
      &lt;/p&gt;
      &lt;p&gt;
        &lt;span&gt;03&lt;/span&gt;
        &lt;span&gt;ARCH&lt;/span&gt;
        &lt;span&gt;Single team, or multi-org with permissions?&lt;/span&gt;
      &lt;/p&gt;
    &lt;/div&gt;
    &lt;p&gt;Scoping, trade-offs, edge cases — the real work. Before a line of code.&lt;/p&gt;
  &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you’re interested in what capable models can actually do in production rather than in your terminal, &lt;a href="https://mindstudio.ai"&gt;you can try MindStudio free at mindstudio.ai&lt;/a&gt;. It’s a different solution to the same underlying goal: getting access to capable AI without the friction.&lt;/p&gt;
&lt;hr&gt;&lt;/section&gt;&lt;section&gt;&lt;h2 id="s-9"&gt;Practical Setup Considerations for SSD Streaming&lt;/h2&gt;
&lt;p&gt;If you’re interested in trying expert streaming locally, here’s what to think through before starting.&lt;/p&gt;
&lt;h3 id="storage-requirements"&gt;Storage Requirements&lt;/h3&gt;
&lt;p&gt;Large MoE models are large. Mixtral 8x7B at 4-bit quantization is around 26GB. Larger models like Mixtral 8x22B run over 80GB even quantized. Make sure you have adequate free space — running out mid-inference is painful.&lt;/p&gt;
&lt;h3 id="quantization-still-helps"&gt;Quantization Still Helps&lt;/h3&gt;
&lt;p&gt;SSD streaming and quantization work together. A 4-bit or 8-bit quantized model still produces the expert streaming benefit, and smaller file sizes mean faster disk reads. GGUF format (used by llama.cpp) and similar quantized formats are generally compatible with streaming approaches.&lt;/p&gt;
&lt;h3 id="ram-allocation"&gt;RAM Allocation&lt;/h3&gt;
&lt;p&gt;You’ll want to be deliberate about what stays in RAM versus what streams from disk. The routing logic, attention layers, and other non-expert components should stay in RAM for speed. Most expert streaming implementations handle this automatically, but knowing the split helps you debug if something’s slow.&lt;/p&gt;
&lt;h3 id="thermal-throttling-on-laptops"&gt;Thermal Throttling on Laptops&lt;/h3&gt;
&lt;p&gt;Sustained inference on a laptop will push both CPU and SSD hard. SSDs throttle when they get hot, which will slow your reads and hurt inference speed. If you’re doing long inference sessions on a laptop, this is worth monitoring.&lt;/p&gt;
&lt;hr&gt;&lt;/section&gt;&lt;section&gt;&lt;h2 id="s-10"&gt;Frequently Asked Questions&lt;/h2&gt;
&lt;h3 id="what-is-ssd-streaming-for-ai-models"&gt;What is SSD streaming for AI models?&lt;/h3&gt;
&lt;p&gt;SSD streaming is a technique that stores model weights on an SSD rather than keeping them entirely in RAM. During inference, the system loads only the weights it currently needs from disk, runs the computation, and discards or cycles them. This reduces RAM requirements significantly while maintaining full model quality, at the cost of some inference speed.&lt;/p&gt;
&lt;h3 id="does-ssd-streaming-reduce-model-quality"&gt;Does SSD streaming reduce model quality?&lt;/h3&gt;
&lt;p&gt;No. The model weights are identical — they’re just stored on disk rather than RAM. The outputs of inference are the same as if you’d loaded everything into RAM. Quality is only affected if you also apply additional quantization on top of the streaming setup, which is separate.&lt;/p&gt;
&lt;h3 id="what-kind-of-ssd-do-i-need-for-ai-model-streaming"&gt;What kind of SSD do I need for AI model streaming?&lt;/h3&gt;
&lt;p&gt;You need an NVMe SSD, ideally PCIe 4.0 or better. These drives achieve sequential read speeds of 5,000–7,000 MB/s, which is fast enough for prefetching to work effectively. SATA SSDs (capped around 550 MB/s) are generally too slow for comfortable inference. PCIe 3.0 NVMe drives are workable but will show more latency.&lt;/p&gt;
&lt;h3 id="why-does-ssd-streaming-work-better-for-moe-models"&gt;Why does SSD streaming work better for MoE models?&lt;/h3&gt;
&lt;p&gt;Mixture of Experts models only activate a fraction of their expert networks per token — typically two out of eight or more. This creates a natural partition between “weights needed right now” and “weights that can stay on disk.” Dense models don’t have this partition, so streaming them is less efficient — you’d need all weights for every forward pass.&lt;/p&gt;
&lt;h3 id="how-does-dwarf-star-differ-from-just-using-swap-space"&gt;How does Dwarf Star differ from just using swap space?&lt;/h3&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;
    &lt;p&gt;REMY IS NOT&lt;/p&gt;
    &lt;ul&gt;&lt;li&gt;&lt;span&gt;✕&lt;/span&gt;&lt;span&gt;a coding agent&lt;/span&gt;&lt;/li&gt;
      &lt;li&gt;&lt;span&gt;✕&lt;/span&gt;&lt;span&gt;no-code&lt;/span&gt;&lt;/li&gt;
      &lt;li&gt;&lt;span&gt;✕&lt;/span&gt;&lt;span&gt;vibe coding&lt;/span&gt;&lt;/li&gt;
      &lt;li&gt;&lt;span&gt;✕&lt;/span&gt;&lt;span&gt;a faster Cursor&lt;/span&gt;&lt;/li&gt;
    &lt;/ul&gt;&lt;p&gt;IT IS&lt;/p&gt;
    &lt;p&gt;&lt;span&gt;✓&lt;/span&gt;&lt;span&gt;a general contractor for software&lt;/span&gt;&lt;/p&gt;
    &lt;p&gt;The one that tells the coding agents what to build.&lt;/p&gt;
  &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Operating system swap space uses disk as a passive overflow when RAM fills up. It’s not optimized for sequential weight loading and lacks any awareness of model structure or inference patterns. Dwarf Star actively manages which weights to load, when to load them, and uses prefetching based on routing decisions to hide disk latency. It’s a purpose-built system, not a generic fallback.&lt;/p&gt;
&lt;h3 id="can-i-use-ssd-streaming-for-production-applications"&gt;Can I use SSD streaming for production applications?&lt;/h3&gt;
&lt;p&gt;For low-traffic or single-user scenarios, it’s viable. For production systems handling multiple concurrent users or requiring high throughput, the latency and bandwidth limitations of SSD streaming become problematic. In those cases, cloud inference or dedicated GPU infrastructure is more appropriate.&lt;/p&gt;
&lt;hr&gt;&lt;/section&gt;&lt;section&gt;&lt;h2 id="s-11"&gt;Key Takeaways&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;RAM has historically been a binary constraint for local AI models — either the full model fits or it doesn’t run. SSD streaming turns it into a sliding scale.&lt;/li&gt;
&lt;li&gt;MoE architectures make SSD streaming practical because only a subset of expert weights are active at any given time — the rest can live on disk without affecting output quality.&lt;/li&gt;
&lt;li&gt;Dwarf Star implements this by streaming expert weights from SSD to RAM on demand, with prefetching to minimize latency impact.&lt;/li&gt;
&lt;li&gt;NVMe SSD speed is critical. PCIe 4.0 drives make this approach genuinely usable; slower storage undermines the value.&lt;/li&gt;
&lt;li&gt;The trade-off is tokens per second, not quality. Outputs are identical to a fully RAM-loaded run — inference is just slower.&lt;/li&gt;
&lt;li&gt;For most people building AI-powered applications, cloud inference through a platform like &lt;a href="https://mindstudio.ai"&gt;MindStudio&lt;/a&gt; eliminates the hardware constraint entirely and lets you focus on what the models actually do rather than how to make them run.&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://i.mscdn.ai/70cbb1ad-08d7-4fdc-ab31-e343780966a6/generated-images/64da3721-843c-4efa-a497-30af3cdf3b65.png?fm=auto&amp;w=1200&amp;h=630&amp;fit=crop"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://www.mindstudio.ai/rss.xml</id>
            <title type="html">MindStudio</title>
            <link href="https://www.mindstudio.ai" rel="alternate" type="text/html"/>
            <updated>2026-08-07T10:58:48Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19f714b9083:193b35:54ced93c</id>
        <title type="html">New research: The Core Web Vitals thresholds you trust might be wrong for your site</title>
        <published>2026-07-17T18:16:45Z</published>
        <updated>2026-07-17T18:16:49Z</updated>
        <link href="https://embrace.io/blog/research-core-web-vitals/" rel="alternate" type="text/html"/>
        <summary type="html">Core Web Vitals data from ten leading online retailers reveals that &amp;quot;good&amp;quot; for Google may not be good enough for your users or your business.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;
		&lt;div&gt;
			&lt;p&gt;I decided to dig into my questions properly. I pulled a month of real user data from ten leading online retailers and built a correlation chart for each one, plotting session-level Largest Contentful Paint (LCP) data against bounce rate for each site.&lt;/p&gt;
&lt;p&gt;Before we get into the findings, first I want to cover what a performance correlation chart is and what it shows you.&lt;/p&gt;
&lt;p&gt;A correlation chart is a powerful data visualization that shows you the relationship between your page speed metrics and your business and user engagement metrics.&lt;/p&gt;
&lt;p&gt;To illustrate, here’s the correlation chart for the first retail site in this research:&lt;/p&gt;

		&lt;/div&gt;
	&lt;/div&gt;
&lt;div&gt;
		&lt;div&gt;
			&lt;p&gt;Correlation charts are generated using real user monitoring (RUM) data. They give you a histogram view of all your user traffic, broken out into cohorts based on performance metrics, such as Start Render, Largest Contentful Paint, Interaction to Next Paint, and more. Each cohort shows you the median time for whatever metric you’re tracking for the session. (Those cohorts are represented in the yellow columns in the chart above.)&lt;/p&gt;
&lt;p&gt;The next layer of the chart is where things get really interesting.&lt;/p&gt;
&lt;p&gt;You also get an overlay (the blue line in the chart above) that shows you the business or user engagement metric – commonly conversion rate or bounce rate, but there are many more – that correlates to each of these cohorts. This lets you see at a glance how closely your business/engagement metric aligns with the speed of your site.&lt;/p&gt;
&lt;p&gt;For this investigation, I focused on bounce rate rather than conversion rate because it’s captured by default in most RUM tools, so there’s no extra instrumentation required. It’s also a good directional proxy. In my experience, if you see a relationship between a performance metric and bounce rate, there’s a good chance a similar relationship exists with conversion rate, too.&lt;/p&gt;
&lt;p&gt;On the correlation chart for each site, I marked three points:&lt;/p&gt;

		&lt;/div&gt;
	&lt;/div&gt;
&lt;div&gt;
		&lt;div&gt;
			&lt;p&gt;If you’re not familiar with the performance plateau, &lt;a href="https://www.speedcurve.com/blog/web-performance-plateau/" target="_blank"&gt;I’ve written about it before&lt;/a&gt;. Short version: every site eventually hits a point where shaving off more milliseconds stops moving the needle on engagement. Below that point, speed matters a lot. Beyond it, you’re optimizing for nothing.&lt;/p&gt;

		&lt;/div&gt;
	&lt;/div&gt;
&lt;div&gt;
		&lt;div&gt;
			&lt;p&gt;Across all ten retailers, the optimal LCP — the point tied to the best bounce rate — ranged from &lt;strong&gt;100 milliseconds to 1 second&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Not 2.5 seconds.&lt;/p&gt;
&lt;p&gt;Every one of these sites saw its best engagement well before Google’s “good” threshold. For several sites, 2.5 seconds was already sitting on the plateau – the zone where speed had stopped helping long before.&lt;/p&gt;

		&lt;/div&gt;
	&lt;/div&gt;
&lt;div&gt;
		&lt;div&gt;
			&lt;p&gt;&lt;strong&gt;I want to emphasize this before I go on: this is NOT a new number for you to chase.&lt;/strong&gt; 100ms–1s isn’t a universal target any more than 2.5 seconds is. That’s exactly the trap we’re trying to avoid. What it tells you is that “good enough” – for real users on real sites – can be much faster than the industry standard suggests. The only way to know your number is to look at your own data.&lt;/p&gt;

		&lt;/div&gt;
	&lt;/div&gt;
&lt;div&gt;
		&lt;div&gt;
			&lt;p&gt;The start time for the performance plateau ranged from 500 milliseconds to 5.6 seconds across all ten sites. This wasn’t surprising. There’s typically a lot of variance in how the performance plateau manifests across different sites.&lt;/p&gt;
&lt;p&gt;What was surprising was that, for four of the ten sites, the performance plateau started &lt;em&gt;before&lt;/em&gt; 2.5 seconds. By the time those four sites met Google’s recommended threshold for “good” Largest Contentful Paint time, they were already on the performance plateau.&lt;/p&gt;
&lt;p&gt;In other words, &lt;strong&gt;even though these four sites are technically fast enough for Google, they’ve actually bottomed out in terms of bounce rate&lt;/strong&gt;.&lt;/p&gt;

		&lt;/div&gt;
	&lt;/div&gt;
&lt;div&gt;
		&lt;div&gt;
			&lt;p&gt;At the risk of repeating myself (but it bears repeating), you should never assume that meeting external thresholds is actually helping your business. You need to look at your own data to set meaningful targets.&lt;/p&gt;

		&lt;/div&gt;
	&lt;/div&gt;
&lt;div&gt;
		&lt;div&gt;
			&lt;p&gt;Below, I’m sharing the correlation chart for each of the ten retailers, along with what stood out in each one: where the optimal LCP landed, where the plateau began, and anything else worth noting about the shape of the curve.&lt;/p&gt;
&lt;p&gt;To be completely candid, even though I hypothesized these results, I was surprised at how consistent they were.&lt;/p&gt;

		&lt;/div&gt;
	&lt;/div&gt;
&lt;div&gt;
		&lt;div&gt;
			&lt;p&gt;It’s tempting to look at “100 milliseconds to 1 second” and treat it as a new target to chase. Please, please don’t. As I mentioned earlier, that’s exactly the trap we’re trying to avoid. Consider this research a methodology, not a number.&lt;/p&gt;
&lt;p&gt;The good news is that the method is straightforward:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Pull your own RUM data.&lt;/li&gt;
&lt;li&gt;Build a correlation chart plotting LCP (or any other user-oriented metric) against bounce rate.&lt;/li&gt;
&lt;li&gt;Find where your bounce rate is actually at its best, and where your plateau begins.&lt;/li&gt;
&lt;li&gt;Set your performance goals based on that – not on a number based on millions of sites you don’t run.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;Your optimal LCP might turn out to be 1.8 seconds. It might be 400 milliseconds. It might be 5 seconds. Who knows! There’s no way to know without looking. But if these ten retailers are any indication, “fast enough” may be faster than the industry standard suggests. &lt;strong&gt;Treating 2.5 seconds as your finish line could mean leaving engagement and revenue on the table without realizing it.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you run this analysis on your own site, I’d love to hear what you find! If you’re an Embrace/SpeedCurve customer and you want to know how to do your own investigation, let me know and I’ll be happy to help.&lt;/p&gt;

		&lt;/div&gt;
	&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name>Tammy Everts</name>
        </author>
        <media:content medium="image" url="https://embrace.io/wp-content/uploads/2026/07/LCP-retail-research-hero-NEW2.gif"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://embrace.io/feed/</id>
            <title type="html">Embrace</title>
            <link href="https://embrace.io" rel="alternate" type="text/html"/>
            <updated>2026-07-17T18:16:49Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19f215420f5:1bc635:3cf045dc</id>
        <title type="html">A Boring Tiny Tooling paradigm</title>
        <published>2026-07-02T05:36:29Z</published>
        <updated>2026-07-02T05:36:33Z</updated>
        <link href="https://vaughntan.org/bttparadigm" rel="alternate" type="text/html"/>
        <summary type="html">tl;dr: Small-medium businesses are missing a huge opportunity because of three imagination failures: they believe digital transformation must be big</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;

        

        &lt;p&gt;
          6/1/2026

            ☼ &lt;a href="https://vaughntan.org/tagged/product"&gt;product&lt;/a&gt;
            ☼ &lt;a href="https://vaughntan.org/tagged/consulting"&gt;consulting&lt;/a&gt;
            ☼ &lt;a href="https://vaughntan.org/tagged/ai"&gt;AI&lt;/a&gt;
            ☼ &lt;a href="https://vaughntan.org/tagged/btt"&gt;BTT&lt;/a&gt;
        &lt;/p&gt;

        

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;tl;dr:&lt;/strong&gt; Small-medium businesses are missing a huge opportunity because of three imagination failures: they believe digital transformation must be big and disruptive, they haven’t recalibrated software buying for the collapse in software development costs, and they don’t realise this economic shift requires a different consulting approach. These businesses need Boring Tiny Tools (very narrowly scoped software automating repetitive workflows) built by amorphous consultants who observe actual operations before proposing solutions.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Over the last year, I’ve been writing about and doing a lot of &lt;a href="https://vaughntan.org/amorphousconsulting"&gt;amorphous consulting&lt;/a&gt; while using the &lt;a href="https://vaughntan.org/meaningmakingai"&gt;meaningmaking lens to think practically about what human work should be in a time of &lt;span&gt;AI&lt;/span&gt;&lt;/a&gt;. In doing that, I’ve spent a lot of time both in and lurking at the edges of conversations with people working at the cutting edge of &lt;span&gt;AI&lt;/span&gt; use. I recently had a long call with &lt;a href="https://roblog.co.uk/about/" target="_blank"&gt;Rob Miller&lt;/a&gt;, a software developer and independent consultant who emailed me after reading &lt;a href="https://vaughntan.org/boringtinytools"&gt;an essay I wrote about Boring Tiny Tools&lt;/a&gt;. (A &lt;span&gt;BTT&lt;/span&gt; is a &lt;em&gt;very&lt;/em&gt; narrowly scoped piece of software that automates boring, routine workflows without disrupting other workflows.)&lt;/p&gt;
&lt;p&gt;Rob told me about spending a single afternoon building a &lt;span&gt;BTT&lt;/span&gt; for his wife’s firm, a barristers’ chambers. This &lt;span&gt;BTT&lt;/span&gt; is a court submission preparation tool that works on-premises to be compliant with data protection. The tool makes &lt;span&gt;PDF&lt;/span&gt; multi-document bundles, automatically creates hyperlinked tables of contents, and formats the bundle to court specifications. Error-prone humans used to spend hours doing this boring job manually. Now it is a software-enabled, reliable automated process that takes minutes. Multiply that across 50-60 people and thousands of cases per year in the organisation and the savings quickly stack up.&lt;/p&gt;
&lt;p&gt;Rob’s &lt;span&gt;BTT&lt;/span&gt; is yet another validation of the new emerging paradigm I’m seeing (and am maybe a part of) at the place where software, consulting, and digital transformation touch each other. The greatest potential beneficiaries of this new paradigm are small-medium businesses with a particular profile:&lt;/p&gt;
&lt;ol type="1"&gt;&lt;li&gt;Small enough to have no in-house transformation tools team, &lt;strong&gt;but&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Large enough that efficiency gains from transformation save a significant amount of money annually (say, millions vs tens of thousands of dollars), &lt;strong&gt;but&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Not so large that transformation savings justify spending hundreds of thousands of dollars on conventional transformation consultants to write bespoke business process software.&lt;/li&gt;
&lt;/ol&gt;&lt;h2 id="three-imagination-failures"&gt;Three imagination failures&lt;/h2&gt;
&lt;p&gt;Unfortunately, these small-medium businesses don’t actually seem to be benefiting yet. It’s more accurate to say that the new &lt;strong&gt;Boring Tiny Tooling Paradigm&lt;/strong&gt; is struggling to emerge but three related imagination failures keep getting in the way.&lt;/p&gt;
&lt;p&gt;These three imagination failures affect the idea of what transformation should look like, what the software that enables it should be, and what kind of consulting approach to buy:&lt;/p&gt;
&lt;ol type="1"&gt;&lt;li&gt;&lt;strong&gt;Inability to conceive of digital transformation as anything other than Grand and Disruptive.&lt;/strong&gt; Only big strategic software deployments, enterprise platforms, major integrations, or organisation-wide rollouts are legible as transformation. BTTs that address a profusion of tiny business processes don’t read as transformation, even when the return on investment is both immediate and measurable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inability to recalibrate the scale of software bought to account for the sudden collapse in the cost of writing software.&lt;/strong&gt; Software that used to cost tens of thousands of dollars and take months to write now costs thousands of dollars and takes days to write. It’s finally economically feasible to write BTTs to automate tiny, boring business processes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inability to see that the collapse in software economics necessitates a different transformation consulting approach.&lt;/strong&gt; That BTTs are suddenly cheap, fast, and viable to build should be feeding through to the kinds of transformation consulting services businesses buy … but businesses continue to want conventional consulting services that price and deploy oversize, too-expensive transformation solutions based on pre-collapse software economics.&lt;/li&gt;
&lt;/ol&gt;&lt;h2 id="a-consulting-problem"&gt;A consulting problem&lt;/h2&gt;
&lt;p&gt;That last imagination failure is the big one.&lt;/p&gt;
&lt;p&gt;When businesses can only imagine transformations that are big and legible, they look for and engage conventional consultants who promise big legible transformations. These conventional consultants scope and deliver expensive and needlessly enterprise-scale solutions to justify their big fees and promises of big transformations.&lt;/p&gt;
&lt;p&gt;(It is now an open secret that the big consulting firms are using &lt;span&gt;AI&lt;/span&gt; tools internally to cut development time while still charging out at pre-AI prices. They keep the difference and have no incentive to educate clients about these new economics.)&lt;/p&gt;
&lt;p&gt;Meanwhile, what the small-medium business actually needs is a different consulting approach entirely: What I call &lt;a href="https://vaughntan.org/amorphousconsulting"&gt;amorphous consulting&lt;/a&gt;. In contrast to conventional consulting, amorphous consulting comes into a client engagement with an open mind about the problem and the solution.&lt;/p&gt;
&lt;p&gt;Rob’s bundle preparation tool was a solution scoped out of a deep understanding of the user’s real operational problems. The amorphous consultant gets this kind of deep understanding by spending time observing what’s happening in the client’s organisation. This approach to consulting leads to tiny, boring, highly effective solutions. When software is scoped and built by amorphous consultants, it is more likely to be&lt;/p&gt;
&lt;ol type="1"&gt;&lt;li&gt;Minimal and fast to build (because the scope will be limited to only features needed to address the actual problem to be solved),&lt;/li&gt;
&lt;li&gt;Functional within actual operational constraints faced by users on the ground,&lt;/li&gt;
&lt;li&gt;Actually adopted by users with operational responsibilities, and&lt;/li&gt;
&lt;li&gt;Undisruptive to other workflows.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;These are all desirable characteristics of transformation software.&lt;/p&gt;
&lt;p&gt;How Rob scoped and built his &lt;span&gt;BTT&lt;/span&gt; — prompted by a serendipitous observation of his wife doing silly rote work to prepare a bundle manually while on holiday in Champagne — is an example of the kind of amorphous consulting approach that most small-medium businesses actually need.&lt;/p&gt;
&lt;p&gt;Unfortunately, most of these businesses don’t know how to procure these kinds of amorphous consulting services where the scopes of work and target deliverables can’t (and shouldn’t be) defined in advance.&lt;/p&gt;
&lt;h2 id="imagination-is-the-hard-part"&gt;Imagination is the hard part&lt;/h2&gt;
&lt;p&gt;As I wrote in &lt;a href="https://uncertaintymindset.substack.com/p/ai-missing-middle" target="_blank"&gt;&lt;span&gt;AI&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;’&lt;/span&gt;s missing middle&lt;/a&gt;, small-medium firms in traditional industries represent a massive market and a massive missed opportunity. (Policymakers and investors might want to take note.) Small-medium firms don’t need fancy conventional consultants and their overscoped and expensively disruptive software. What they do need are Boring Tiny Tools scoped and built by amorphous consultants.&lt;/p&gt;
&lt;p&gt;None of this is expensive or technically challenging. All it takes is better imagination.&lt;/p&gt;

        
    
      

&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name/>
        </author>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://vaughntan.org/feed.rss</id>
            <title type="html">vaughntan.org</title>
            <link href="https://vaughntan.org" rel="alternate" type="text/html"/>
            <updated>2026-07-02T05:36:33Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19f16a13cf3:1fc5de:4f181844</id>
        <title type="html">Even with a silver bullet, you still have to aim</title>
        <published>2026-06-30T03:44:53Z</published>
        <updated>2026-06-30T03:44:56Z</updated>
        <link href="https://productpicnic.beehiiv.com/p/even-with-a-silver-bullet-you-still-have-to-aim" rel="alternate" type="text/html"/>
        <summary type="html">Productivity gains among frontline employees can't counteract the abdication of responsibility at the executive level.</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;As George Lucas once said, for some reason: history is like poetry. It rhymes.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Our industry is certainly rhyming with a historical era I love to pick on: the boom in tech investment that followed the one-two punch of the Dot Com Bust and the Financial Crisis. Companies and their investors were desperate to find that one silver bullet that would turn everything around. Back then, that silver bullet was Design Thinking. Big tech poached designers from celebrated consultancies and airdropped them into pure chaos without a plan beyond “fix the product for us.”&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Ten years after design thinking’s heyday, there’s a new silver bullet that everyone has pinned their hopes on, without a clear idea of what exactly it will do. At this point in the hype cycle, you’ve seen the numbers for yourselves: billions in investments, and loud cricket noises for returns. Anecdotes of individual productivity gains are easy to come by, and yet they do not translate to improvements in the bottom line!&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;What’s actually making teams move faster is &lt;/span&gt;&lt;span&gt;&lt;a href="https://www.linkedin.com/posts/mbowler_self-sufficient-teams-activity-7470482224623157250-XNt-/?utm_campaign=even-with-a-silver-bullet-you-still-have-to-aim&amp;amp;utm_medium=referral&amp;amp;utm_source=productpicnic.beehiiv.com" target="_blank" rel=""&gt;org design&lt;/a&gt;&lt;/span&gt;&lt;span&gt;. Taking care of organizational dysfunction may be a side effect of “AI transformation” but it sure seems to be offering the majority of the benefits.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;❝&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Consider &lt;/span&gt;&lt;span&gt;&lt;a href="https://blog.mikebowler.ca/2024/07/02/self-sufficient-teams/?utm_campaign=even-with-a-silver-bullet-you-still-have-to-aim&amp;amp;utm_medium=referral&amp;amp;utm_source=productpicnic.beehiiv.com" target="_blank" rel=""&gt;this story&lt;/a&gt;&lt;/span&gt;&lt;span&gt; when the right handpicked people completed in 3 hours, a piece of work estimated at 10-12 weeks. That story was from roughly ten years ago, well before any of the AI tools appeared on the scene. It didn’t rely on magical tooling, it just needed us to &lt;/span&gt;&lt;span&gt;&lt;strong&gt;undo some organizational dysfunction and to put the right people in the room&lt;/strong&gt;&lt;/span&gt;&lt;span&gt; to collaborate on the problem. &lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p id="the-purpose-of-a-system-is-what-it-"&gt;&lt;/p&gt;&lt;h1&gt;&lt;span&gt;The purpose of a system is what it does&lt;/span&gt;&lt;/h1&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;“AI works” vs “AI doesn’t work” is an exhausting debate; no one is ever convinced of anything and the only winners of the arguments are keyboard manufacturers. I’m not going to go over the various hallucination rates and studies on how much longer it takes to check an AI’s output for errors than to just do it yourself. Even with 0% error rates, those figures would be besides the point. The biggest problem — and don’t take my word for it, listen to some Nobel laureate guy — is that &lt;/span&gt;&lt;span&gt;&lt;a href="https://finance.yahoo.com/technology/ai/articles/nobel-laureate-daron-acemoglu-brainless-090000758.html?utm_campaign=even-with-a-silver-bullet-you-still-have-to-aim&amp;amp;utm_medium=referral&amp;amp;utm_source=productpicnic.beehiiv.com" target="_blank" rel=""&gt;the bottleneck lies upstream of your Jira ticket&lt;/a&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;❝&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Most research on AI productivity is overblown because it overwhelmingly focuses on easy, well-defined tasks where context is clear. These are not representative of the economy, which simply isn't set up with so many of those.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;We are not in the future. It is still 2016. It is still &lt;/span&gt;&lt;span&gt;&lt;em&gt;1986&lt;/em&gt;&lt;/span&gt;&lt;span&gt;, if you are so inclined. We have hardly made any strides in our ability to define tasks and their context. What baby steps we’ve made in the right direction have been swallowed up by a great cloud of noise. For every Toyota Way book written in the 90s, there is a &lt;/span&gt;&lt;span&gt;&lt;a href="https://www.the-independent.com/tech/ford-ai-automation-human-workers-b3003787.html?utm_campaign=even-with-a-silver-bullet-you-still-have-to-aim&amp;amp;utm_medium=referral&amp;amp;utm_source=productpicnic.beehiiv.com" target="_blank" rel=""&gt;Ford counterexample&lt;/a&gt;&lt;/span&gt;&lt;span&gt; being eagerly practiced today.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;AI has intensified this problem, as it intensifies all things to which it is applied. But the problem did not originate with AI. Before Claude could learn to talk like the median LinkedIn influencer, the median LinkedIn influencer had to &lt;/span&gt;&lt;span&gt;&lt;a href="https://www.linkedin.com/posts/cameron-tonkinwise-80a5987_zuckerberg-urges-meta-to-explore-working-activity-7476445339470823424-Ug96/?utm_campaign=even-with-a-silver-bullet-you-still-have-to-aim&amp;amp;utm_medium=referral&amp;amp;utm_source=productpicnic.beehiiv.com" target="_blank" rel=""&gt;arrive at that mindset&lt;/a&gt;&lt;/span&gt;&lt;span&gt; under their own power:&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;❝&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Criticism of the […] slop genAI produces invariably fails to acknowledge how much &lt;/span&gt;&lt;span&gt;&lt;strong&gt;slop-ification was already characterising so many discourses prior to retail LLMs&lt;/strong&gt;&lt;/span&gt;&lt;span&gt;: PEEL paragraphs, Powerpoint slides, TED talks, Big Consultancy strategy canvases, PR company produced apologies to customers, LinkedIn posts...&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p id="the-bottleneck-is-the-context-in-wh"&gt;&lt;/p&gt;&lt;h1&gt;&lt;span&gt;The bottleneck is the context in which the work takes place&lt;/span&gt;&lt;/h1&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;For every step a would-be thought leader took towards “synergistic leadership” or “growth-hacking paradigms” they took a step &lt;/span&gt;&lt;span&gt;&lt;a href="https://news.cornell.edu/stories/2026/03/workers-who-love-synergizing-paradigms-might-be-bad-their-jobs?utm_campaign=even-with-a-silver-bullet-you-still-have-to-aim&amp;amp;utm_medium=referral&amp;amp;utm_source=productpicnic.beehiiv.com" target="_blank" rel=""&gt;away from reality&lt;/a&gt;&lt;/span&gt;&lt;span&gt;. And corporate bullshit (not my name for it but a real term of the art, take it up with Cornell!) is not the only layer lubricating a product’s slide into slop. Robert Fabricant has &lt;/span&gt;&lt;span&gt;&lt;a href="https://www.fastcompany.com/91563306/are-designers-to-blame-for-our-tech-dystopia-its-complicated?trk=feed_main-feed-card_feed-article-content&amp;amp;utm_campaign=even-with-a-silver-bullet-you-still-have-to-aim&amp;amp;utm_medium=referral&amp;amp;utm_source=productpicnic.beehiiv.com" target="_blank" rel=""&gt;a good layman’s overview&lt;/a&gt;&lt;/span&gt;&lt;span&gt; of how little a modern product is influenced by a given design decision, and how much is a consequence of business and technology decisions that stack up over time without a single decision-maker deliberately steering things to be so. I recommend giving this one a read, as a means of cognitive estrangement from the ways of working we take for granted, because we are too close to them to realize how ridiculous they really are. &lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Unfortunately, many tech leaders do not take the time to reflect on whether the system actually makes sense. They shirk the responsibility given to them — to build an effective system — and instead &lt;/span&gt;&lt;span&gt;&lt;a href="https://www.linkedin.com/posts/udanium_designleadership-designethics-techculture-activity-7476357241084878848-h4Wa/?utm_campaign=even-with-a-silver-bullet-you-still-have-to-aim&amp;amp;utm_medium=referral&amp;amp;utm_source=productpicnic.beehiiv.com" target="_blank" rel=""&gt;whip the existing, faulty system&lt;/a&gt;&lt;/span&gt;&lt;span&gt; to run faster and faster, until all the wheels fall off:&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;❝&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;“Ship more faster now” is not a product strategy; it’s a psychosis. …&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;Reframe craft as care for collaborators, users, and the collective aspiration to create something useful, meaningful, and significant. &lt;/span&gt;&lt;span&gt;&lt;strong&gt;Craft is a social act&lt;/strong&gt;&lt;/span&gt;&lt;span&gt; that binds teams around shared values and takes time.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;But how do we make this reframe happen? How do we seize control of our discipline’s story?&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;That’s where my head’s been at for years — before the Picnic, before my work for NN/g, even before I started publishing with the UX Collective. But only lately have those thoughts come together into something actionable enough to share. I’ll be writing on this topic through July, but the capstone of these ideas will be my one-day workshop with Throughline on July 31st.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Flex those professional development budgets and join me in taking back control of the conversation. &lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;— Pavel at the Product Picnic&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name>Pavel Samsonov</name>
        </author>
        <media:content medium="image" url="https://beehiiv-images-production.s3.amazonaws.com/uploads/asset/file/8df7d862-87ad-43f0-91fe-72ed4d3b57a9/74.png?t=1782685044"/>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19eeb80c989:771fcb:d67a104c</id>
        <title type="html">untitled</title>
        <published>2026-06-21T18:45:46Z</published>
        <updated>2026-06-21T18:45:50Z</updated>
        <link href="https://thinkinglabs.io/articles/2026/06/20/beyond-shades-of-conways-law-validation.html" rel="alternate" type="text/html"/>
        <content type="html">&lt;div&gt;&lt;body&gt;&lt;div&gt;&lt;article&gt;
&lt;p&gt;Moving beyond hunch and silencing the sceptics. Over the years, Conway’s Law has resurfaced as a guiding principle to organise teams. Yet, many people still hesitate to accept the idea. Ultimately, it remains a hypothesis. Melvin Conway did not provide any proof when publishing the thesis. However, in the meantime, decades later, a good deal of academic research supports the thesis. Ultimately, Harvard and MIT researchers provided robust empirical validation for the hypothesis across the software industry, further confirmed by the CHAOS Report from the Standish Group, reinforcing &lt;em&gt;The Mirroring Principle&lt;/em&gt; as an undeniable organisational reality.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Henderson and Clark observed in 1990 that established firms are unable to adopt architectural innovation because the architectural knowledge is embedded in the communication structures of the organisation, preventing them from adopting new designs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We show that architectural innovations destroy the usefulness of the architectural knowledge of established firms, and that since architectural knowledge tends to become embedded in the structure and information-processing procedures of established organizations, this destruction is difficult for firms to recognize and hard to correct. Architectural innovation therefore presents established organizations with subtle challenges that may have significant competitive implications.&lt;/p&gt;
&lt;p&gt;– &lt;a href="http://www.iot.ntnu.no/innovation/norsi-pims-courses/tushman/Handerson%20&amp;amp;%20Clark%20(1990).pdf"&gt;Architectural Innovation: The Reconfiguration of Existing Product Technologies and the Failure of Established Firms&lt;/a&gt;, Henderson and Clark, 1990&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;“&lt;em&gt;Architectural innovation&lt;/em&gt;” is defined as innovations that change the way product components are linked together, while leaving the core design concepts — and thus the basic knowledge underlying the components — untouched. That does not mean that components remain untouched. Often, architectural innovation is triggered by a change in a component that triggers new interactions and connections with other components.&lt;/p&gt;
&lt;p&gt;The study’s outcome is based on the analysis of 20 years of data (1962-1986) from the photolithographic alignment equipment industry, where four organisations subsequently took over market leadership because a competitor uncovered an architectural innovation that the previous leader was unable to detect due to its organisational structure.&lt;/p&gt;
&lt;p&gt;There has been growing evidence of numerous technical innovations that involved apparent modest changes to existing technology, but that had dramatic competitive consequences. In the mid-1950s, RCA developed a prototype for a portable radio receiver. It utilised technology that RCA mastered well. Yet, they did not pursue the idea. Until Sony, a small, relatively new company, entered the US market with portable radio receivers. RCA remained a follower. The irony was that Sony produced radios during many years using RCA’s licensed technology. The case of Xerox, in the mid-1970s, although the inventor of the plain-paper copier, was, during eight years of strategic errors, unable to compete with competitors offering smaller, more reliable copiers. Xerox lost half of the market. For a more recent example, we look at the German car industry, which had trouble marketing electric cars until Tesla, and later the Chinese car manufacturers took over the market. Meanwhile, Audi closes its Belgian factory that produces the expensive electric SUV because of a market mismatch.&lt;/p&gt;
&lt;p&gt;This inability to adopt architectural innovation happens because an organisation organises itself around the product’s key components that make up the “&lt;em&gt;dominant design&lt;/em&gt;”. The “&lt;em&gt;dominant design&lt;/em&gt;” is characterised by a general acceptance of a particular product architecture. It incorporates a range of basic design choices that are not revisited — think car platforms, or in software, a CRUD design. It results in communication channels between the design teams that reflect the organisation’s knowledge about the critical interactions between these components. Thus, an organisation’s communication channels embody the organisation’s architectural knowledge that is critical to effective design. From this moment on, organisations incline to focus only on “&lt;em&gt;incremental innovation&lt;/em&gt;”, tending to reinforce the competitive positions of established firms as it builds on their core competencies.&lt;/p&gt;
&lt;p&gt;As such, the architectural knowledge is a mirror of the communication structures organised around the product being designed. Somehow, this is the knowledge parallel of Conway’s thesis “&lt;em&gt;[Any] organisation which designs systems is constrained to produce designs which are copies of the communication structures of the organisation&lt;/em&gt;”. If communication structures dictate design (Conway, 1968), they also dictate what an organisation is capable of knowing (Henderson and Clark, 1990).&lt;/p&gt;
&lt;p&gt;Henderson and Clark establish that, given how knowledge is organised in firms, learning about changes in the product architecture is unlikely to happen. It requires explicit management, or even a different organisation. To counter this, the authors suggest cross-functional teams as a possible response to the danger of architectural knowledge becoming embedded within tacit communication lines.&lt;/p&gt;
&lt;p&gt;Recently, I was front-seat observing an organisation unable to run only a simple experiment, which would let them eventually discover a potential innovation, merely because of the structures in place.&lt;/p&gt;
&lt;p&gt;Because the communication channels are “frozen” around the old product architecture, the knowledge is also frozen; the organisation cannot distinguish or adjust to a possible new architecture reality, or even conceive a new design reality. This is Conway’s Law working back over time (more on this in &lt;em&gt;Dynamics: Conway’s Time Component&lt;/em&gt;).&lt;/p&gt;
&lt;h2&gt;The Aircraft Engine/Automotive Validations (2004, 2007)&lt;/h2&gt;
&lt;p&gt;In 2004, Sosa et al. published a study that investigated the design process of a commercial aircraft jet engine, the PW4098, at Pratt &amp;amp; Whitney. They studied how design interfaces in the product architecture map onto communication patterns within the development organisation. More specifically, they explored whether organisational problems exist at the boundaries of the components inside the jet engine, i.e. whether a misalignment exists between the design team boundaries and system component interfaces.&lt;/p&gt;
&lt;p&gt;According to Henderson and Clarke, architectural knowledge is embedded in the communication structures of organisations; therefore, organisations need to understand how they manage embedded knowledge. This is especially relevant when designing new products. In this context, system design interfaces are the primary sources of team interdependence, or at least, they should be. Meaning, when a specific team designs their own component with an interface to a neighboring component, there should be a corresponding technical interaction between the two respective teams. These are the expected cases. On the other hand, unmatched design interfaces and unmatched team interactions are unexpected. As it seems, these cases were largely ignored in previous research.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;However, we believe that considering these cases is important because their existence would indicate that not all known product-related interdependences are addressed by direct technical communication, and that technical communication (where not expected) may uncover undocumented product-related interdependences.&lt;/p&gt;
&lt;p&gt;– &lt;a href="http://users.ece.utexas.edu/~perry/education/382v-s08/papers/sosa.pdf"&gt;The Misalignment of Product Architecture and Organizational Structure in Complex Product Development&lt;/a&gt;, Sosa et al, 2004&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Finally, the study concludes that …&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;… we provide empirical evidence that product ambiguity exists, and it is more likely to be present across organizational and system boundaries&lt;/p&gt;
&lt;p&gt;– &lt;a href="http://users.ece.utexas.edu/~perry/education/382v-s08/papers/sosa.pdf"&gt;The Misalignment of Product Architecture and Organizational Structure in Complex Product Development&lt;/a&gt;, Sosa et al, 2004&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Meaning, that there is a great likelihood that some of the system component interfaces are not covered by team interactions, which can lead to major setbacks involving significant rework that can be extremely costly. The paper, exactly, refers to two major setbacks during the design of that specific jet engine.&lt;/p&gt;
&lt;p&gt;What they are essentially saying here is: &lt;strong&gt;If there is a misalignment between the organisation and the product architecture, there will be problems&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In 2007, Gokpinar et al. conducted a study examining the design of cars in the automotive industry.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[…] a key challenge for product development organizations is matching the organization to the product being developed. This involves two fundamental problems: (1) How to assign people to the parts and subsystems that make up the product, and (2) How to ensure that people communicate/collaborate effectively in the performance of design tasks.&lt;/p&gt;
&lt;p&gt;– &lt;a href="https://citeseerx.ist.psu.edu/document?repid=rep1&amp;amp;type=pdf&amp;amp;doi=5553ae01b690837f755cd2c91fa6a4b7fdec6345"&gt;The Impact of Misalignment of Organization Structure and Product Architecture on Quality in Complex Product Development&lt;/a&gt;, Gokpinar et al, 2007&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The research defined a new metric &lt;em&gt;coordination deficit&lt;/em&gt;, which quantifies mismatches between product architecture and organisation structure, and investigated the effect of coordination deficit on product quality.&lt;/p&gt;
&lt;p&gt;They concluded that mismatches between product architecture and organisational structure are positively associated with quality problems.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Our results suggest that misalignment of the design organization with the product architecture negatively affects product quality”&lt;/p&gt;
&lt;p&gt;– &lt;a href="https://citeseerx.ist.psu.edu/document?repid=rep1&amp;amp;type=pdf&amp;amp;doi=5553ae01b690837f755cd2c91fa6a4b7fdec6345"&gt;The Impact of Misalignment of Organization Structure and Product Architecture on Quality in Complex Product Development&lt;/a&gt;, Gokpinar et al, 2007&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once more, &lt;strong&gt;if there is a misalignment between organisation and product architecture, there will be problems&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;The Micro Software Validation (2006)&lt;/h2&gt;
&lt;p&gt;In 2006, Cataldo et al. investigated the consequences of congruence on the speed and efficiency of software development work inside a single software project. They investigated the impact of misalignment.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is particularly important in product development organizations which organize themselves around their products’ architectures because the main components of their products define the organization’s key sub-tasks.&lt;/p&gt;
&lt;p&gt;– &lt;a href="https://www.cs.drexel.edu/~yfcai/CS680/Readings/Week8/Identification%20of%20Coordination%20Requirements.pdf"&gt;Identification of Coordination Requirements: Implications for the Design of Collaboration and Awareness Tools&lt;/a&gt;, Cataldo et al, 2006&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This matters because, as architectural knowledge is embedded in the communication structures of organisations (Henderson and Clark, 1990), any minor changes to the product architecture can disrupt the organisation’s ability to coordinate effectively.&lt;/p&gt;
&lt;p&gt;Ultimately, Cataldo et al. found that tasks were completed more rapidly when the patterns of communication between team members were congruent with the patterns of interdependency between components (more on &lt;em&gt;congruence&lt;/em&gt; in &lt;em&gt;Mechanics: The Mathematical &amp;amp; Geometrical Shades&lt;/em&gt;).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Congruence&lt;/strong&gt; between coordination requirements and coordination activities shortened development time.&lt;/p&gt;
&lt;p&gt;– &lt;a href="https://www.cs.drexel.edu/~yfcai/CS680/Readings/Week8/Identification%20of%20Coordination%20Requirements.pdf"&gt;Identification of Coordination Requirements: Implications for the Design of Collaboration and Awareness Tools&lt;/a&gt;, Cataldo et al, 2006&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Fundamentally, the research indicates, &lt;strong&gt;if teams are aligned with the components they work on, teams are more efficient&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;The Macro Software Validation (2012)&lt;/h2&gt;
&lt;p&gt;In 2012, we had the first empirical evidence for the software industry provided by the research paper &lt;a href="https://www.hbs.edu/ris/Publication%20Files/08-039_1861e507-1dc1-4602-85b8-90d71559d85b.pdf"&gt;Exploring the Duality between Product and Organizational Architectures: A Test of the “Mirroring” Hypothesis&lt;/a&gt; from Baldwin et al.&lt;/p&gt;
&lt;p&gt;One of their findings was: software products tend to mirror the structure of the organisation in which they are developed.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We find strong evidence to support the hypothesis that a product’s architecture tends to mirror the structure of the organization in which it is developed.&lt;/p&gt;
&lt;p&gt;– Exploring the Duality between Product and Organizational Architectures: A Test of the “Mirroring” Hypothesis, Baldwin, MacCormack and Rusnak, 2012&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt="Conway's Law Sketchplanations" src="https://thinkinglabs.io/images/shades-of-conways-law/sketchplanations-conways-law.jpg"&gt;&lt;/p&gt;
&lt;center&gt;&lt;i&gt;Source: Sketchplanations &lt;a href="https://sketchplanations.com/conways-law"&gt;https://sketchplanations.com/conways-law&lt;/a&gt;&lt;/i&gt;&lt;/center&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;To validate this hypothesis, they compared open-source with closed-source software. Based on the idea that the open-source communities are loosely coupled organisations by design because contributors have diverse goals, belong to different organisations, work in different geographical locations and time zones, and have no formal authority to guide development activities. Whereas closed-source organisations are more tightly-coupled, as they consist of smaller, co-located teams that share common goals, and are dedicated full-time to products and have formal decision-making authority to guide development.&lt;/p&gt;
&lt;p&gt;The study’s approach focused on comparing the architectures of open-source and closed-source software products that have similar functions and are of a similar size. They identified five software categories: financial management, word processing, spreadsheets, operating systems and databases. For each category, they selected an open-source and a closed-source software product.&lt;/p&gt;
&lt;p&gt;Because commercial firms are somehow reluctant to share their code base, especially for research comparing with “free” equivalents, researchers did not always have a closed-source codebase at their disposal. When commercial options were unavailable, they turned to formerly closed-source products that had been open-sourced. In those instances, they analysed the first open-source version.&lt;/p&gt;
&lt;p&gt;So they ended up comparing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For financial management: GnuCash vs MyBooks&lt;/li&gt;
&lt;li&gt;For word processing: Abiword vs StarWriter&lt;/li&gt;
&lt;li&gt;For spreadsheets: Gnumeric vs StarCalc&lt;/li&gt;
&lt;li&gt;For operating systems: Linux vs Solaris&lt;/li&gt;
&lt;li&gt;For databases: MySQL vs Berkeley DB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The paper primarily explores whether organisations with distinctly different forms develop products with distinctly different architectures.&lt;/p&gt;
&lt;p&gt;The outcome of the study was that &lt;strong&gt;loosely-coupled organisations, such as open-source, tend to develop products with a more modular architecture&lt;/strong&gt; than tightly-coupled organisations such as commercial firms. This confirms Parnas’ information hiding concept (Parnas 1972), which allows individual, isolated contributors to work independently. It also reaffirms Henderson and Clark’s observation. Loosely coupled, open-source organisations prevent architectural knowledge from becoming a central, siloed trap. They adopt cross-functional, loosely coupled teams to democratise knowledge. Tightly-coupled organisations, on the other hand, stumble when faced with innovations, because knowledge is tightly frozen, siloed, in the structures of the organisation.&lt;/p&gt;
&lt;h2&gt;The Industry Reality Check (1995-2020)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;We know why projects fail, we know how to prevent their failure – so why do they still fail?&lt;/p&gt;
&lt;p&gt;– Cobb’s Paradox, Martin Cobb, CIO for the Secretariat of Treasury Board of Canada, CHAOS University, 1995, published in &lt;a href="https://www.inf.ufpr.br/urban/2019-1_205_e_220/205e220_Ler_ver_para_complementar/StandishGroup__UnfinishedVoyages-I.pdf"&gt;Unfinished Voyages I&lt;/a&gt;, Standish Group, 1995&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When organisations keep treating software products as mere, rigid projects, the resulting architecture and outcomes for users will be notably poor, leading to surprising failures.&lt;/p&gt;
&lt;p&gt;From 1994 until 2020, the Standish Group published the CHAOS Report, a study on the success, failure, and challenge rates of IT projects.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Success&lt;/th&gt;
&lt;th&gt;Challenged&lt;/th&gt;
&lt;th&gt;Cancelled&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1994&lt;/td&gt;
&lt;td&gt;16%&lt;/td&gt;
&lt;td&gt;53%&lt;/td&gt;
&lt;td&gt;31%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2000&lt;/td&gt;
&lt;td&gt;28%&lt;/td&gt;
&lt;td&gt;49%&lt;/td&gt;
&lt;td&gt;23%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2002&lt;/td&gt;
&lt;td&gt;34%&lt;/td&gt;
&lt;td&gt;51%&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2004&lt;/td&gt;
&lt;td&gt;29%&lt;/td&gt;
&lt;td&gt;53%&lt;/td&gt;
&lt;td&gt;18%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2006&lt;/td&gt;
&lt;td&gt;35%&lt;/td&gt;
&lt;td&gt;46%&lt;/td&gt;
&lt;td&gt;19%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2008&lt;/td&gt;
&lt;td&gt;32%&lt;/td&gt;
&lt;td&gt;44%&lt;/td&gt;
&lt;td&gt;24%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2011&lt;/td&gt;
&lt;td&gt;39%&lt;/td&gt;
&lt;td&gt;39%&lt;/td&gt;
&lt;td&gt;22%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;37%&lt;/td&gt;
&lt;td&gt;46%&lt;/td&gt;
&lt;td&gt;17%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;41%&lt;/td&gt;
&lt;td&gt;40%&lt;/td&gt;
&lt;td&gt;19%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2014&lt;/td&gt;
&lt;td&gt;36%&lt;/td&gt;
&lt;td&gt;47%&lt;/td&gt;
&lt;td&gt;17%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2015&lt;/td&gt;
&lt;td&gt;36%&lt;/td&gt;
&lt;td&gt;45%&lt;/td&gt;
&lt;td&gt;19%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;31%&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;19%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;– source: CHAOS Reports 1995, 2009, 2015, 2020, Standish Group.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Although this may look baffling at first, this stagnation blindsides no experienced practitioners. The numbers reveal a flatline trend. Despite agile and DevOps, the success rate is capped at approximately 30%.&lt;/p&gt;
&lt;p&gt;Till 2015, the Standish Group focused mainly on classic project management requirements to measure success, such as well-defined upfront requirements, proper planning, skilled project managers, as well as user involvement and project sponsor. Success was defined in classic PMI terms, the &lt;a href="https://en.wikipedia.org/wiki/Project_management_triangle"&gt;Iron Triangle&lt;/a&gt;, i.e. on time, on budget, with all features as initially specified. Then, in 2015, a first significant shift happened. The success resolution definition changed. It was still on time and on budget, however, with satisfactory results. All requirements no longer have to be implemented, since data shows that some projects meet the Iron Triangle yet do not return value. In the end, satisfaction and value are greater when fewer features are delivered, but meeting obvious user needs. This is aligned with Lean principles, as well as another Standish Group study revealing &lt;a href="https://www.mountaingoatsoftware.com/blog/are-64-of-features-really-rarely-or-never-used"&gt;that 64% of delivered features are rarely used&lt;/a&gt;. Later confirmed by the 2019 &lt;a href="https://www.pendo.io/resources/the-2019-feature-adoption-report/"&gt;Pendo Feature Adoption Study&lt;/a&gt;, revealing 80% of features see low to zero adoption.&lt;/p&gt;
&lt;p&gt;By 2020, the Standish Group had pivoted and reported an astonishing outcome that contradicts their early recommendations. Stop treating software products as a project because, by definition, projects have a start and an end, but products do &lt;em&gt;not&lt;/em&gt;. They explicitly stated that &lt;a href="https://medium.com/leadership-and-agility/project-managers-fail-to-help-software-projects-standish-group-chaos-2020-e65d803e99f2"&gt;project managers harm software products&lt;/a&gt; and increase the failure rate. Specifically, the report demonstrates that project managers often generate more paperwork, resulting in slower decision-making that inevitably slows down feedback. While, once more, not surprising for experienced practitioners — it was already established long before, and brought to the public in 2017 with the &lt;a href="https://www.allankelly.net/archives/4796/what-ever-happened-to-noprojects-post-projects/"&gt;#NoProjects movement&lt;/a&gt; — the Standish Group brought it to the masses.&lt;/p&gt;
&lt;p&gt;Ultimately, the Standish Group concluded that project success is defined by &lt;em&gt;Good Sponsor&lt;/em&gt;, &lt;em&gt;Good Team&lt;/em&gt;, &lt;em&gt;Good Place&lt;/em&gt;, and &lt;em&gt;Short Decision Latency&lt;/em&gt;. To a large extent, this aligns with the 2001 findings from the book &lt;a href="https://app.thestorygraph.com/books/1771dbe4-16a3-4316-83a1-ed2b21984bc9"&gt;Good to Great&lt;/a&gt;. Organisations become great when they have &lt;em&gt;Level 5 Leadership&lt;/em&gt; (Good Sponsor) to build enduring greatness through a paradoxical blend of personal humility and professional will. They focus on &lt;em&gt;First Who … Then What&lt;/em&gt; (Good Team), first securing the right people before figuring out where to drive it, before vision, before strategy, before tactics, before organisational structure, before technology. Lastly, they &lt;em&gt;Confront the Brutal Facts of Reality&lt;/em&gt; (Good Place), by fostering a climate where the truth is heard.&lt;/p&gt;
&lt;p&gt;These crucial prerequisites enable &lt;em&gt;Short Decision Latency&lt;/em&gt;, allowing information to circulate rapidly. However, low decision latency is entirely dependent on communication paths. Since Conway’s Law dictates that systems reflect the organisation’s communication structures, high-latency organisations stuck in silos are unable to produce high-velocity, decoupled system designs. This architectural congestion hampers communication and impedes overall success.&lt;/p&gt;
&lt;p&gt;Ultimately, the CHAOS Report recommends moving away from “projects” and adopting Infinite Flow, which Lean has been promoting since the 1980’s. Somehow, this is the business world finally accepting what Baldwin et al. proved in 2012: tightly coupled organisations produce tightly coupled system architectures that work against flow, while loosely-coupled organisations thrive because they produce loosely coupled system architectures that enable flow, short time to markets, and consequently accelerated feedback to meet user expectations better. The 2020 CHAOS Report &lt;em&gt;Good Place&lt;/em&gt; — where truth is heard to reduce Decision Latency — is exactly the type of environment needed to break the “frozen”, siloed, tacit communication lines Henderson and Clark blamed for corporate blindness.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;To conclude, the tech industry spent decades trying to fix management processes, only to realise they were fighting a losing battle against the structural gravity of Conway’s Law and its implications on innovation and market leadership. Mediocrity is the default. Success is optional. Just saying …&lt;/p&gt;
&lt;h2&gt;The Series: Navigating the Shades&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://thinkinglabs.io/articles/2026/04/24/beyond-the-shades-of-conways-law.html"&gt;Beyond the Shades of Conway’s Law series&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://thinkinglabs.io/articles/2026/06/07/beyond-shades-of-conways-law-foundations.html"&gt;Foundations: The Origin &amp;amp; The Mirroring Principle&lt;/a&gt; - How the worlds of organisation and product design observed the same thesis independently.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validation: The Research &amp;amp; Reality Check&lt;/strong&gt; - Moving beyond the “hunch”, how researchers proved the Law in different industries, but especially in software.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mechanics: The Mathematical &amp;amp; Geometrical Shades&lt;/strong&gt; - The geometry of design: from mathematical isomorphism, homomorphism, congruence to compatibility.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strategy: Reversing the Law&lt;/strong&gt; - How the system ultimately forces the organisation to change versus deliberately changing the organisation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scale: Conway’s Corollary&lt;/strong&gt; - The required organisational flexibility.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamics: Conway’s Time Component&lt;/strong&gt; - The “Engineer Half Life” and why architecture is “sticky” long after teams change.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conclusion: The Different Lenses&lt;/strong&gt; - A concluding look at how we perceive organisations and their systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Bibliography&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.melconway.com/Home/Committees_Paper.html"&gt;How Do Committees Invent?&lt;/a&gt;, Melvin Conway, 1968&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.win.tue.nl/~wstomv/edu/2ip30/references/criteria_for_modularization.pdf"&gt;On the Criteria To Be Used in Decomposing Systems into Modules&lt;/a&gt;, Parnas, 1972&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.iot.ntnu.no/innovation/norsi-pims-courses/tushman/Handerson%20&amp;amp;%20Clark%20(1990).pdf"&gt;Architectural Innovation: The Reconfiguration of Existing Product Technologies and the Failure of Established Firms&lt;/a&gt;, Henderson and Clark, 1990&lt;/li&gt;
&lt;li&gt;&lt;a href="https://personal.utdallas.edu/~chung/SYSM6309/chaos_report.pdf"&gt;CHAOS Report 1995&lt;/a&gt;, Standish Group, 1995&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.inf.ufpr.br/urban/2019-1_205_e_220/205e220_Ler_ver_para_complementar/StandishGroup__UnfinishedVoyages-I.pdf"&gt;Unfinished Voyages I&lt;/a&gt;, Standish Group, 1995&lt;/li&gt;
&lt;li&gt;&lt;a href="https://app.thestorygraph.com/books/1771dbe4-16a3-4316-83a1-ed2b21984bc9"&gt;Good to Great&lt;/a&gt;, Jim Collins, 2001&lt;/li&gt;
&lt;li&gt;&lt;a href="http://users.ece.utexas.edu/~perry/education/382v-s08/papers/sosa.pdf"&gt;The Misalignment of Product Architecture and Organizational Structure in Complex Product Development&lt;/a&gt;, Sosa et al, 2004&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cs.drexel.edu/~yfcai/CS680/Readings/Week8/Identification%20of%20Coordination%20Requirements.pdf"&gt;Identification of Coordination Requirements: Implications for the Design of Collaboration and Awareness Tools&lt;/a&gt;, Cataldo 2006&lt;/li&gt;
&lt;li&gt;&lt;a href="https://citeseerx.ist.psu.edu/document?repid=rep1&amp;amp;type=pdf&amp;amp;doi=5553ae01b690837f755cd2c91fa6a4b7fdec6345"&gt;The Impact of Misalignment of Organization Structure and Product Architecture on Quality in Complex Product Development&lt;/a&gt;, Gokpinar et al, 2007&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.classes.cs.uchicago.edu/archive/2014/fall/51210-1/required.reading/Standish.Group.Chaos.2009.pdf"&gt;CHAOS Report 2009&lt;/a&gt;, Standish Group, 2009&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.hbs.edu/ris/Publication%20Files/08-039_1861e507-1dc1-4602-85b8-90d71559d85b.pdf"&gt;Exploring the Duality between Product and Organizational Architecture: A Test of the “Mirroring” Hypothesis&lt;/a&gt;, Baldwin, MacCormack, Rusnak, 2012&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cdn1-public.infotech.com/agile/CHAOSReport2015-Final.pdf"&gt;CHAOS Report 2015&lt;/a&gt;, Standish Group, 2015&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.infoq.com/articles/standish-chaos-2015/"&gt;Standish Group 2015 Chaos Report - Q&amp;amp;A with Jennifer Lynch&lt;/a&gt;, InnoQ, 2015&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mountaingoatsoftware.com/blog/are-64-of-features-really-rarely-or-never-used"&gt;Are 64% of Features Really Rarely or Never Used?&lt;/a&gt;, Mike Cohn, 2016&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pendo.io/resources/the-2019-feature-adoption-report/"&gt;The 2019 Feature Adoption Report&lt;/a&gt;, Pendo, 2019&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thestory.is/en/journal/chaos-report/"&gt;Chaos Report — why this study about IT project management is so unique&lt;/a&gt; about the CHAOS Report 2020, Radek, The Story, 2024&lt;/li&gt;
&lt;/ul&gt;
&lt;/article&gt;
&lt;/div&gt;&lt;/body&gt;&lt;/div&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://thinkinglabs.io/images/shades-of-conways-law/sketchplanations-conways-law.jpg"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://thinkinglabs.io/feed.xml</id>
            <title type="html">ThinkingLabs:: Thierry de Pauw</title>
            <link href="https://thinkinglabs.io" rel="alternate" type="text/html"/>
            <updated>2026-06-21T18:45:50Z</updated>
        </source>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19ec6e6229f:633c44:5df5eb75</id>
        <title type="html">untitled</title>
        <published>2026-06-14T16:10:31Z</published>
        <updated>2026-06-14T16:10:37Z</updated>
        <link href="https://www.funnyshit.com.au/the_plan.html" rel="alternate" type="text/html"/>
        <content type="html">&lt;div&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;h1&gt;The Plan&lt;/h1&gt;
&lt;p&gt;In the beginning was the Plan.&lt;/p&gt;
&lt;p&gt;And then came the Assumptions.&lt;/p&gt;
&lt;p&gt;And the Assumptions were without form.&lt;/p&gt;
&lt;p&gt;And darkness was upon the face of the Workers.&lt;/p&gt;
&lt;p&gt;And they spoke among themselves, saying, "It is a crock of shit, and it
stinketh."&lt;/p&gt;
&lt;p&gt; And the workers went unto their Supervisors and said, "It is a pail of
dung, and none may abide the odour thereof."&lt;/p&gt;
&lt;p&gt; And the Supervisors went unto their Managers, saying, "It is a container
of excrement, and it is very strong, such that none may abide by it."&lt;/p&gt;
&lt;p&gt; And the Managers went unto their Directors, saying, "It is a vessel of
fertiliser, and none may abide its strength."&lt;/p&gt;
&lt;p&gt; And the Directors spoke amongst themselves, saying one to another, "It
contains that which aids plant growth, and it is very strong."&lt;/p&gt;
&lt;p&gt;And the Directors then went onto the Vice Presidents, saying unto them, "It
promotes growth and is very powerful."&lt;/p&gt;
&lt;p&gt;And the Vice Presidents went unto the President, saying unto him, "This
new plan will actively promote the growth and vigour of the company; with powerful
effects."&lt;/p&gt;
&lt;p&gt;And the President looked upon the Plan, and saw that it was good.&lt;/p&gt;
&lt;p&gt;And the Plan became Policy.&lt;/p&gt;
&lt;p&gt;This is How Shit Happens.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.funnyshit.com.au/"&gt;→ Check out more Funny Shit&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/body&gt;&lt;/div&gt;</content>
        <author>
            <name/>
        </author>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19ec4cf4358:49c447:b0e60e29</id>
        <title type="html">untitled</title>
        <published>2026-06-14T06:26:18Z</published>
        <updated>2026-06-14T06:26:22Z</updated>
        <link href="https://www.psychologytoday.com/gb/blog/creating-successful-change/202606/why-its-so-hard-to-know-if-youre-really-making-progress" rel="alternate" type="text/html"/>
        <content type="html">&lt;div&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;
&lt;p&gt;Think about the last time you tracked your progress on a personal goal. Maybe you were trying to exercise more frequently, be more patient with a spouse or partner, or learn a new skill. Chances are, you had a rough sense of how it was going. And chances are that sense was more &lt;a hreflang="en" href="https://www.psychologytoday.com/gb/basics/optimism" title="Psychology Today looks at optimistic"&gt;optimistic&lt;/a&gt; than the evidence warranted.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This is not a character flaw. It is one of the most reliable findings in behavioral science: We are systematically poor judges of our own progress, and during periods of active change, we tend to get worse.&lt;/p&gt;
&lt;p&gt;One reason is what psychologists Terence Mitchell and Leigh Thompson at the University of Washington termed rosy retrospection. This is our consistent tendency to remember past experiences more favorably than we rated them in the moment. When we look back over a month of trying to exercise more or sleep differently, our &lt;a hreflang="en" href="https://www.psychologytoday.com/gb/basics/memory" title="Psychology Today looks at memory"&gt;memory&lt;/a&gt; selectively surfaces the days we succeeded. The days we failed are there, but they are not as memorable. We feel we’ve been doing better than we really have.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;A second explanation is subtler. &lt;a hreflang="en" href="https://www.psychologytoday.com/gb/basics/motivation" title="Psychology Today looks at Motivation"&gt;Motivation&lt;/a&gt; isn’t a fixed state; it shifts gradually, often well before behavior changes in any visible way. The enthusiasm that felt solid in January has usually started to erode by March. But because the erosion is incremental, we rarely catch it in real time. We trust that how we feel today is roughly how we’ve been feeling all along. It almost never is.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Together, these two patterns create a specific failure mode: We assume the feedback loop is running when it isn’t. We trust that because we know our own goals and intentions, we know how we’re actually doing. But intention and behavior are not the same thing. How we feel about a change is not the same as how we’re enacting it. And until we close that gap with something more rigorous than a gut check, we’re optimizing for a version of our progress that may not exist.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;In researching what separates change successes from failures at organizations for my book, my coauthors and I found that leaders designing change were consistently more confident about employee progress on change than the employees’ actual behavior or stated experience supported. They trusted their read on the room. The data, when they gathered it, told a different story. The wider the gap, the more likely the change failure.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The same gap operates in personal change. And the same remedy applies: Use measurement, not instinct.&lt;/p&gt;
&lt;p&gt;This doesn’t require an elaborate system. Research by James Pennebaker at the University of Texas at Austin shows that the act of writing specifically about your experience—not vague journaling, but naming exactly what you did, how it felt, and where the friction was—produces measurable improvements in self-understanding and in the likelihood of follow-through. Writing forces the brain to translate impression into information. The gap between what you felt and what actually happened becomes clearer.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Social accountability produces a related effect. A study by psychologist Gail Matthews at Dominican University found that people who wrote down their goals and reported progress to a friend were 42 percent more likely to achieve those goals than people who kept them private. Part of what accountability does is discipline the story you tell yourself. You can’t smooth over the gaps as easily when someone else is watching for them.&lt;/p&gt;

&lt;p&gt;Both interventions do the same thing: They replace vague impressions with actual information. When people begin gathering that information honestly, they usually discover not only the gaps in their efforts, but also the progress they had stopped noticing.&lt;/p&gt;
&lt;p&gt;Teresa Amabile, a psychologist at Harvard Business School, spent years studying what helps people stay motivated during sustained effort. Her research, drawn from the daily diaries of 238 professionals tracked over several months, produced a counterintuitive finding: The single biggest day-to-day motivator wasn’t recognition from a manager or a meaningful reward. It was the perception of making progress on work that mattered. Small wins, when noticed specifically, produced outsized improvements in engagement and follow-through. The operative word is “noticed.” Progress that goes unobserved doesn’t produce the same effect. It needs to be named to become fuel.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This is what honest self-assessment makes possible. When you journal about your week in specific detail, you often discover you accomplished much less than your vague, optimistic impression suggested—and you'll have a record you can point to as concrete evidence. That specificity matters more than it might seem. Ayelet Fishbach at the University of Chicago Booth School of Business has shown across repeated experiments that concrete progress feedback strengthens goal commitment more than encouragement does. What keeps people going isn’t being told they’re doing well. It’s seeing, with some precision, that they truly are.&lt;/p&gt;
&lt;div&gt;
&lt;p&gt;Motivation Essential Reads&lt;/p&gt;

&lt;/div&gt;
&lt;p&gt;The story you’re telling yourself about your progress probably has some blind spots. But it may also be overlooking real growth that felt too small or too ordinary to count. Learning to see your progress more clearly, without judgment, isn’t self-&lt;a hreflang="en" href="https://www.psychologytoday.com/gb/basics/punishment" title="Psychology Today looks at punishment"&gt;punishment&lt;/a&gt;. It’s an invaluable form of self-support.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/body&gt;&lt;/div&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="http://www.googletagmanager.com/ns.html?id=GTM-NW5PKS"/>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19eb821c9a4:1c44b1:ac72f017</id>
        <title type="html">untitled</title>
        <published>2026-06-11T19:21:22Z</published>
        <updated>2026-06-11T19:21:25Z</updated>
        <link href="https://mixpanel.com/blog/ai-adoption-gap/" rel="alternate" type="text/html"/>
        <content type="html">&lt;div&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Chances are you used an AI tool before 9 a.m. today. Chances are most of your users didn't.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;It's a simple contrast, but Debbie McMahon, VP of Product at &lt;a rel="noreferrer noopener" href="https://www.loveholidays.ie/"&gt;Loveholidays&lt;/a&gt;, built an entire &lt;a href="https://mixpanel.com/mxp/mxp-2026"&gt;MXP London&lt;/a&gt; session around it and what it means for every product decision. She called it the AI adoption gap, and she was refreshingly clear-eyed about the ways it had distorted her own judgment.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;&lt;span&gt;The AI adoption gap and the 0.04%&lt;/span&gt;&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;An infographic Debbie shared on stage breaks the world's relationship with AI into four tiers:&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;ul&gt;&lt;span&gt;&lt;li&gt;&lt;strong&gt;84%&lt;/strong&gt; of people have never used AI&lt;/li&gt;&lt;li&gt;&lt;strong&gt;16%&lt;/strong&gt; have tried a free chatbot&lt;/li&gt;&lt;li&gt;&lt;strong&gt;0.3%&lt;/strong&gt; pay for AI&lt;/li&gt;&lt;li&gt;&lt;strong&gt;0.04%&lt;/strong&gt; are actively building with it&lt;/li&gt;&lt;/span&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;That last group is the one that fills rooms like MXP London. And Debbie's point is that this creates a fundamental disconnect. The people designing AI-powered products are a statistical anomaly relative to the people they're designing for.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;She made that concrete with a story about her friend Agnes. Agnes is a primary school teacher who spends her days helping children from difficult family circumstances settle into school. She's smart, busy, and has a free ChatGPT account she’s never used. Why? Her school doesn't allow AI tools on work devices. The budget isn't there for anything paid. And nobody on staff is confident enough with the technology to know where to start, even if the first two problems disappeared tomorrow.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Agnes is not an edge case. She's a typical Loveholidays customer, and she's who Debbie asked the MXP London audience to hold in mind.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;



&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;span&gt;“&lt;/span&gt;
&lt;p&gt;We're living in a bubble. We're the 0.04% and our daily experience of AI has essentially nothing to do with our users’ reality.&lt;span&gt;”&lt;/span&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p&gt;Debbie McMahon&lt;/p&gt;
&lt;p&gt;VP of Product, Loveholidays&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;em&gt;For a look at how AI engagement is trending across real products, Mixpanel's &lt;a rel="noreferrer noopener" href="https://mixpanel.com/blog/ai-benchmarks-2026/"&gt;2026 AI benchmarks&lt;/a&gt; draw on nearly 290 billion AI events across 2.6 billion devices, and the picture is more nuanced than adoption headlines suggest.&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;&lt;span&gt;Three ways the bubble distorts your decisions&lt;/span&gt;&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Debbie named three patterns she sees in herself and across the industry.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;strong&gt;Over-prioritizing AI features.&lt;/strong&gt; "Have you put something out there just because it was easy to do?" She raised her own hand. The pressure is real: boards want AI in the roadmap, investors want AI in the roadmap, competitors are shipping. And the tooling makes it fast to build something. But fast to build isn't the same as worth building.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;strong&gt;Underestimating friction.&lt;/strong&gt; Most people are forming their views on AI right now without the benefit of product communities or conference sessions to help them process it. That's not a knock on users. They're figuring out whether to trust the technology at the same moment they're being asked to use it. Dropping a disclaimer like "generated by AI, so may not be accurate" into a set of results doesn't help someone who's already uncertain about the output.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;strong&gt;Asking users to change behavior.&lt;/strong&gt; Behavior change is extraordinarily difficult even when people want to change. For users with an ingrained habit and no clear reason to do anything differently, it's close to impossible. The customers who are confused or skeptical about AI aren't going to reroute their entire search behavior for a feature they weren't asking for. If what they want is a keyword search and a filter, that's what they'll reach for.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;

&lt;div&gt;
&lt;div&gt;
&lt;p&gt;Go deeper&lt;/p&gt;
&lt;p&gt;See whether your AI features are delivering value, or just getting used:&lt;/p&gt;


&lt;/div&gt;
&lt;/div&gt;
&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;&lt;span&gt;What the data actually showed&lt;/span&gt;&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Two case studies from Loveholidays that Debbie shared with the kind of candor that makes MXP sessions worth attending.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;strong&gt;The room grouping problem.&lt;/strong&gt; Hotel listings at Loveholidays had accumulated a long tail of duplicated, poorly named room types, a messy, years-old problem that had resisted every previous attempt to fix it. When a team went after it with AI, they went in with confidence. What they found was that the problem was deterministic: two rooms are either the same or they're not, and AI isn't built for that kind of precision. The model grouped rooms in ways that didn't hold up to scrutiny, renamed options customers recognized, and quietly dropped listings that people had been booking. The output looked coherent, but it wasn't. &lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;In Debbie’s own words: "Don't put AI on top of a broken thing. Fix the actual problem."&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;strong&gt;The Q&amp;amp;A tool.&lt;/strong&gt; Another team shipped an LLM-based Q&amp;amp;A feature into the pre-booking journey, built around the hypothesis that customers comparing hotels would want to ask questions. The feature got used, a lot, actually. But when the team looked at who was using it, the data didn't match the hypothesis at all.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;The users weren't pre-booking customers weighing their options. They were people who had already made a booking, navigating a long and confusing post-booking experience to find basic information about their upcoming trip. And the questions they were asking weren't the ones the team had imagined. The top two were whether their room came with a kettle and whether it had an iron. Third most common: the wi-fi password.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;In other words, customers who needed information Loveholidays already had were threading through a complex AI-powered feature just to ask if they needed to pack a travel iron. The team proposed moving the tool into the post-booking journey where the actual demand was. Debbie shut it down, and the reasoning is worth sitting with.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;



&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;span&gt;“&lt;/span&gt;
&lt;p&gt;Just because it does answer those users’ questions doesn't mean that this is the right solution. We have that information. It’s a fact. We just need to give it to them in a form that they need it in.&lt;span&gt;”&lt;/span&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p&gt;Debbie McMahon&lt;/p&gt;
&lt;p&gt;VP of Product, Loveholidays&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;The post-booking journey got fixed instead, and the Q&amp;amp;A tool got pulled. The story doesn't end there: the team is relaunching the feature with a genuinely different hypothesis behind it. Since that original experiment, Loveholidays launched its own reviews platform, which changes what a Q&amp;amp;A tool can meaningfully do and who it's actually for. There's a real use case now tied to a specific user group, and that's what "not now" looks like when it's used well.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;

&lt;div&gt;
&lt;div&gt;
&lt;p&gt;Go deeper&lt;/p&gt;
&lt;p&gt;Further reading on building and measuring product adoption in the AI era:&lt;/p&gt;


&lt;/div&gt;
&lt;/div&gt;
&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;&lt;span&gt;Building different journeys for different people&lt;/span&gt;&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Loveholidays went to their customers directly and asked how they'd feel about AI-mediated booking experiences. The words that came back included "anxiety," "I trust filters more," and "I prefer clicking." There was also genuine enthusiasm from a portion of respondents. But the skepticism was real, present across a significant share of the audience, and Debbie didn't try to explain it away.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;&lt;a rel="noreferrer noopener" href="https://www.gartner.com/en/newsroom/press-releases/2025-09-03-gartner-survey-finds-53-percent-of-consumers-distrust-ai-powered-search-results0"&gt;53% of consumers distrust AI-powered search results&lt;/a&gt;, according to a Gartner survey published in September 2025. Debbie cited a similar figure on stage and her read was plain: "I can't ignore that. That’s the reality for my customers, and that's where we're starting from."&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Her answer is to segment more precisely. AI comfort and interest now sit alongside demographics, holiday type, and referral route as a dimension Debbie's team uses to personalize the experience. Design one AI-mediated journey for everyone and you either hold back the users who want to explore new things or push unwanted change onto the customers who aren't ready for it. &lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Debbie's dad started using email about a decade after she did. She uses that as a reference point: adoption follows its own timeline, and you can't compress it by shipping a feature and calling it done.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;&lt;span&gt;The playground: A structural answer&lt;/span&gt;&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Recognizing a problem clearly is useful. Building an organizational system to address it at scale is something else. That's what separates this MXP session from a general mindset piece.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Debbie's team built what she calls "out the bubble," an internal prototyping environment that anyone at Loveholidays with access to Codex or Claude Code can use, with no prior technical setup required. It's pre-integrated with the company's design system, data mesh, live pricing, and real images, which matters more than it might sound. When you put a prototype in front of a customer in a travel context, the first thing they notice is whether the hotel price looks real. This system removes that distraction so the feedback is about the experience itself.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;The pipeline works in three stages:&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;ul&gt;&lt;span&gt;&lt;li&gt;Anyone at the company builds a prototype, sometimes in 10 minutes, sometimes a couple of hours, and pushes it to an internal testing environment for NMD review.&lt;/li&gt;&lt;li&gt;Prototypes that clear that bar are automatically routed to Usertesting.com, with the builder setting up the user panel themselves, no dependency on a separate research team.&lt;/li&gt;&lt;li&gt;The ones that pass user testing move to a small live test, pushed out via socials, CRM, or the homepage to a defined customer segment.&lt;/li&gt;&lt;/span&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;The important shift isn't the speed, though. It's that the people with the best product ideas are no longer gated by their ability to build prototypes. The system makes that capability available to the whole company. Fewer ideas get duplicated. More of them get in front of real customers. And when something earns the full investment of a real build, there's actual evidence behind the decision.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;&lt;span&gt;Start with the problem. The tools will follow.&lt;/span&gt;&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Debbie closed by revisiting the Henry Ford quote, carefully, because she's not entirely sure Ford said it. But the argument holds regardless. The car achieved adoption because Ford understood what his customers actually needed. He built a faster horse, not a different animal entirely.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;



&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;span&gt;“&lt;/span&gt;
&lt;p&gt;AI doesn't let us skip understanding what our users need. If you want people to adopt things, you have to start with their problem.&lt;span&gt;”&lt;/span&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p&gt;Debbie McMahon&lt;/p&gt;
&lt;p&gt;VP of Product, Loveholidays&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;The tools have changed. The product job hasn't. What AI does change is how big your answers can be once you've understood the question clearly. The potential is genuinely extraordinary, but only after you've done the harder work of understanding what your users need and why they'd change.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;Remember Agnes from the opening of this piece? The primary school teacher who supports children through difficult family circumstances, has a ChatGPT account she never uses, and has no budget, no tech support, and no time to figure it out herself. She's never going to ask for an AI-powered solution to her problems. But she'd benefit enormously if someone took the time to understand those problems and built something that addressed them, with AI as part of the answer rather than the starting point. &lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;That's what good product work looks like, and as Debbie put it, that's always been the job.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;&lt;span&gt;Build the bridge your users are ready to cross&lt;/span&gt;&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;The product teams pulling ahead right now aren't the ones shipping the most AI features. They're the ones who understand the gap between their own relationship with AI and their users', and who've built the systems to keep closing it.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;span&gt;To understand what your users are doing in your product, &lt;a rel="noreferrer noopener" href="https://mixpanel.com/ai"&gt;Mixpanel AI&lt;/a&gt; is built to surface that signal proactively, so the insight finds you rather than the other way around. For technical teams who want to go further, &lt;a rel="noreferrer noopener" href="https://mixpanel.com/blog/mixpanel-headless/"&gt;try Mixpanel Headless&lt;/a&gt; where AI agents read and drive Mixpanel programmatically.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;/body&gt;&lt;/div&gt;</content>
        <author>
            <name/>
        </author>
    </entry>
    <entry>
        <id>tag:feedly.com,2013:cloud/entry/cOXRjoksGIQzU1pdqbdybn+OTWYeI0riptNneGckdsI=_19e83d95682:e7fa0:6797f8b7</id>
        <title type="html">Social RSS (?)</title>
        <published>2026-06-01T15:41:59Z</published>
        <updated>2026-06-01T15:42:03Z</updated>
        <link href="https://chriscoyier.net/2026/06/01/social-rss-2/" rel="alternate" type="text/html"/>
        <summary type="html">What’s up with this? The spirit, of course, is “I want my Google Reader back.” I should be careful though: I imagine if we really did have it back exactly as it was, it would prob…</summary>
        <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;
        
&lt;p class="wp-block-paragraph"&gt;What’s up with this?&lt;/p&gt;



&lt;figure class="wp-block-embed is-type-rich is-provider-bluesky-social wp-block-embed-bluesky-social"&gt;&lt;/figure&gt;&lt;p class="wp-block-paragraph"&gt;The spirit, of course, is “I want my Google Reader back.” &lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;I should be careful though: I imagine if we really did have it back exactly as it was, it would probably not be as cool as we remember. That was a long-ass time ago and web software just wasn’t as nice as it is now. But: it &lt;em&gt;really did&lt;/em&gt; have social features in it! I could leave comments and see comments from my friends. (How “friends” worked back then, I scarcely remember.)&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;I’m not alone in wanting this. Here’s what I’ve seen so far.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;There is &lt;a href="https://skyreader.app/"&gt;Skyreader&lt;/a&gt;, and it looks like Tim Disney is still actively working on it:&lt;/p&gt;



&lt;figure class="wp-block-embed is-type-rich is-provider-bluesky-social wp-block-embed-bluesky-social"&gt;&lt;/figure&gt;&lt;p class="wp-block-paragraph"&gt;In my mind (and perhaps not reality?) Skyreader solves a “cold start” problem where signing up for a new social site is a lonely bummer because you don’t have any friends. Well, if all my Bluesky friends were automatically my friends on Skyreader, wouldn’t that be, like, good? &lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;My problem is… I don’t… get it. &lt;/p&gt;



&lt;ul class="wp-block-list"&gt;&lt;li&gt;Maybe there aren’t really “friends” on Skyreader? If there are, I can’t really find a list of them. If there aren’t, I guess this isn’t what I’m looking for. Or is it just assumed they are the same as Bluesky friends? I dunno, I think I have a lot to learn about this AT Proto stuff. &lt;/li&gt;



&lt;li&gt;What happens when I share an article? Does it share on my Bluesky? It doesn’t look like it. Who sees my shared articles then? My friends, if there are such a thing? Other people who explicitly subscribe? How would anyone figure out how to do that?&lt;/li&gt;



&lt;li&gt;It looks like I can share and I can highlight stuff specifically in what I share. Who sees the highlight? I imagine they need to use Skyreader to see that highlight? Or does it republish as RSS somehow?&lt;/li&gt;



&lt;li&gt;No comments… right? That’s weird to me for a social feed reader. What I think of when I think &lt;strong&gt;social feed reader&lt;/strong&gt; is 90% writing and reading my friends comments.&lt;/li&gt;



&lt;li&gt;The UI is confusing to me. I don’t love the expanding list format that jumps around a lot.&lt;/li&gt;
&lt;/ul&gt;&lt;p class="wp-block-paragraph"&gt;The main reason I can’t “really use it,” though, is that it doesn’t sync with Feedbin. I understand that’s a niche concern, but I need &lt;em&gt;one&lt;/em&gt; canonical feed source, so everything I’m subscribed to is in one place, and how much I’ve read is also canonical. That might be antithetical to AT Proto? I have no idea.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://indieweb.org/social_reader"&gt;Indieweb has their own version&lt;/a&gt; of all this, but it’s seriously no joke getting involved. From one of the reader sites, &lt;a href="https://alltogethernow.io/"&gt;Togther&lt;/a&gt;:&lt;/p&gt;



&lt;blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"&gt;
&lt;p class="wp-block-paragraph"&gt;What You Need&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;#1 Your own website&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;On the IndieWeb your website is your identity. It doesn’t need to be advanced, it doesn’t need to be pretty, it just needs to be &lt;strong&gt;yours&lt;/strong&gt;.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;#2 IndieAuth&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://indieweb.org/indieauth"&gt;IndieAuth&lt;/a&gt; is a technology to allow you to sign into services using only your website.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;#3 A MicroSub Server&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://indieweb.org/microsub"&gt;MicroSub&lt;/a&gt; is where the real magic happens. There are 2 parts; the server and the client. Together is the client, but it needs a server to go along with it. The server does a lot of tricky work such as fetching &amp;amp; parsing feeds as well as keeping track of what you have read and how you have everything organized. If you don’t already have a MicroSub server then check out &lt;a href="https://aperture.p3k.io/"&gt;Aperture&lt;/a&gt;.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;#4 MicroPub (optional)&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://indieweb.org/micropub"&gt;MicroPub&lt;/a&gt; is an optional but very cool piece of the puzzle. If your own website has MicroPub functionality then you can use Together not just to read content, but also to reply and create your own content. You can like, repost and reply to stuff you are following, you can even write full on blog posts from inside Together.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;p class="wp-block-paragraph"&gt;I mean, I’m not above some nerd hackin’, but this is 100% not ever going to be a place with a critical mass of my friends just chillin’ and sharing/commenting on blog posts. Way too hard.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;The closest thing I’ve seen is &lt;a href="https://www.commonrss.com/"&gt;CommonRSS&lt;/a&gt; by Brad Coffield which was build in a direct response to my original Bluesky post.&lt;/p&gt;



&lt;figure class="wp-block-embed is-type-rich is-provider-bluesky-social wp-block-embed-bluesky-social"&gt;&lt;/figure&gt;&lt;p class="wp-block-paragraph"&gt;I originally had a gut reaction to the vibe coding nature of it. Like &lt;em&gt;I don’t wanna use this thing you slopped together over a weekend, you gotta get more serious about it first.&lt;/em&gt; Which I admit is just a gut reaction and not a fair assessment of the software. &lt;a href="https://bsky.app/profile/studio303.bsky.social/post/3mhsvtzd5mk2r"&gt;Brad does seem quite sincere&lt;/a&gt; about making it a thing. But at the same time, it feels like it’s been months without updates, which is a bad sign for &lt;em&gt;super brand new&lt;/em&gt; software.&lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;In testing it lately…&lt;/p&gt;



&lt;ul class="wp-block-list"&gt;&lt;li&gt;It looks great, love the design — but it feels quite slow across the board.&lt;/li&gt;



&lt;li&gt;The Feedbin sync (yay!) didn’t work at first, but appears to be working now.&lt;/li&gt;



&lt;li&gt;It now claims to have 2-way syncing (yay!) but that doesn’t work for me. The articles I read aren’t read in feedbin. The stars don’t sync.&lt;/li&gt;



&lt;li&gt;It’s got the cold-start social problem: there isn’t anyone to follow. The discover page only lists Brad, and clicking the follow button from him doesn’t work.&lt;/li&gt;
&lt;/ul&gt;&lt;p class="wp-block-paragraph"&gt;It feels like it’s close, but it needs to work better. The commenting feature (again, like my main thing for wanting this) requires you to highlight some of the post first, then you can comment, but the comment won’t save. &lt;/p&gt;



&lt;figure class="wp-block-image size-full is-resized"&gt;&lt;img src="https://i0.wp.com/chriscoyier.net/wp-content/uploads/2026/06/Screenshot-2026-06-01-at-8.06.26-AM.png?resize=666%2C678&amp;amp;ssl=1" alt="Screenshot of a user interface with a text box for notes, showcasing a message about a group walk and an error related to JSON input." class="wp-image-13628"&gt;&lt;/figure&gt;&lt;p class="wp-block-paragraph"&gt;CommonRSS feels like a solid concept, but it’s both got the cold-start thing in terms of the social network, but also the cold-start thing in terms of a business. My guess is it’s hard for Brad to muster a ton of enthusiasm to work on the project all day every day with low users. &lt;/p&gt;



&lt;hr class="wp-block-separator has-alpha-channel-opacity"&gt;&lt;p class="wp-block-paragraph"&gt;I’m thinking the main problem is that there just isn’t much of a business to be built around RSS. It can build boutique one-person companies with a passion for it, but even then, difficult. &lt;/p&gt;



&lt;p class="wp-block-paragraph"&gt;On top of that, maybe there just isn’t much appetite for the social part? There are a number of arguably already-decently-successful boutique RSS apps, so why haven’t they done it? Why hasn’t Feedbin done anything terribly social? Why not Feedly? Remember FlipBoard’s kind weird taking on RSS? Now they have &lt;a href="https://about.surf.social/"&gt;an equally weird spinoff&lt;/a&gt;. My guess is it’s just too hard of work for too little payoff.&lt;/p&gt;

      &lt;/div&gt;

      
    &lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;</content>
        <author>
            <name/>
        </author>
        <media:content medium="image" url="https://jetpack.com/redirect/?source=sigenerate&amp;query=t%3DeyJpbWciOiJodHRwczpcL1wvY2hyaXNjb3lpZXIubmV0XC93cC1jb250ZW50XC91cGxvYWRzXC8yMDI2XC8wNlwvcGV4ZWxzLXBob3RvLTM3MDAyNTAtMTAyNHg2ODIuanBlZyIsInR4dCI6IlNvY2lhbCBSU1MgKD8pIiwidGVtcGxhdGUiOiJmdWxsc2NyZWVuIiwiZm9udCI6IiIsImJsb2dfaWQiOjc0MDc0MjQ4fQ.WOLKCjcfUYr5xMP44ix1Q1xnFK06KcqYxXL96613SoUMQ"/>
        <source>
            <id>tag:feedly.com,2013:cloud/feed/https://chriscoyier.net/feed/</id>
            <title type="html">Chris Coyier</title>
            <link href="https://chriscoyier.net" rel="alternate" type="text/html"/>
            <updated>2026-06-01T15:42:03Z</updated>
        </source>
    </entry>
</feed>