<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Jens Roland</title>
    <link>https://jensroland.com</link>
    <description>Articles on software engineering, leadership, and architecture by Jens Roland</description>
    <language>en</language>
    <lastBuildDate>Wed, 29 Apr 2026 00:00:00 +0200</lastBuildDate>
    <atom:link href="https://jensroland.com/rss" rel="self" type="application/rss+xml" />
    <image>
      <url>https://jensroland.com/apple-touch-icon.png</url>
      <title>Jens Roland</title>
      <link>https://jensroland.com</link>
    </image>
    <item>
      <title>The Reader Axiom</title>
      <link>https://jensroland.com/articles/215/the-reader-axiom</link>
      <description>The Reader Axiom — that code is read far more often than it is written — has been quietly invalidated. Most of what we built on top of it was ergonomics cosplaying as engineering. And AI doesn&apos;t need human ergonomics.</description>
      <content:encoded><![CDATA[<p>It begins like it always does. You pick up a coding agent — for me it was Claude Code with Opus 4.5, but the model and the tool don’t matter — and you build something. The first few thousand lines come fast. Then, somewhere around day three, you discover a single file that contains, by your reckoning, most of the application’s actual logic. Functions four hundred lines long. Conditionals nested four levels deep. Naming that suggests the agent ran out of words around the second pass.</p>
<p>A <em>God Module</em>, you mutter, with the disgust of a person who has read the book.</p>
<p>So you do what twenty-five years of professional habit demand. You refactor it. You don’t do it by hand, of course — you have the agent do it, under your supervision, with Uncle Bob whispering in your other ear. You break the file apart. You name things properly. You introduce a few small, pragmatic abstractions. You document the architecture in Markdown. The result reads as prose. It is not over-engineered. It is <em>clean</em>, in the canonical sense.</p>
<p>You feel good. You go to add the next feature.</p>
<p>And the agent stumbles.</p>
<p>The same model that one-shot refactored six thousand lines of slop spaghetti the previous afternoon now cannot find its own utility classes. It hallucinates function arguments. It reinvents code that already exists, two folders away, with a name that almost rhymes with the one the agent just made up. You watch this happen, and you realize, slowly, that something is broken. It is not the model — the model didn’t get worse in twelve hours. <em>It is the codebase</em>. The very act of cleaning it up has made it harder for the agent to navigate.</p>
<p>And then, you realize what’s actually broken is your instinct to refactor. It is the foundation that instinct stood on. It is the entire premise of the book on your shelf.</p>
<blockquote>
<p>The Reader Axiom — that code is read far more often than it is written, so optimize for the reader — has been quietly invalidated. Most of what we built on top of it was QWERTY.</p>
</blockquote>
<p>The QWERTY keyboard layout was designed in the 1870s to <em>slow typists down</em>, so the mechanical arms in early typewriters wouldn’t jam. The constraint vanished a century ago. The layout did not. We’ve spent 150 years training people on a workaround for a problem that no longer exists, and calling it “how to type”.</p>
<figure>
  <img class="blend-mode-luminosity"
    srcset="/assets/img/gen/articles/typing-class-642.avif 642w,
    /assets/img/gen/articles/typing-class-389.avif 389w"
    sizes="(min-width: 720px) 647px, calc(95.5vw - 28px)"
    src="https://jensroland.com/assets/img/gen/articles/typing-class-389.avif"
    alt="A mid-century typing class, rows of trainees at manual typewriters with an instructor at the front."
    loading="lazy">
  <figcaption><em>Teaching the workaround</em>, Nano Banana 2</figcaption>
</figure>
<p>Most of <em>Clean Code</em> is like QWERTY. The mechanical typewriter is the human brain; the arms are working memory; the workaround is a thousand small disciplines designed to keep the brain from jamming when it tries to hold a five-thousand-line file in mind. Small functions, meaningful names, careful formatting, intention-revealing structure. The patterns persist because we taught them — not because they were ever about the underlying activity. They were always about the human reader.</p>
<p>Robert Martin’s book wasn’t wrong. It was correctly identifying the typewriter, the arms, and the jam, and prescribing layouts that reduced jams. The book held for forty years because the constraint held for forty years. The next reader was always a junior engineer, scrolling through that five-thousand-line file in a terminal at 2 a.m., trying to figure out why production is on fire. We owed her clarity. We mostly delivered.</p>
<p>The next reader is no longer her. The next reader has a 1M context window, no opinions about formatting, and reads the file in 800 milliseconds. It does not jam.</p>
<figure>
  <img class="blend-mode-luminosity"
    srcset="/assets/img/gen/articles/library-642.avif 642w,
    /assets/img/gen/articles/library-389.avif 389w"
    sizes="(min-width: 720px) 647px, calc(95.5vw - 28px)"
    src="https://jensroland.com/assets/img/gen/articles/library-389.avif"
    alt="A vintage library reading desk at night, a green-shaded banker's lamp still lit over an open book and an empty chair."
    loading="lazy">
  <figcaption><em>The reader has stepped away</em>, Nano Banana 2</figcaption>
</figure>
<p>Twenty-five years writing code, two writing it with agents at my elbow. Sample size of codebases where I’ve watched the Reader Axiom break: enough to have stopped treating it as bad luck. There is no peer-reviewed paper for this — yet — but the pattern is visible enough that I have stopped trying to refactor my way out of it.</p>
<p>Take a deep breath. Most of what we called engineering ‘best practice’ is up for inspection.</p>
<hr />
<h2>Sorting the canon</h2>
<p>The interesting question isn’t whether the Reader Axiom has expired. It has. The interesting question is which of our practices were QWERTY all along — workarounds for a constraint that no longer holds — and which were doing structural work the whole time.</p>
<p>DRY is the cleanest example. Every senior engineer can recite it: <em>don’t repeat yourself</em>. Every senior engineer also knows DRY is a balancing act. Too DRY and you get baroque inheritance hierarchies named <code>AbstractFactoryStrategyVisitor</code>. Not DRY enough and you get bug-multiplying brittleness. The balance was always governed by a hidden term: the <em>cost of changing things in lockstep across the codebase</em>. When that cost is human and high, DRY wins. When that cost approaches zero, DRY loses its argument.</p>
<p>A SaaS company I know spent the better part of a quarter building a polished internal design system. Tokens, primitives, smart components, the works. The reason — without it, every minor design tweak required hunting down forty-three slight variations of the date picker. With the design system, you tweak in one place and inherit everywhere.</p>
<p>Then they pointed an agent at the codebase. The agent, faced with a need for a date picker, did what agents do. It built one. From scratch. Inline. Beautifully styled. Using none of the company’s design system, which it had been told about in three different places.</p>
<p>Under the old constraint, that agent’s behavior would have been a catastrophe — forty-three slight variations of the date picker, and any global design tweak now requires forty-three little surgeries. Under the new constraint, the cost of that global tweak is ten minutes of agent time. The case for the design system is no longer that humans will pay for it later. The case has to be made on different grounds, or it can’t be made at all.</p>
<p>You can run the same exercise across the rest of the canon. Small functions: QWERTY. Cutting a function into ten-line pieces was a chunking workaround for human working memory, which holds about seven items at a time and overflows on anything longer. The agent has no such ceiling, and reads the whole file in one breath. The fix was for our jam, not the code’s. Meaningful names: QWERTY. The agent is happy with <code>processStuff_v2_FINAL_actually_real</code>. SOLID, design patterns, careful formatting — every rule that was about helping a <em>human</em> chunk meaning loses most of its weight. They are still useful at the human-review boundary. They are no longer load-bearing.</p>
<p>What gets promoted is the half of the discipline that was never QWERTY in the first place — the practices whose load came from the runtime, not the reader. API-first design. End-to-end testing. CI/CD. Observability. Property-based testing. Contracts, invariants, machine-checkable assertions. These were always doing structural work, and the Reader Axiom expiring doesn’t touch them. If anything, it makes them more important: <strong>when nobody reads the code, the contracts and the tests are the only things keeping the system honest</strong>.</p>
<p>What dies entirely is the third pile — the practices that existed to coordinate teams of humans. These solved problems that don’t exist in the same shape when half the work is being done by something that doesn’t get tired, doesn’t bikeshed, and doesn’t need a daily standup. The team is something else now, and the rituals that managed it are vestigial.</p>
<p>The surviving rules were the ones about intent, verification, contracts, or safety. The rest was QWERTY.</p>
<figure>
  <img class="blend-mode-luminosity"
    srcset="/assets/img/gen/articles/workbench-642.avif 642w,
    /assets/img/gen/articles/workbench-389.avif 389w"
    sizes="(min-width: 720px) 647px, calc(95.5vw - 28px)"
    src="https://jensroland.com/assets/img/gen/articles/workbench-389.avif"
    alt="An antique craftsman's workbench, hand tools laid out on weathered wood — some polished from use, others tarnished and pushed to the back."
    loading="lazy">
  <figcaption><em>Surviving and vestigial</em>, Nano Banana 2</figcaption>
</figure>
<hr />
<h2>What this reveals</h2>
<p>Once you start running practices through the QWERTY filter, the bigger picture is uncomfortable.</p>
<p>Most software engineering “best practices” were never about software. They were cognitive scaffolding for the humans holding the code. Most of what we taught engineers was never engineering — it was ergonomics dressed up in engineering vocabulary.</p>
<p><strong>The AI age won’t kill software engineering. It will reveal that half of it was always something else.</strong></p>
<p>That single reframing explains a lot of things that used to be confusing. Why a junior engineer who was rigorous about formatting could ship correct systems and a principal engineer who couldn’t be bothered with naming conventions could too. Why pair programming was transformative on some teams and felt anachronistic on others within five years of the same generation. Why every company that adopted SOLID at the C-suite level produced two camps of engineers, one swearing by it and one quietly working around it. The practices weren’t failing or succeeding on their merits. They were succeeding when they happened to fit the cognitive ergonomics of the people doing the work, and failing when they didn’t.</p>
<p>The discipline never had a clean way to talk about that, so it called the ergonomics “engineering” and went on. The AI age is the first time the labels have been forced apart, because half the practices stop applying when the entity reading the code doesn’t have the cognitive constraint they were designed around.</p>
<p>What survives is the engineering. Which, it turns out, is a much smaller and harder thing than the canon implied.</p>
<hr />
<h2>Clean Code, rederived</h2>
<p>There is a comfortable reading of all this — <em>the agent has it covered, stop messing with the code</em> — and it is wrong. Left alone, an agent will gleefully ship you a twelve-thousand-line God Module with three slightly different date formatters and a serializer named <code>format2</code>. Sure, it can refactor — modern agentic harnesses ship with refactoring as a built-in step, and an agent will gladly carve a four-hundred-line function into ten neat forty-line ones if you ask. The problem isn’t whether it refactors. The problem is calibration. Every byte of its training data was written under the old constraint — <em>Clean Code</em>, the tutorials that reproduced it, forty years of code structured for a human reader. The post-QWERTY corpus is a few thousand blog posts from the last two years, written by people like me working it out in real codebases. My take here is one of those.</p>
<p>The work has not gone away. It has changed shape.</p>
<p>If QWERTY was the layout we kept after the jam disappeared, the question now is what you’d design if you sat down at the keyboard fresh, with the next reader at your elbow. The new orthodoxy — the one I now hand my agents in AGENTS.md, with a refactoring skill alongside — looks, file by file, like the inverse of what the book on the shelf prescribes:</p>
<ul>
<li><strong>Vertical slices, not horizontal layers.</strong> All the logic for a single entity — type definition, database mapping, validator, serializer, output formatter, factory, repository, service interface, local utility class, DI wiring — lives in <em>one</em> file. The agent never has to pick between ten possible homes for a function it is about to write. Pulling the right context out of a six-thousand-line file is trivial. Guessing wrong about which of ten files to grep is what produces the second date formatter.</li>
<li><strong>One file per concept, up to about ten thousand lines.</strong> Big enough to swallow a whole entity-shaped slice. Small enough that a million-token context window inhales it without breaking stride. The old <em>two thousand lines is a smell</em> rule was a constraint on human scrolling. The new ceiling is five times higher, and it’s a constraint on the model’s context. Different organ, different limit.</li>
<li><strong>Machine-checkable seams, not pretty ones.</strong> Contracts, types, tests at the module boundary. The agent respects a failing assertion. It does not respect a comment that says <em>please don’t add a fourth implementation of date formatting</em>. Nor should it have to.</li>
</ul>
<p>None of this means the old disciplines disappear entirely. I still ask my agents for meaningful names and decent formatting — the small ergonomic moves that keep things bearable at the human-review boundary. They have been demoted, not deleted. Soft preferences, not hard rules.</p>
<p>This is not laissez-faire. It is more deliberate, not less. You are still designing the codebase from first principles. You are just designing it for a different reader.</p>
<hr />
<h2>Pushback</h2>
<p>The objections write themselves.</p>
<p><em>“But humans still read code. When production breaks at 2 a.m., you can’t ask the agent to debug itself.”</em></p>
<p>Sure you can, Strawman Sam.. but I get your point. The demoted disciplines exist for exactly that 2 a.m. session. They just no longer govern the whole codebase. What has changed is the ratio. The vast majority of code-touching activity has shifted from read-then-modify to spec-then-regenerate or test-then-verify. And when the 2 a.m. session arrives, the thing you actually need to read is the test that failed, the contract that was violated, or the diff between two agent runs — not the function body.</p>
<p><em>“This is just an excuse to write sloppy code.”</em></p>
<p>It is not. Machine-checkable seams are harder to install than cosmetic ones are to enforce. Property-based testing is harder than naming. Designing a bounded interface the agent cannot accidentally misimplement is a more rigorous kind of work than refactoring <code>processOrder</code> into <code>validateOrder + persistOrder + emitOrderEvent</code>. What gets stripped away is cosmetic rigor. What remains is the actual kind.</p>
<p><em>“You’re describing a transitional moment. In five years the agents will be reliable enough that none of this matters.”</em></p>
<p>Maybe. Maybe even probably. But the transition is the rest of our careers, and assuming a problem will resolve itself eventually is exactly the cognitive move that produced every legacy codebase any of us has had to dig out of. We are in the transition. The transition is where the work is.</p>
<p>The objections are not silly. They are written from inside the old constraint — and the old constraint is what changed.</p>
<hr />
<p>I haven’t refactored that codebase since. The God Module is, of course, gone — Uncle Bob and I made sure of that. But the parts I’d have rewritten next, on instinct, on autopilot, because something in me said <em>this is too messy</em> — those parts are still standing. The agent has been navigating them like it owns the place. We get along now.</p>
<p>I keep the book on the shelf. I just stopped reaching for it before I refactor.</p>
]]></content:encoded>
      <pubDate>Wed, 29 Apr 2026 00:00:00 +0200</pubDate>
      <guid isPermaLink="true">https://jensroland.com/articles/215/the-reader-axiom</guid>
      <enclosure url="https://jensroland.com/assets/img/gen/covers/typewriter.avif" type="image/avif" length="0" />
      <category>software engineering</category>
      <category>clean code</category>
      <category>ai</category>
      <category>architecture</category>
      <category>refactoring</category>
    </item>
    <item>
      <title>The Cascade Reclaimed</title>
      <link>https://jensroland.com/articles/214/the-cascade-reclaimed</link>
      <description>We spent fifteen years running from the cascade. Now CSS has quietly become a different language — one that makes a new paradigm possible.</description>
      <content:encoded><![CDATA[<p>We have spent fifteen years running from the cascade.</p>
<p>First with naming conventions — BEM, SMACSS, OOCSS — bolting structure onto a language that didn’t enforce it. Then with build tools — CSS Modules, styled-components, Emotion — abandoning stylesheets entirely in favor of runtime JavaScript. And finally with Tailwind, which solved the collision problem by nuking the abstraction layer altogether, scattering <code>flex items-center justify-between px-4 py-2 bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow duration-200</code> across every <code>&lt;div&gt;</code> in the codebase and calling it progress.</p>
<p>Every one of these was a rational response to a real problem. The cascade <em>was</em> unwieldy at scale. Naming <em>was</em> fragile. Global stylesheets <em>did</em> break silently when teams grew. Each layer of workaround was a cast on a bone that was genuinely fractured — and at the time, you were right to put one on.</p>
<p>The bone healed years ago. <strong>The casts are still on. The limb underneath has begun to atrophy.</strong></p>
<p>What healed it is not a single feature. It is a quiet evolution. <code>@layer</code> gives you explicit control over cascade priority — <strong>specificity wars, solved.</strong> <code>@scope</code> contains styles to a DOM subtree without any naming convention, build tool, or runtime — <strong>collisions, structurally impossible</strong>. <code>@property</code> lets you register typed, inheritable custom properties with defaults, turning the cascade into something close to a <strong>contextual type system</strong>. <code>:has()</code> lets CSS inspect the contents and relationships of elements, <strong>eliminating most presentational classes</strong>. Native nesting. Container queries. <code>@starting-style</code> for entry and exit transitions. Most of these shipped in the last two years. None existed in practical cross-browser form five years ago. Together, these form a new paradigm.</p>
<p>Call it <strong>Platform-First CSS</strong>. After fifteen years of frameworks built to <em>patch</em> the platform, here is one that runs on it. You don’t reach past CSS any more. You reach <em>into</em> it.</p>
<p>I have been writing CSS since it first landed in Netscape Navigator 4.0 in 1997 — coming up on three decades. Eight years on preprocessors. Five on the various pre-utility workaround layers. Four on Tailwind. Every one of these casts has been on my arm at some point. Every one came off again.</p>
<p>So — three casts, three layers of workaround, one underlying paradigm. Let’s take them off, one at a time.</p>
<hr />
<h2>The naming-convention cast</h2>
<p>In 2013 I had a brief love affair with SMACSS. The first weekend was almost zen — the conventions were thoughtful, the philosophy was clearly onto something, and the part of my brain that had been holding the cascade in its context finally got to relax for a few hours. Within weeks however, the housekeeping was eating the project. CSS variables didn’t exist yet, so the modifier classes multiplied: <code>.button--primary</code>, <code>.alert--primary</code>, <code>.badge--primary</code>, each one duplicating the same color value in a slightly different declaration. Adding SASS rescued the ergonomics, but felt a bit like fixing your bike by throwing it on the back of a truck and driving instead. The discipline of remembering which prefix went with which module never became automatic, and SMACSS never quite earned a place in my default toolkit.</p>
<p>That was twelve years ago. The thing SMACSS (and BEM and OOCSS) were trying to do — give every selector a bounded address so it couldn’t collide with another component’s styles — is now what <code>@scope</code> does, but enforced by the parser instead of by a twenty-page convention document.</p>
<pre><code class="language-css">@layer components {
  @scope (.card) {
    :scope { background: var(--surface); padding: var(--space-m); }
    .title { font-size: var(--text-lg); font-weight: 600; }
    .body  { line-height: 1.6; }
  }
}
</code></pre>
<p>Here, <code>.title</code> inside <code>.card</code> and <code>.title</code> inside any other component are completely separate selectors. There is no <code>.card__title</code>. There is no naming convention. There is no slow accrual of compound class names that read like German nouns. You can call your inner elements <code>.title</code> and <code>.body</code> and <code>.actions</code> across the entire codebase, and the browser will keep them apart for you.</p>
<p>And not just within one team’s codebase. Two teams in different parts of a large organization can both define <code>.actions</code> inside their own scopes without coordinating, without sharing a naming ledger, and without anyone owning a global compatibility table. This is the thing that finally lets no-build vanilla CSS scale past the two-pizza team.</p>
<p>Conventions are not architecture. <code>@scope</code> is architecture.</p>
<hr />
<h2>The runtime cast</h2>
<p>Throughout my career, different companies have tried different methods for taming the cascade — CSS-in-JS, styled-components, hand-rolled variations on CSS Modules. Each one bought us a ticket out of <code>!important</code> hell, and each one paid for it with our ability to keep a consistent style across the site over time. The styles lived in the components, which meant the design system lived in the components — which meant the design system was now duplicated across every component file, drifting in tiny ways from one PR to the next.</p>
<p>I realized it during a theme-switch implementation. To turn dark mode on, the codebase needed a context provider, a hook in every styled-component file, and a re-render of every leaf node so the new theme prop could trickle down to the color declarations. To me, this amount of structural housekeeping for something so trivial was unacceptable: in my mind, all it should have needed was an attribute on a <code>&lt;section&gt;</code> element.</p>
<pre><code class="language-css">[data-theme=&quot;dark&quot;] {
  --surface: #1a1a1a;
  --on-surface: #e5e5e5;
}
</code></pre>
<p>Wrap any subtree in <code>&lt;section data-theme=&quot;dark&quot;&gt;</code> and every component inside it adapts. No prop drilling. No context provider. No re-render. The cascade does what it has always done — it passes values down the tree — and you just give it better values to pass. Once <code>@property</code> lets you type the color tokens, the theme switch can also <em>transition</em> smoothly, because the browser knows the values it is interpolating between are colors. An entire layer of React state plumbing collapses into an HTML attribute and a CSS rule.</p>
<p>Styles handle their own contexts again. Components stop pretending to be stylesheets. Separation of concerns, restored.</p>
<hr />
<h2>The utility cast</h2>
<p>I never liked Tailwind. The syntax is ugly. It is a regression to the era of <code>&lt;FONT&gt;</code> tags and inline styles, dressed in a modern build pipeline. It takes a perfectly valid idea — single-purpose utility classes — and pushes it well past the point where the idea was useful.</p>
<p>My breaking point was the official launch of the JIT compiler. The pitch was that the new compiler would solve the bundle-size problem. In the demo, the build took twenty seconds. The resulting dev build was twelve megabytes.</p>
<p>To get back to a sane production bundle, you needed tree-shaking, content scanning, a config file that needs its own documentation site, and the patience to debug why your dynamic class names had silently vanished from the build. None of that complexity existed before Tailwind. The problem it was solving did not exist before Tailwind. The framework multiplied class names by every breakpoint, every state, every variant; the compiler then had to subtract them back out. The plumbing needed to fix the framework was bigger than the framework.</p>
<p>Take this cast off and you find that the limb works. It was the cast that was always clumsy — slow to bend, indifferent to context, full of ways to chafe. The longer it has been on, the harder it is to remember what writing CSS without one used to feel like.</p>
<pre><code class="language-css">@layer components {
  @scope (button[data-variant]) {
    :scope {
      padding: var(--space-s) var(--space-m);
      border-radius: var(--radius-m);
    }
    :scope[data-variant=&quot;primary&quot;] { background: var(--color-primary); }
    :scope[data-variant=&quot;danger&quot;]  { background: var(--color-danger); }
  }
}
</code></pre>
<p>Two scoped rules. Token-driven. No build step. The class on the button is <code>data-variant=&quot;primary&quot;</code>, which is the same number of bytes you would have spent on <code>bg-primary text-white px-4 py-2 rounded</code>, and the <em>meaning</em> lives in the markup instead of being scattered across half a dozen utility classes that the next person to touch this file will have to grep for.</p>
<p>A thin utility shelf still has a place — forty or fifty rules at most, derived from the same tokens, used to fill compositional gaps between components. The mortar between the bricks. The escape valve. Not the whole framework.</p>
<hr />
<h2>The pushback</h2>
<p>The workarounds had reasons. The reasons deserve answers — not a wave of the hand.</p>
<p><em>“CSS is hard. Tailwind makes it easier.”</em></p>
<p>CSS <em>was</em> hard. The cascade was a minefield. Naming was a psychic discipline. Theming meant copying values everywhere or fighting a preprocessor’s variable scope. None of that is true now. With <code>@scope</code>, every component name is local. With <code>@layer</code>, every priority is explicit. With <code>@property</code> and a <code>data-</code> attribute, every theme switch is one line. The thing Tailwind was making easier no longer needs to be made easier. For the parts of CSS that many devs still find challenging – like positioning, alignment, or responsive media queries – there is no shame in going to Stephanie Eckles’ <a tabindex="0" href="https://smolcss.dev/">SmolCSS site</a> and copying a utility class or six. That’s where utility classes shine.</p>
<p><em>“But the browser support — can I really use <code>@scope</code> and <code>@property</code> in production?”</em></p>
<p>Yes. As of May 2026, global support sits at 95.4% for <code>@layer</code>, 91.1% for <code>@scope</code>, 94.7% for <code>@property</code>, 94.3% for <code>:has()</code>, and 91.2% for <code>@starting-style</code>. These are not bleeding-edge numbers. They are baseline-2024, with eighteen months’ safety margin.</p>
<p>And one more honorable mention. Native <code>@mixin</code> rules landed in Chrome Canary last year. With a little luck, the last feature SASS still genuinely offered will be part of the platform itself in the not-too-distant future.</p>
<p><em>“What about animations? CSS-in-JS won partly because React gave us animation power that pure CSS didn’t have — keyframe choreography, spring physics, FLIP transitions.”</em></p>
<p>True, for a while. CSS keyframes caught up in 2016. The <code>linear()</code> easing function landed in late 2023 — spring physics in pure CSS, no library. View Transitions have been broadly available since 2025, handling FLIP-style animations natively. Most of what required Framer Motion or react-spring is now a stylesheet.</p>
<p>And when an animation genuinely <em>has</em> to be driven by JavaScript — interactive scrubbing, gestures, runtime physics — the integration point is one line: <code>element.style.setProperty('--x', value)</code>. JavaScript pokes the cascade. The cascade does the rest. No stylesheet inside the component required.</p>
<p><em>“My team already knows BEM / Tailwind / styled-components. Switching is expensive.”</em></p>
<p>Knowing how to walk on crutches is not a transferable skill once the cast is off. The investment your team made in BEM was in remembering a convention. The parser does that work now. The investment in Tailwind was in memorizing thousands of utility class names; the platform provides what those classes were emulating. The cost of switching is non-zero, but it is paid once. The cost of <em>not</em> switching is paid every quarter, in the form of a build pipeline that needs care, a bundle size that needs trimming, and a design system that lives in twelve places at once.</p>
<hr />
<p>Platform-First CSS is not a framework. There is no <code>npm install</code>, no config file, and no build step, though you can add one if you want file splitting or minification. Your choice, not a requirement. Platform-First CSS is an architectural pattern: the layer stack as the spine, scopes as the boundaries, typed properties as the type system, <code>:has()</code> and container queries as the relational and dimensional primitives. The composition primitive — native <code>@mixin</code> — is waiting in the wings. You can implement it hand-rolled. You can implement it using a kit. The paradigm is the platform itself.</p>
<p>It does not ask you to unlearn CSS. It asks you to stop fighting the cascade and start declaring it with intent. It asks you to use the CSS that actually exists today, instead of the CSS you learned to work around in 2015.</p>
<p>The cast is off. The limb is healed. We developers just need to use it again.</p>
]]></content:encoded>
      <pubDate>Mon, 13 Apr 2026 00:00:00 +0200</pubDate>
      <guid isPermaLink="true">https://jensroland.com/articles/214/the-cascade-reclaimed</guid>
      <enclosure url="https://jensroland.com/assets/img/gen/covers/cascade-reclaimed.avif" type="image/avif" length="0" />
      <category>css</category>
      <category>web development</category>
      <category>frontend</category>
      <category>architecture</category>
    </item>
    <item>
      <title>The Codebase That Lasts Twice As Long Costs Half As Much</title>
      <link>https://jensroland.com/articles/213/the-codebase-that-lasts-twice-as-long-costs-half-as-much</link>
      <description>How to slow the atrophy of your code and create software solutions that last longer - much longer</description>
      <content:encoded><![CDATA[<p>No codebase was meant to be legacy code. And yet, here you are, plowing through 5000+ line source files so littered with undocumented side effects that touching any line could cause a failure on the other side of the repository. Even with a valiant attempt at refactoring, a codebase like this will probably deteriorate faster than you can patch it. This is life in a software debtor’s prison.</p>
<p>Time to start fresh and begin building a brand new system! One that is clean and modern. <em>Version 2.0</em> we’ll call it. We will learn from our mistakes and get it right with an all new team.</p>
<p><em>This time it’ll be different.</em></p>
<h2>The Greenfield-Bedlam Cycle</h2>
<figure>
  <img class="blend-mode-luminosity"
    srcset="/assets/img/gen/articles/greenfield-bedlam-cycle-642.avif 642w,
    /assets/img/gen/articles/greenfield-bedlam-cycle-389.avif 389w"
    sizes="(min-width: 720px) 647px, calc(95.5vw - 28px)"
    src="https://jensroland.com/assets/img/gen/articles/greenfield-bedlam-cycle-389.avif"
    alt="A Greenfield-Bedlam Cycle Spotted In The Wild, Dall-E 3"
    loading="lazy">
  <figcaption><em>A Greenfield-Bedlam Cycle Spotted In The Wild</em>, Dall-E 3</figcaption>
</figure>
<p>Take a deep breath.</p>
<p>That tangled codebase you’re looking at started its life with similar aspirations. And then reality hit: a quick fix here, a compromise there, some over-eager abstraction in the heat of the moment, and before long, it’s death by a thousand corner cuts.</p>
<p><strong>With stakeholders to please and deadlines to meet, the technical debt piles up, until all that remains of the original architecture is a chalk outline in the rough shape of a strategy design pattern.</strong></p>
<p>Once a codebase has been through the grinder of business for a fiscal year or two, the debt can become so expensive that a certain class of organization (that rhymes with <em>schmenterprise</em>) would rather just send it adrift on an ice floe and start over from scratch.</p>
<p>I call it the <em>Greenfield-Bedlam Cycle of enterprise software development</em>; when a large enterprise keeps building the same software solution over and over, only to have it become a nightmare of incalculable risk within months of deployment. When this happens, the old solution is scrapped and the cycle starts over with a brand new <a tabindex="0" href="https://en.wikipedia.org/wiki/Greenfield_project">Greenfield</a> project.</p>
<figure>
  <img class="blend-mode-luminosity"
    srcset="/assets/img/gen/articles/eternal-waterfall-clouds-642.avif 642w,
    /assets/img/gen/articles/eternal-waterfall-clouds-389.avif 389w"
    sizes="(min-width: 720px) 647px, calc(95.5vw - 28px)"
    src="https://jensroland.com/assets/img/gen/articles/eternal-waterfall-clouds-389.avif"
    alt="Bow before the Waterfall of Eternity, Dall-E 3"
    loading="lazy">
  <figcaption><em>Bow before the Waterfall of Eternity</em>, Dall-E 3</figcaption>
</figure>
<p>Even in lean organizations with stronger technical vision, I have encountered the view that non-trivial codebases cannot be kept clean and maintainable over longer timeframes. At least not in fast-evolving fields like front end web development, where the same <del>framework</del> <del>library</del> <em>meta-framework</em> will happily reinvent itself five times in ten years, each time creating a trail of dead remains of legacy code scattered across the web.</p>
<p>You can’t blame someone for having a cynical stance on the longevity of things when the very house they live in was built on quicksand.</p>
<figure>
  <img class="blend-mode-luminosity"
    srcset="/assets/img/gen/articles/engineer-and-clown-642.avif 642w,
    /assets/img/gen/articles/engineer-and-clown-389.avif 389w"
    sizes="(min-width: 720px) 647px, calc(95.5vw - 28px)"
    src="https://jensroland.com/assets/img/gen/articles/engineer-and-clown-389.avif"
    alt="Reacto The Clown Hopes You Weren't Doing Anything Important, Dall-E 3"
    loading="lazy">
  <figcaption><em>Reacto The Clown Hopes You Weren't Doing Anything Important</em>, Dall-E 3</figcaption>
</figure>
<h2>Confronting The Messy Reality</h2>
<p>How do we break the Greenfield-Bedlam Cycle? Can we somehow stop the atrophy of our codebases and create software solutions that last longer - much longer - and that remains functional, maintainable, and extensible for years or decades, without sacrificing development speed or relying on <a tabindex="0" href="https://medium.com/ingeniouslysimple/the-origins-of-the-10x-developer-2e0177ecef60">mythical 10x developers</a>? And can we achieve this not just occasionally by happy accident, but methodically, repeatably?</p>
<p>I believe so, and the key to unlocking this power is acknowledging the imperfections of technology and developers, and the <em>messy reality</em> that your code will meet once it hits production. As a developer or software architect, you cannot opt out of reality, so you have to design around it.</p>
<figure>
  <img class="blend-mode-luminosity"
    srcset="/assets/img/gen/articles/reality-is-a-messy-place-tech-blonde-642.avif 642w,
    /assets/img/gen/articles/reality-is-a-messy-place-tech-blonde-389.avif 389w"
    sizes="(min-width: 720px) 647px, calc(95.5vw - 28px)"
    src="https://jensroland.com/assets/img/gen/articles/reality-is-a-messy-place-tech-blonde-389.avif"
    alt="Why does my coffee machine have node_modules?, Dall-E 3"
    loading="lazy">
  <figcaption><em>Why does my coffee machine have node_modules?</em>, Dall-E 3</figcaption>
</figure>
<h3>Messy Reality Number 1: Requirements change</h3>
<p>All project requirements are based on assumptions and limited information. In a matter of hours, assumptions can be invalidated or leadership priorities can change. Perhaps you simply encounter overwhelming success and need to scale beyond your wildest projections.</p>
<p>How would you change your design to face the reality that requirements could change at any point in almost any way?</p>
<h3>Messy Reality Number 2: External circumstances change</h3>
<p>Any new codebase is designed based on a version of reality that will not exist in 18 months. Technological innovation could make part of your solution obsolete, a competitor could launch a killer app that pulls the rug out from under your product, security vulnerabilities may be uncovered, or dependent libraries are abandoned by their maintainers, forcing you to replace them.</p>
<p>In short: Reality <em>drifts</em></p>
<p>How would you change your design to face the reality that almost any technology it uses could require replacement on short notice?</p>
<h3>Messy Reality Number 3: Mistakes are made</h3>
<p>Even a stack of mind-boggling complexity can be maintainable by a highly skilled developer with in-depth knowledge of the tech, the domain, the patterns and paradigms. That person could never have an ‘off’ day; and they would always be given all the time and resources they need. Does that sound like the world of enterprise? I have it on good authority that it’s not the world of <a tabindex="0" href="https://www.forbes.com/advisor/investing/faang-stocks-mamaa/">FAANG</a> either.</p>
<p>In all likelihood, the developer assigned to work on your stack on a given day is going to be on a deadline or unfamiliar with some part of the domain or tech. They might be lazy (in the <em>“move fast and break things”</em> sense of the word), or just having a bad day. A lot of the developers who are going to work on your codebase in 18 months haven’t even been hired yet, and some are still in school or interviewing for their first consulting job.</p>
<figure>
  <img class="blend-mode-luminosity"
    srcset="/assets/img/gen/articles/smug-consultant-cartoony-left-642.avif 642w,
    /assets/img/gen/articles/smug-consultant-cartoony-left-389.avif 389w"
    sizes="(min-width: 720px) 647px, calc(95.5vw - 28px)"
    src="https://jensroland.com/assets/img/gen/articles/smug-consultant-cartoony-left-389.avif"
    alt="Let me tell you all about No-Code AI on the Quantum Blockchain, Dall-E 3"
    loading="lazy">
  <figcaption><em>Let me tell you all about No-Code AI on the Quantum Blockchain</em>, Dall-E 3</figcaption>
</figure>
<p>The fact is, your code is going to see some hasty workarounds in its life, and not even the most meticulously designed software architecture survives first contact with a junior developer. That is, not unless it was built to adapt and endure – to resist bad changes and even self-heal over time.</p>
<p>How would you change your design to face the reality that the developers maintaining your solution will be making mistakes like they’re going out of fashion?</p>
<h2>Principles of long-lived software design</h2>
<p>How do we architect systems to survive and even thrive in a messy reality?</p>
<h3>Principle 1: Know the developers</h3>
<blockquote>
<p><em>“People are part of the system. The design should match the user’s experience, expectations, and mental models.”</em></p>
<p class="source">~ <a tabindex="0" href="https://books.google.com/books?id=I-NOcVMGWSUC&amp;pg=PA85">Principles of computer system design: an introduction</a></p>
</blockquote>
<p>To achieve longevity in our solutions, we must consider the skill and time constraints of the end users (developers/maintainers) and design a system with the largest-possible <a tabindex="0" href="https://blog.codinghorror.com/falling-into-the-pit-of-success/">Pit of Success</a> for the average developer to stumble towards.</p>
<p>This often means “killing your darlings” by meeting the developers where they are, not where you wish they were. There could be a significant skill gap between where the team is today and where they need to be to maintain the system you’re proposing. Few companies will invest in a comprehensive training program to not only re-train your current developers, but also bring all future hires up to speed – including after you leave or get promoted to senior management?</p>
<figure>
  <img class="blend-mode-luminosity"
    srcset="/assets/img/gen/articles/smug-consultant-cartoony-642.avif 642w,
    /assets/img/gen/articles/smug-consultant-cartoony-389.avif 389w"
    sizes="(min-width: 720px) 647px, calc(95.5vw - 28px)"
    src="https://jensroland.com/assets/img/gen/articles/smug-consultant-cartoony-389.avif"
    alt="I mix $500 Scotch with Monster Energy. It's what plants crave, Dall-E 3"
    loading="lazy">
  <figcaption><em>I mix $500 Scotch with Monster Energy. It's what plants crave</em>, Dall-E 3</figcaption>
</figure>
<p>Even if it breaks your heart; that cool technology you like probably has to go.</p>
<p>Knowing the developers can also mean creating frameworks or abstractions that remove boilerplate and flatten the learning curve, or relying on patterns and even naming conventions that are already familiar to the devs.</p>
<p>In 2014, I had to build a frontend framework for a team of backend developers, and rather than try to convert them all to JavaScript enthusiasts, I created an HTMX-like markup syntax and async DOM manipulation component allowing them to build and maintain interactive frontend features without ever having to leave their familiar C#.</p>
<figure>
  <img class="blend-mode-luminosity"
    srcset="/assets/img/gen/articles/reduce-friction-642.avif 642w,
    /assets/img/gen/articles/reduce-friction-389.avif 389w"
    sizes="(min-width: 720px) 647px, calc(95.5vw - 28px)"
    src="https://jensroland.com/assets/img/gen/articles/reduce-friction-389.avif"
    alt="Reducing friction, Dall-E 3"
    loading="lazy">
  <figcaption><em>Reducing friction</em>, Dall-E 3</figcaption>
</figure>
<p>Finally, ‘knowing the developers’ means <em>helping the developers know</em>. Supporting their daily workflow by investing in writing rich documentation, templates and developer tooling, and spending time evangelizing the system, will reduce the friction of working on the system, and pay dividends in the long run.</p>
<p>You want both existing and future team members to feel intuitively familiar going into the codebase. This will allow them to make robust changes from day one.</p>
<h3>Principle 2: Keep It Simple</h3>
<p>Designing for longevity might sound like advocating for highly abstracted <a tabindex="0" href="https://wiki.c2.com/?RavioliCode">Ravioli Code</a>, but that is not the case. For instance, in the early phases of development, you can safely assume that each iteration will be so different that any abstraction you built in the previous one will have to be scrapped anyway. The pragmatic way to design for such drastic change is to cobble together an MVP with bash scripts and bubble gum, since the next version will start from scratch either way.</p>
<blockquote>
<p>Any complex system that works is invariably found to have evolved from a simple system that worked. The inverse proposition also appears to be true: A complex system designed from scratch never works and cannot be made to work. You have to start over, beginning with a working simple system.</p>
<p class="source"><strong>Gall’s Law</strong></p>
</blockquote>
<p>As a rule, abstractions should be avoided until they can pay for themselves by taming parts of the code which have become unwieldy. Pragmatic, well-timed abstraction is beneficial, whereas premature abstraction violates <a tabindex="0" href="http://principles-wiki.net/principles:gall_s_law">Gall’s Law</a> and leads to broken software.</p>
<p>Start Simple. <a tabindex="0" href="https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it">You Ain’t Gonna Need It</a>.</p>
<figure>
  <img class="blend-mode-luminosity"
    srcset="/assets/img/gen/articles/matryoshka-doll-guy-642.avif 642w,
    /assets/img/gen/articles/matryoshka-doll-guy-389.avif 389w"
    sizes="(min-width: 720px) 647px, calc(95.5vw - 28px)"
    src="https://jensroland.com/assets/img/gen/articles/matryoshka-doll-guy-389.avif"
    alt="But some day we might *need* six extra layers of abstraction..., Dall-E 3"
    loading="lazy">
  <figcaption><em>But some day we might *need* six extra layers of abstraction...</em>, Dall-E 3</figcaption>
</figure>
<h3>Principle 3: Design for change</h3>
<p>Try to ensure that the systems you design are <em>easy to change</em>.</p>
<h4>Naming things</h4>
<p>Make the code - and repo structure - intuitive, adhering to the <a tabindex="0" href="https://en.wikipedia.org/wiki/Principle_of_least_astonishment">Principle of Least Surprise</a>. If a developer doesn’t find the natural place to apply a change in the first or second place they look, they might just come up with a <em>creative solution</em> instead. Now you have two places where authorization rules are defined. Next week it could be three. Every ‘surprise’ in your design will attract code rot over time.</p>
<h4>Encapsulation</h4>
<p>Identify natural domain boundaries and encapsulate them with clear contracts. Rather than a technical boundary defined by microservice gateways and deployment bundles, or an organizational boundary defined by lines of management, a ‘domain boundary’ encapsulates a family of concepts which are likely to change together because they are intrinsically coupled, such as Users and UserProfiles.</p>
<p>Encapsulation by good domain boundaries simplifies future refactoring and aligns with real world aspects of your business/product, which are naturally long-lived.</p>
<h4>The paved road</h4>
<p>Prefer the well established stack over the ‘best’ one, since the 75% ‘batteries included’ solution that just works beats the 100% solution that requires constant tinkering because the stack is unstable and the developer ecosystem doesn’t exist yet. The exception is when you’re building systems at <em>massive scale</em>, where the absolute value of even a small incremental improvement can dramatically outweigh the cost of a team tinkering away on the 100% solution.</p>
<figure>
  <img class="blend-mode-luminosity"
    srcset="/assets/img/gen/articles/yak-shaving-642.avif 642w,
    /assets/img/gen/articles/yak-shaving-389.avif 389w"
    sizes="(min-width: 720px) 647px, calc(95.5vw - 28px)"
    src="https://jensroland.com/assets/img/gen/articles/yak-shaving-389.avif"
    alt="It may look like shaving a yak, but we're actually patching the ORM, Dall-E 3"
    loading="lazy">
  <figcaption><em>It may look like shaving a yak, but we're actually patching the ORM</em>, Dall-E 3</figcaption>
</figure>
<p>For open source dependencies, consider the size of the community and the number of contributors. If the project is maintained by a single person, it makes for a risky dependency. How frequently are major versions released? That is how often breaking changes are introduced. Do the maintainers keep updating and patching older versions, or will your team be forced to migrate to whole new versions every year to address unpatched security vulnerabilities?</p>
<p>When it comes to cloud platforms and application frameworks, these are load-bearing structures for your application, and you should choose the most stable and well supported option available. When it comes to libraries and services, you can afford to be more experimental as long as you take the necessary precautions, which brings us to…</p>
<h4>Loose coupling</h4>
<p>Keep systems loosely coupled: if you have fifty services all sending their logs to some 3rd party analytics tool, then consider creating a custom facade library to wrap all communication with the 3rd party tool. That way, when the day comes that the company changes logging tools (a safe bet), you only have to change the facade library in one place rather than all fifty services.</p>
<p>…but be pragmatic: If you can’t achieve looser coupling without adding a ton of complexity (stomping all over the <a tabindex="0" href="https://people.apache.org/~fhanik/kiss.html">KISS principle</a>), you are probably better off accepting a bit of tight coupling. Certain key decisions, such as your choice of cloud provider, are <a tabindex="0" href="https://www.inc.com/jeff-haden/amazon-founder-jeff-bezos-this-is-how-successful-people-make-such-smart-decisions.html">one way doors</a> with no easy way out, and while this may feel risky, it can bring great benefits; and one should never trade ten birds in the hand for one in the bush.</p>
<h4>Open standards</h4>
<p>Make sure that all critical business data is stored and accessible over open or de facto standards. ODBC is an open standard, as is Apache Arrow, whereas AWS S3 is an example of a defacto standard.</p>
<figure>
  <img class="blend-mode-luminosity"
    srcset="/assets/img/gen/articles/data-lock-in-pay-pay-pay-642.avif 642w,
    /assets/img/gen/articles/data-lock-in-pay-pay-pay-389.avif 389w"
    sizes="(min-width: 720px) 647px, calc(95.5vw - 28px)"
    src="https://jensroland.com/assets/img/gen/articles/data-lock-in-pay-pay-pay-389.avif"
    alt="Modern Data Warehousing: Extract-Load-Pay-Pay-Pay..., Dall-E 3"
    loading="lazy">
  <figcaption><em>Modern Data Warehousing: Extract-Load-Pay-Pay-Pay...</em>, Dall-E 3</figcaption>
</figure>
<p>Stay away from closed proprietary data formats to prevent data lock-in and maximise the chances of future tool integrations <em>just working</em> out of the box.</p>
<h2>Go build things to last</h2>
<blockquote>
<p><em>“What are you waiting for?! DO IT! JUST DO IT! YES, YOU CAN! JUST DO IT! If you’re tired of starting over, STOP GIVING UP!”</em></p>
<p class="source">~ <a tabindex="0" href="https://youtu.be/ZXsQAXx_ao0?si=elYi969rBnFawre4&amp;t=36">Shia LaBeouf</a>, possibly referring to this article</p>
</blockquote>
<p>Once you begin thinking of code in terms of longevity, it changes your approach to most aspects of software development.</p>
<p>The value of good documentation skyrockets when code needs to outlive multiple teams of maintainers. You still take on tech debt, but you may determine that some classes of tech debt (e.g. a quick and dirty one-off script or MVP) are acceptable while others (unintuitive naming or convention-breaking folder structures) are not. Your policies and preconceptions about automated tests, encapsulation, etc. – all need to be revisited when designing systems to stay in production for a decade or longer.</p>
<p>In return for this, however, you get a codebase that actively resists bad code; that neatly allows for innovation or changing requirements; a codebase that stays lean while maintaining high developer productivity for years – and which doesn’t require a costly bottom-up rebuild for every 2-3 years.</p>
<p>The result is faster development, higher quality, happier developers, and all of that at a significantly lower total cost of ownership.</p>
]]></content:encoded>
      <pubDate>Sun, 21 Apr 2024 00:00:00 +0200</pubDate>
      <guid isPermaLink="true">https://jensroland.com/articles/213/the-codebase-that-lasts-twice-as-long-costs-half-as-much</guid>
      <enclosure url="https://jensroland.com/assets/img/gen/covers/a-stable-platform-for-technology-large.avif" type="image/avif" length="0" />
      <category>software engineering</category>
      <category>architecture</category>
      <category>tech debt</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Start with a Monolith: a startup manifesto</title>
      <link>https://jensroland.com/articles/151/start-with-a-monolith-a-startup-manifesto</link>
      <description>A short, opinionated poem on entrepreneurship. Aggressively self-indulgent, pompous even... but it&apos;s not wrong.</description>
      <content:encoded><![CDATA[<p>I was going to qualify this by claiming I wrote it after binge watching <em>Silicon Valley</em>, but the truth is I’ve spent a good portion of my life thinking about - and living - the mistakes businesses keep making in the realm of software, product engineering, and entrepreneurship, and I’ve been wanting to distill some of it into a short, and most importantly, actionable format.</p>
<p>Something I could one day nail to my wall if and when I ever get around to launching a startup of my own.</p>
<p>I didn’t expect it to come out as poetry though.</p>
<blockquote>
<p>Start with a monolith</p>
<p>Start in the cloud</p>
<p>Start with a stack you know</p>
<p>Lean in for the KISS</p>
<p> </p>
<p>DevOps is rocket fuel</p>
<p>Tests give you speed</p>
<p>Stay off the bleeding edge</p>
<p>This is your creed</p>
<p> </p>
<p>Start with an MVP</p>
<p>Forget about scale</p>
<p>Look for that market fit</p>
<p>Make that first sale</p>
<p> </p>
<p>Obsess over customers</p>
<p>Know why they click</p>
<p>Shorten your feedback loop</p>
<p>Don’t be a dick</p>
<p> </p>
<p>Don’t chase that VC yet</p>
<p>Start with no dough</p>
<p>Launch early and often</p>
<p>Then pitch once you grow</p>
<p> </p>
<p>And when you have…</p>
<p> </p>
<p>Your culture can get out of hand</p>
<p>like a manifesto turned poem</p>
<p>Don’t hire what you cannot coach</p>
<p>Grow slow… or grow home</p>
</blockquote>
<p>I will free admit that this is aggressively self-indulgent, pompous even… but it’s <em>not wrong</em>.</p>
]]></content:encoded>
      <pubDate>Wed, 28 Sep 2022 00:00:00 +0200</pubDate>
      <guid isPermaLink="true">https://jensroland.com/articles/151/start-with-a-monolith-a-startup-manifesto</guid>
      <enclosure url="https://jensroland.com/assets/img/gen/covers/pexels-pixabay-161798.avif" type="image/avif" length="0" />
      <category>leadership</category>
      <category>lean</category>
      <category>software engineering</category>
      <category>entrepreneurship</category>
    </item>
    <item>
      <title>On Leadership</title>
      <link>https://jensroland.com/articles/116/on-leadership</link>
      <description>Hiring the right people, giving them what they need, and getting out of the way.</description>
      <content:encoded><![CDATA[<p>My leadership style boils down to three deceptively simple practices: <em>hiring the right people, giving them what they need, and getting out of the way</em>.</p>
<h2>Hiring the right people</h2>
<p>If I am going hiking, I don’t want the person who has memorized all the different hardening grades in the alloys of his pocket knife. I want the person who could <em>start a fire in the dark in four different ways with nothing but a rock and pinecone. And in two more ways if the pinecone is wet</em>.</p>
<p>Apply that to software and you have a surprisingly powerful recruiting method. Personally, in 10 years of hiring, I have not regretted a single hiring decision I made in this way.</p>
<p>(This is not the article to get into the details of hiring developers, but if you are interested, feel free to ask me. You may regret asking though – I can talk for <em>hours</em> about recruiting.)</p>
<h2>Giving them what they need</h2>
<p>Par for the course is powerful hardware, choose-your-own-keyboard, and the occasional conference ticket; but two essential things that top developers need to be both happy and highly productive, is <strong>clear direction</strong> and <strong>trust</strong>.</p>
<p>Once developers understand the “why” behind a high level requirement and are trusted with the details, they are free to explore and implement new and creative solutions. Such solutions may arrive at the goal in unconventional ways, but they almost always arrive both faster and better than anticipated by stakeholders or architects.</p>
<h2>Getting out of the way</h2>
<p>Obviously, don’t micromanage, but take it a step further and <em>eliminate any and all interruptions and dependencies</em> that get in the way of the work or pull the developer out of their productive ‘flow’. By my count, even the smallest interruption costs at least 30 minutes of productivity, in part due to the context switch itself but mainly from the process of slowly getting back into the flow of coding.</p>
<p>As for dependencies, any process that requires getting a response from a person outside the immediate team introduces a perfect storm of context switching, busy-waiting, and in some case even anxiety; these are people who may very well have spent 10,000 hours learning to code alone in front of a computer: a lot of them don’t like talking to people, let alone people they don’t know.</p>
<p>Finally, hands-off management is not ears-off management. Every team has needs which evolve over time, and as a leader your job is to listen and provide, whether that means coaching them, buying them nerf guns, or fighting off stakeholders when they come bearing gifts of this week’s feature creep (and it is <em>always</em> feature creep).</p>
]]></content:encoded>
      <pubDate>Wed, 09 Mar 2022 00:00:00 +0100</pubDate>
      <guid isPermaLink="true">https://jensroland.com/articles/116/on-leadership</guid>
      <enclosure url="https://jensroland.com/assets/img/gen/covers/pexels-cottonbro-studio-4065145.avif" type="image/avif" length="0" />
      <category>leadership</category>
      <category>recruiting</category>
      <category>software engineering</category>
    </item>
    <item>
      <title>On Developer Productivity</title>
      <link>https://jensroland.com/articles/114/on-developer-productivity</link>
      <description>About no-blame culture, uninterrupted flow, and high trust in developer teams.</description>
      <content:encoded><![CDATA[<p>In my experience, software development velocity correlates strongly with:</p>
<ol>
<li>The willingness to make mistakes. Small mistakes can be accepted as the cost of doing business, bigger ones can be automatically detected and remediated in real time. Velocity is gained through automation and no-blame culture.</li>
<li>The number of hours per week where a state of high-productivity <em>‘flow’</em> can be achieved. Flow can only happen where developers have uninterrupted time and clarity of goals. Velocity is gained through hands-off leadership.</li>
<li>The ability to <em>make decisions in real time</em>, without having to ask permission or coordinate with external teams. Velocity is gained through trust, transparency and architectural decoupling.</li>
</ol>
<p><strong>In short: Automation, No-blame culture, Hands-off leadership, Trust, Transparency, and Architectural decoupling.</strong></p>
<p>Tenets of Lean, but boogeymen in the typical enterprise.</p>
]]></content:encoded>
      <pubDate>Tue, 08 Mar 2022 00:00:00 +0100</pubDate>
      <guid isPermaLink="true">https://jensroland.com/articles/114/on-developer-productivity</guid>
      <enclosure url="https://jensroland.com/assets/img/gen/covers/pexels-thisisengineering-3861958.avif" type="image/avif" length="0" />
      <category>leadership</category>
      <category>productivity</category>
      <category>software engineering</category>
      <category>lean</category>
    </item>
  </channel>
</rss>