<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Wouter on Architecture</title>
    <description>Random musings mostly on cloud architecture, Azure, and .NET.</description>
    <link>https://woutervanranst.github.io/</link>
    <atom:link href="https://woutervanranst.github.io/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Mon, 15 Jun 2026 10:06:38 +0000</pubDate>
    <lastBuildDate>Mon, 15 Jun 2026 10:06:38 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>Naming is hard: choosing a C# Dictionary</title>
        <description>&lt;p&gt;I recently ran into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FrozenDictionary&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt;, which apparently &lt;a href=&quot;https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/&quot;&gt;arrived in .NET 8&lt;/a&gt;, and first read it as “a colder &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReadOnlyDictionary&lt;/code&gt;”. That’s not it though, and the naming is quite confusing.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dictionary&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt; is the ‘normal’ general purpose dictionary.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReadOnlyDictionary&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt; is an adapter around an existing dictionary. Callers will not be able to write.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImmutableDictionary&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt; is optimized to create cheap modified (mutated) copies (confusing, right).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FrozenDictionary&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt; is optimized for quick reads.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConcurrentDictionary&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt; is for shared writes from multiple threads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives you the following tradeoffs:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Type&lt;/th&gt;
      &lt;th&gt;Strength&lt;/th&gt;
      &lt;th&gt;Cost&lt;/th&gt;
      &lt;th&gt;Use when&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReadOnlyDictionary&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Cheap wrapper&lt;/td&gt;
      &lt;td&gt;Not truly immutable; backing dictionary can still change&lt;/td&gt;
      &lt;td&gt;Expose a dictionary as read-only and you control the backing dictionary&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImmutableDictionary&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Immutable snapshots; safe sharing; efficient “modified copies”&lt;/td&gt;
      &lt;td&gt;Slower and more allocation-heavy than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dictionary&lt;/code&gt; for normal mutation/read-heavy workloads&lt;/td&gt;
      &lt;td&gt;Persistent versions, functional updates, or concurrency-safe snapshots&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FrozenDictionary&amp;amp;lt;TKey,TValue&amp;amp;gt;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Very fast lookup/enumeration after construction&lt;/td&gt;
      &lt;td&gt;Expensive to build; cannot update; intended for trusted stable keys&lt;/td&gt;
      &lt;td&gt;Build once, then read many times, often for app lifetime&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;readonlydictionary&quot;&gt;ReadOnlyDictionary&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReadOnlyDictionary&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt; is a wrapper class. You pass it an existing dictionary. The wrapper does not expose &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Add&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remove&lt;/code&gt;, or a settable indexer, but it reads from the dictionary you gave it. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReadOnlyDictionary&lt;/code&gt; does not take a defensive copy of the source dictionary.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BE&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;31&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;readOnly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReadOnlyDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LU&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;352&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;-- we can still modify the underlying source&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readOnly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LU&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 352&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReadOnlyDictionary&lt;/code&gt; does not mean “nobody can ever change this data”. It means “you cannot change it through this object”. Its interface is often used as return type:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IReadOnlyDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CountryCodes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_countryCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That says “you can read this” without promising that nobody inside the class will ever change &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_countryCodes&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;immutabledictionary-and-frozendictionary&quot;&gt;ImmutableDictionary and FrozenDictionary&lt;/h2&gt;

&lt;p&gt;Both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImmutableDictionary&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FrozenDictionary&lt;/code&gt; stop you from changing the object in place. That is where the similarity ends.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImmutableDictionary&lt;/code&gt; is a data structure where updates return a new version while sharing most of the old structure. This is common terminology in functional programming, but it is easy to misread in everyday .NET code.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;original&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImmutableDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BE&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;changed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;original&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LU&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;352&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;original&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContainsKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LU&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// False&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContainsKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LU&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That is the point of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImmutableDictionary&lt;/code&gt;: keep &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;original&lt;/code&gt;, pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;changed&lt;/code&gt;, and do not copy the whole dictionary for each change.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FrozenDictionary&lt;/code&gt; has a different job. Build it when the data is done changing.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frozen&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BE&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LU&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;352&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToFrozenDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringComparer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ordinal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frozen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;NL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There is no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Add&lt;/code&gt; method returning a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FrozenDictionary&lt;/code&gt;. If the data changes, build a new one. The type spends extra work during construction so lookups can be cheaper later.&lt;/p&gt;

&lt;p&gt;That makes sense for long-lived maps: route tables, schema metadata, code-to-handler maps, keyword maps, and lookup tables built from configuration. It makes much less sense for something you rebuild every request.&lt;/p&gt;

&lt;h2 id=&quot;benchmarks&quot;&gt;Benchmarks&lt;/h2&gt;

&lt;p&gt;The benchmark code is &lt;a href=&quot;https://github.com/woutervanranst/woutervanranst.github.io/tree/main/docs/assets/posts/choosing-a-csharp-dictionary/benchmark&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;lookup&quot;&gt;Lookup&lt;/h3&gt;

&lt;p&gt;The lookup benchmark performs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TryGetValue&lt;/code&gt; calls against string keys in randomized order:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;BenchmarkDotNet v0.14.0, macOS 26.5.1, Apple M4
.NET SDK 10.0.201
Runtime=.NET 10.0.5, Arm64 RyuJIT AdvSIMD

| Method                           | Count | Mean      | Ratio | Allocated |
|--------------------------------- |------ |----------:|------:|----------:|
| Dictionary_TryGetValue           | 10000 |  68.90 us |  1.00 |         - |
| ReadOnlyDictionary_TryGetValue   | 10000 |  71.48 us |  1.04 |         - |
| ImmutableDictionary_TryGetValue  | 10000 | 665.74 us |  9.70 |         - |
| FrozenDictionary_TryGetValue     | 10000 |  45.38 us |  0.66 |         - | &amp;lt;-- cheaper vs Dictionary
| ConcurrentDictionary_TryGetValue | 10000 |  60.81 us |  0.89 |         - |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For this string-key scenario, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FrozenDictionary&lt;/code&gt; was about one third faster than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dictionary&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImmutableDictionary&lt;/code&gt; was much slower for lookup.&lt;/p&gt;

&lt;h3 id=&quot;construction&quot;&gt;Construction&lt;/h3&gt;

&lt;p&gt;Construction tells the other half of the story:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;| Method                           | Count | Mean      | Ratio | Allocated  |
|--------------------------------- |------ |----------:|------:|-----------:|
| BuildDictionary                  | 10000 |  121.3 us |  1.00 |  276.43 KB |
| BuildReadOnlyDictionary          | 10000 |  139.3 us |  1.15 |  276.47 KB |
| BuildImmutableDictionary         | 10000 | 1625.9 us | 13.41 |  625.26 KB | &amp;lt;-- MUCH more expensive vs Dictionary
| BuildFrozenDictionary            | 10000 |  692.6 us |  5.71 | 1102.85 KB | &amp;lt;-- more expensive vs Dictionary
| BuildConcurrentDictionary        | 10000 |  679.8 us |  5.61 | 1022.32 KB |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this run, building a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FrozenDictionary&lt;/code&gt; was about 5.7 times slower than building a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dictionary&lt;/code&gt;. Fine for a map that lives for a while. Wasteful for a throwaway map.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImmutableDictionary&lt;/code&gt; is even slower to build from scratch here because it is building the machinery for future snapshots: a persistent structure that can share nodes between versions. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FrozenDictionary&lt;/code&gt; also spends extra construction work, but it spends it on one final lookup table. Neither is trying to beat &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dictionary&lt;/code&gt; at first construction.&lt;/p&gt;

&lt;h3 id=&quot;mutations&quot;&gt;Mutations&lt;/h3&gt;

&lt;p&gt;Produce a new dictionary-like collection with one extra key:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;| Method                           | Count | Mean       | Ratio  | Allocated | Alloc Ratio |
|--------------------------------- |------ |-----------:|-------:|----------:|------------:|
| ImmutableDictionary_Add          | 10000 |   0.202 us |  0.004 |     872 B |       0.003 | &amp;lt;-- MUCH cheaper vs Dictionary
| Dictionary_CopyAndAdd            | 10000 |  55.774 us |  1.005 |  283094 B |       1.000 |
| FrozenDictionary_RebuildAndAdd   | 10000 | 661.273 us | 11.918 | 1129400 B |       3.989 |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That is the metric where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImmutableDictionary&lt;/code&gt; wins: producing another version is cheap. It does not copy 10,000 entries to add one key. It creates a new root and a small number of changed nodes. Most of the old structure is shared.&lt;/p&gt;

&lt;h3 id=&quot;merging-dictionaries&quot;&gt;Merging dictionaries&lt;/h3&gt;

&lt;p&gt;If you want to merge a Dictionary, there are several strategies - I refer to &lt;a href=&quot;https://code-maze.com/csharp-how-to-merge-dictionaries/&quot;&gt;How to Merge Dictionaries in C#? - Code Maze&lt;/a&gt; who does an excellent write-up.&lt;/p&gt;

&lt;p&gt;The fastest way turns out to be a nested &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreach&lt;/code&gt; loop wins. LINQ is fine for small, cold paths. For large merges or hot paths, write the loop and make the duplicate rule visible.&lt;/p&gt;

&lt;h2 id=&quot;the-flowchart&quot;&gt;The flowchart&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;flowchart TD
    A[&quot;Need key-value lookup?&quot;] --&amp;gt;|No| B[&quot;Use List, array, tuple list, or a custom type&quot;]
    A --&amp;gt;|Yes| C{&quot;Keys unique?&quot;}
    C --&amp;gt;|No| D[&quot;Use Lookup, grouping, or Dictionary of lists&quot;]
    C --&amp;gt;|Yes| E{&quot;Need ordering or ranges by key?&quot;}
    E --&amp;gt;|Yes| F[&quot;SortedDictionary&quot;]
    E --&amp;gt;|No| G{&quot;Shared concurrent writes?&quot;}
    G --&amp;gt;|Yes| H[&quot;ConcurrentDictionary&quot;]
    G --&amp;gt;|No| I{&quot;Will contents change after build?&quot;}
    I --&amp;gt;|Yes| J[&quot;Dictionary&quot;]
    I --&amp;gt;|No| K{&quot;Need to produce modified versions cheaply?&quot;}
    K --&amp;gt;|Yes| L[&quot;ImmutableDictionary&quot;]
    K --&amp;gt;|No| M{&quot;Hot read path and built rarely?&quot;}
    M --&amp;gt;|Yes| N[&quot;FrozenDictionary&quot;]
    M --&amp;gt;|No| O{&quot;Need to expose without allowing callers to mutate?&quot;}
    O --&amp;gt;|Yes| P[&quot;IReadOnlyDictionary or ReadOnlyDictionary&quot;]
    O --&amp;gt;|No| Q[&quot;Dictionary&quot;]
&lt;/code&gt;&lt;/pre&gt;
</description>
        <pubDate>Sun, 14 Jun 2026 09:00:00 +0000</pubDate>
        <link>https://woutervanranst.github.io/choosing-a-csharp-dictionary/</link>
        <guid isPermaLink="true">https://woutervanranst.github.io/choosing-a-csharp-dictionary/</guid>
        
        
      </item>
    
      <item>
        <title>Arius 7 and Spec-Driven Development at scale: a failure mode</title>
        <description>&lt;p&gt;&lt;em&gt;A follow-up to&lt;/em&gt; &lt;a href=&quot;https://wouteronarchitecture.medium.com/developing-arius-5-during-the-dawn-of-ai-assisted-code-473fdb05b1e6&quot;&gt;&lt;em&gt;Developing Arius 5 during the dawn of AI-assisted code&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;TL;DR:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Spec Driven Development got me from idea to working legacy rewrite shockingly fast: five years of hand-built feature parity in roughly a month.&lt;/li&gt;
  &lt;li&gt;But the generated code was maintainability hell: strings everywhere, duplicate helpers everywhere, green tests masking architectural rot.&lt;/li&gt;
  &lt;li&gt;The missing piece is not “better specs” alone; it is domain judgment: knowing when to introduce the right domain concepts, when to refactor, and what code smells matter across the whole codebase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since I discovered OpenSpec back in January, I’m a fanboy of Spec Driven Development. So to find out where it breaks, I spent the past few months pointing it at my own pet project and trying to do a full agentic rewrite: &lt;a href=&quot;https://github.com/woutervanranst/Arius7&quot;&gt;Arius 7&lt;/a&gt;, built with OpenCode, vanilla OpenSpec, GitHub Copilot (first Opus 4.6 + Sonnet 4.6 but later I switched to GPT 5.4 for the &amp;gt;400k context window).&lt;/p&gt;

&lt;p&gt;It’s a “legacy modernization”, if you want ;) The nice thing about doing it on my own codebase is that I know &lt;strong&gt;exactly&lt;/strong&gt; what I want the result to be. For context: Arius is a small archival tool that uses the cheap offline/archive tier of Azure Blob Storage - point it at My Documents, get a backup. The rewrite landed at ~12k lines of production code plus an ~18k-line test suite at ~80% coverage.&lt;/p&gt;

&lt;p&gt;Short version of what I found: SDD got me further than I expected, and then it walked me straight into a wall.&lt;/p&gt;

&lt;h3 id=&quot;sdd-is-magic&quot;&gt;&lt;strong&gt;SDD is magic&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;The initial change flowed out of a 2 hour &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/opsx-explore&lt;/code&gt; &lt;a href=&quot;https://github.com/woutervanranst/Arius7/blob/master/openspec/changes/archive/2026-03-24-arius-core-foundation/REQUIREMENTS-Discussion.md&quot;&gt;session&lt;/a&gt; where I described what I wanted to build (I started it off as a ‘&lt;a href=&quot;https://restic.net/&quot;&gt;restic&lt;/a&gt; rewrite for Azure Archive tier’, because restic doesn’t support that and I assume the training set contained more about restic than Arius5 ;)). It correctly identified some of the tensions I encountered during the past 5 years of ‘manual’ code writing and we made something &lt;strong&gt;better&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When the spec was finalized, I pressed enter, it goes &lt;em&gt;whirrrr&lt;/em&gt; - and two hours later &lt;strong&gt;it works&lt;/strong&gt;. It got the basics right. I did a couple more changes and after about a month I had feature parity with something I’d been building on-and-off for five years. I was blown away.&lt;/p&gt;

&lt;h3 id=&quot;and-then-i-looked-at-the-code&quot;&gt;&lt;strong&gt;And then I looked at the code&lt;/strong&gt;&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Strings. Strings everywhere.&lt;/li&gt;
  &lt;li&gt;Helper methods. Helper methods everywhere. Duplicate helper methods galore.&lt;/li&gt;
  &lt;li&gt;Duplicate code, weird names, a 2 GB file loaded into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;byte[]&lt;/code&gt; like memory is cheap these days.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It compiled, the tests were green, round-trip backup/restore worked, but making manual changes to the code; there was no end in sight.&lt;/p&gt;

&lt;p&gt;As an example: during my explore sessions I hinted at some of the domain concepts I introduced during my manual code writing, but I deliberately left out one of the more useful domain concepts in my codebase: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RelativePath&lt;/code&gt;. I’d introduced it years ago in the hand-written version because the alternative is misery. Most &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;File&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path&lt;/code&gt; operations in native C# are string-based, so without that concept you end up with paths-as-strings smeared across the whole codebase. Which is exactly what happened. The agents &lt;strong&gt;never came up with the idea themselves&lt;/strong&gt; because each helper is benign, but on a repository level it’s death by a thousand cuts. At least 35 helper methods to normalize a path: 17 of them assume a trailing slash (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;My Documents\Pictures\&lt;/code&gt;), 10 assume no trailing slash (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;My Documents\Pictures&lt;/code&gt;), and 8 of them normalize them first. It works. Nobody can maintain it.&lt;/p&gt;

&lt;p&gt;The domain concepts I &lt;em&gt;did&lt;/em&gt; hand the agent during prompting are sitting in there nice and clean. The ones I didn’t think to mention never got invented.&lt;/p&gt;

&lt;h3 id=&quot;a-first-diagnosis&quot;&gt;&lt;strong&gt;A first diagnosis&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;My provisional conclusion is that &lt;strong&gt;fully unattended SDD workflows are missing a guardrail&lt;/strong&gt; - something that introduces the right domain concept at the right moment, with the effortful refactor that should come with it.&lt;/p&gt;

&lt;p&gt;I augmented my setup with &lt;a href=&quot;https://github.com/obra/superpowers&quot;&gt;Superpowers&lt;/a&gt;, which does TDD, but it stops at red -&amp;gt; green. The &lt;strong&gt;refactor&lt;/strong&gt; step of red -&amp;gt; green -&amp;gt; refactor is where a domain concept like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RelativePath&lt;/code&gt; would normally get extracted, and that’s the step that quietly goes missing.&lt;/p&gt;

&lt;h3 id=&quot;squeezing-the-toothpaste-back-into-the-tube&quot;&gt;&lt;strong&gt;Squeezing the toothpaste back into the tube&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Regardless, I had ‘&lt;a href=&quot;https://en.wikipedia.org/wiki/IKEA_effect&quot;&gt;IKEA effect&lt;/a&gt;’ and wanted to salvage the Arius 7 codebase, and wanted get the strings/helpers/duplication back under control with &lt;a href=&quot;https://github.com/Th0rgal/open-ralph-wiggum&quot;&gt;ralph&lt;/a&gt; loops. While it looks simple (“strongly type all the primitive types, make no mistakes”) it’s trickier than you’d think. It overdid it until basically every primitive became strongly typed. It was like the codebase was reduced to &lt;a href=&quot;https://en.wikipedia.org/wiki/Instrumental_convergence&quot;&gt;paperclips&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In retrospect, I should have regenerated the code based on the specs with the updated guidance. Hindsight 101.&lt;/p&gt;

&lt;h3 id=&quot;openspec--superpowers-not-sold-on-the-combo&quot;&gt;&lt;strong&gt;OpenSpec + Superpowers: not sold on the combo&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;I started with OpenSpec and added Superpowers along the way because everybody kept raving about it. I’m not convinced by the combination, and there’s surprisingly little written about it online.&lt;/p&gt;

&lt;p&gt;My impression is that the brainstorm/explore and the writing-plans steps get in each other’s way. I prefer OpenSpec, mostly because of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/opsx-archive&lt;/code&gt; it folds the various change specs back into one coherent set of master specs, so specs and code stay aligned over time. Superpowers’ plans feel like &lt;em&gt;plan mode on steroids&lt;/em&gt; - useful in the moment, but they don’t fold back coherently, so I don’t see the long-term value in keeping them around in the repo.&lt;/p&gt;

&lt;p&gt;The TDD workflow also goes too far now and then. Before Superpowers would touch my test suite, it wanted a failing test first - so at one point I had a test suite for my test suite. Got that out with another ralph loop: &lt;em&gt;“remove all tests that are not testing production code.”&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;when-do-i-trust-this-code-with-my-childhood-memories&quot;&gt;&lt;strong&gt;When do I trust this code [with my childhood memories]?&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Here’s the one that got under my skin. The code looks like it works, but these are &lt;em&gt;real&lt;/em&gt; files I’d like to keep safe. The question that kept nagging me:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When do I trust this codebase with eg. my childhood photos if I don’t fully &lt;em&gt;get&lt;/em&gt; the code myself?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I suspect a lot of enterprises are sitting with some version of that exact question.&lt;/p&gt;

&lt;p&gt;Where I landed: a fat &lt;em&gt;behavior&lt;/em&gt; test suite that runs a handful of scenarios “as it really happens” - archive -&amp;gt; restore. I’ve mentally assimilated and verified that test suite. I understand and trust what it does, so I now feel safe about the behavior even if parts of the code are still black-boxed to me. Trusting the behavior is a different thing from trusting the code, and for now that’s the lever I have.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/arius-7-and-spec-driven-development-at-scale-a-failure-mode/image-1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source: Anthropic: &lt;a href=&quot;https://www.youtube.com/watch?v=fHWFF_pnqDk&amp;amp;t=414s&quot;&gt;Vibe coding in prod&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;humans--ai-better-together&quot;&gt;&lt;strong&gt;Humans &amp;amp; AI, better together&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;It’s not all bad. One of the genuine pain points in Arius 5 was a scalability problem on large archives: change one file, and I’d produce a whole new “state” of the entire folder to keep track of, which at scale was an up/download of ~500 MB. Not exactly ideal for responsiveness.&lt;/p&gt;

&lt;p&gt;I handed that problem to OpenSpec explore, and &lt;em&gt;together&lt;/em&gt; we worked our way to a &lt;a href=&quot;https://en.wikipedia.org/wiki/Merkle_tree&quot;&gt;Merkle-tree&lt;/a&gt;-style implementation that I would never have pulled off on my own.&lt;/p&gt;

&lt;h3 id=&quot;local-optima&quot;&gt;&lt;strong&gt;Local optima&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;The above is an example of agents writing brilliant code. I didn’t know Merkle trees, didn’t know that I didn’t know them, but the agents sniffed the pattern out, gave it a name and we ran with it. But it was a local optimum in a rather contained part of the codebase.&lt;/p&gt;

&lt;p&gt;The broader smell, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RelativePath&lt;/code&gt;, is smeared out over the full codebase as low noise. It doesn’t fully fit in the context window, so it doesn’t hurt the agent as much. They tend to propose &lt;strong&gt;local optima&lt;/strong&gt;: solutions that are optimal &lt;em&gt;within their context window&lt;/em&gt; without proactively going to find the context they’d actually need to do better. I think that’s an engineerable problem. But the current SDD frameworks just don’t do enough about it yet.&lt;/p&gt;

&lt;p&gt;Someone else pointed me at the &lt;a href=&quot;https://github.com/anthropics/claude-plugins-official/blob/main/plugins/code-simplifier/agents/code-simplifier.md&quot;&gt;code-simplifier&lt;/a&gt; skill, but that lands you right back in local-optima territory.&lt;/p&gt;

&lt;h3 id=&quot;the-domain-knowledge-advantage&quot;&gt;&lt;strong&gt;The domain-knowledge advantage&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Which is exactly the catch. My advantage on this project is that I &lt;strong&gt;know&lt;/strong&gt; the domain, so I can steer the agent - feed it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RelativePath&lt;/code&gt; and other good concepts, sniff out which refactors are needed.&lt;/p&gt;

&lt;p&gt;For a domain unknown to me, or a genuinely fully hands-off agentic setup, I don’t yet see how we close that gap - and a few of the experts I’ve talked to about this don’t see an obvious fix either. That’s a real risk for new contexts that lack human domain experts. Sharpening the spec is only part of the answer, because a spec only gets you so far; the &lt;em&gt;situational awareness of the code&lt;/em&gt; is what lets you smell where a refactor belongs, and that’s the part that isn’t in the spec.&lt;/p&gt;

&lt;p&gt;Asking the right questions is, and stays, the whole game.&lt;/p&gt;

&lt;p&gt;The Rick Rubin meme gets more relevant by the day.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/arius-7-and-spec-driven-development-at-scale-a-failure-mode/image-2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;so-what-might-actually-fix-this&quot;&gt;&lt;strong&gt;So what might actually fix this?&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;For this failure mode, and given how fast things evolve, I expect a fix for this to show up in the next 3-6 months. It feels like a tractable, engineerable gap rather than a fundamental one.&lt;/p&gt;

&lt;p&gt;There are two distinct aspects, and it’s worth keeping them apart:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Design it properly&lt;/strong&gt; - make sure the right domain concepts get introduced in the first place. Still unsolved, as far as I can tell.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Keeping the toothpaste in the tube&lt;/strong&gt; - stop the string/helper/duplicate sprawl from creeping back. This one is more approachable. I set up a hard guardrail with &lt;a href=&quot;https://github.com/BenMorris/NetArchTest&quot;&gt;NetArchTest&lt;/a&gt;, but I find it pretty verbose, fairly rigid, and not all that expressive. How do you even encode &lt;em&gt;“too many helper methods”&lt;/em&gt; as a NetArchTest rule? I’ll tackle this in the coming weeks with static code analysis.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;a-separate-gripe-spec-drift-in-openspec&quot;&gt;&lt;strong&gt;A separate gripe: spec drift in OpenSpec&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;The promise of SDD is that the code is &lt;em&gt;grounded in&lt;/em&gt; the spec. The code becomes a generated artifact and both are always in sync with each other.&lt;/p&gt;

&lt;p&gt;But after making follow-up changes in a change, eg. after a CodeRabbit review, the code drifts from the spec. Caveat emptor - I thought the sync command in OpenSpec did exactly that: update the spec based on the actual code. But that is not what it does.&lt;/p&gt;

&lt;p&gt;Maybe I’d need to make a custom OpenSpec flow that grounds before syncing - I took a &lt;a href=&quot;https://github.com/woutervanranst/Arius7/blob/master/.opencode/commands/opsx-ground-spec.md&quot;&gt;first swing at it&lt;/a&gt;, the idea being a “clairvoyant” spec that reads as if it had always perfectly described the implementation that exists today. But too little, too late, it made me realize after 3 months that my previous specs are essentially worthless. Exit SDD?&lt;/p&gt;

&lt;p&gt;Note: a colleague pointed me to &lt;a href=&quot;https://www.augmentcode.com/guides/living-specs-vs-static-specs&quot;&gt;Living Specs vs Static Specs&lt;/a&gt;; coming to all SDD frameworks soon?&lt;/p&gt;

&lt;h3 id=&quot;a-philosophical-note-to-end-on&quot;&gt;&lt;strong&gt;A philosophical note to end on&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;Does all of the above even matter if the functional and non-functional requirements are met? Similarly, do we care if compiler-generated code is well crafted?&lt;/p&gt;

&lt;p&gt;For the latter - no.&lt;/p&gt;

&lt;p&gt;For the former - maybe.&lt;/p&gt;

&lt;p&gt;The counterargument is that the garbage and the hallucinations &lt;a href=&quot;https://circleci.com/blog/dora-ai-amplifier/&quot;&gt;&lt;em&gt;accelerate&lt;/em&gt;&lt;/a&gt;. Let the string soup and the 35 normalize-helpers compound across a real codebase and you eventually hit the wall I hit - so keeping the toothpaste in the tube genuinely matters. For now. Maybe that’s a problem stronger models just dissolve.&lt;/p&gt;

&lt;p&gt;I don’t know yet. That’s kind of the point.&lt;/p&gt;
</description>
        <pubDate>Thu, 11 Jun 2026 14:07:16 +0000</pubDate>
        <link>https://woutervanranst.github.io/arius-7-and-spec-driven-development-at-scale-a-failure-mode/</link>
        <guid isPermaLink="true">https://woutervanranst.github.io/arius-7-and-spec-driven-development-at-scale-a-failure-mode/</guid>
        
        
      </item>
    
      <item>
        <title>The Coaching Habit (Book)</title>
        <description>&lt;p&gt;Most of us default to advice-giving. Someone comes to us with a problem, and before they’ve even finished their sentence, we’re already formulating a solution. It feels helpful. It feels efficient. But Michael Bungay Stanier argues it’s actually one of the most counterproductive habits we carry into our work and relationships. His book &lt;a href=&quot;https://www.amazon.com/Coaching-Habit-Less-Change-Forever/dp/0978440749&quot;&gt;&lt;em&gt;The Coaching Habit: Say Less, Ask More &amp;amp; Change the Way You Lead Forever&lt;/em&gt;&lt;/a&gt; offers a deceptively simple framework for breaking free from what he calls our “advice monster” - and it all comes down to seven questions.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/the-coaching-habit-book/image.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source: &lt;a href=&quot;https://sketchyideas.co/the-coaching-habit-sketchnote/&quot;&gt;https://sketchyideas.co/the-coaching-habit-sketchnote/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The Kickstart Question: “What’s on your mind?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is how you open almost any conversation. It’s broad enough to give the other person control, but focused enough to avoid aimless small talk. It invites people to go straight to what matters most to them. Stanier suggests pairing it with what he calls the “3P” framework - you can ask whether the challenge is about a &lt;em&gt;project&lt;/em&gt;, a &lt;em&gt;person&lt;/em&gt;, or a &lt;em&gt;pattern&lt;/em&gt; of behavior. This helps focus the conversation without you having to guess what someone needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The AWE Question: “And what else?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Possibly the most important question in the entire book. “And what else?” - or AWE - does three things. It stops you from latching onto the first thing someone says. It generates more options and ideas. And it buys you time before your advice monster takes over. Stanier recommends asking it at least three times in any coaching conversation. The first answer is rarely the best or most complete answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The Focus Question: “What’s the real challenge here for &lt;em&gt;you&lt;/em&gt;?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the question that cuts through the noise. Conversations often swirl around multiple issues, vague frustrations, and abstract complaints. “What’s the real challenge here for you?” forces specificity. The words “for you” are critical - they bring the focus back to the individual rather than letting them deflect onto circumstances or other people. This is often where the real coaching begins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The Foundation Question: “What do &lt;em&gt;you&lt;/em&gt; want?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It sounds simple, but most people struggle to articulate what they actually want. We’re much better at describing what we don’t want, what’s frustrating us, or what other people should be doing differently. This question demands clarity. And when someone names what they want, it shifts the conversation from problem-dwelling to forward motion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. The Lazy Question: “How can I help?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Stanier calls this the “lazy” question not because it’s low-effort, but because it saves you from doing unnecessary work. Instead of guessing what someone needs from you - and often guessing wrong - you simply ask. It’s also a subtle accountability move: it forces the other person to make a direct, specific request rather than leaving you to assume responsibility for their problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. The Strategic Question: “If you’re saying yes to this, what are you saying no to?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a question about tradeoffs, and it’s one that most teams desperately need. We live in a culture of perpetual overcommitment. Saying yes to something new without acknowledging what it displaces is a recipe for burnout and mediocre execution. This question brings the hidden cost of commitments into the open. It applies to projects, meetings, priorities, and even personal boundaries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. The Learning Question: “What was most useful for you?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This closing question does double duty. For the other person, it creates a moment of reflection that helps solidify whatever insight emerged during the conversation. For you, it provides feedback on what actually landed. It also ends the conversation on a note of value, reinforcing the habit of having these kinds of exchanges in the first place.&lt;/p&gt;
</description>
        <pubDate>Wed, 18 Mar 2026 20:16:30 +0000</pubDate>
        <link>https://woutervanranst.github.io/the-coaching-habit-book/</link>
        <guid isPermaLink="true">https://woutervanranst.github.io/the-coaching-habit-book/</guid>
        
        
      </item>
    
      <item>
        <title>Coaching with Context: Applying Situational Leadership in 1-on-1s</title>
        <description>&lt;p&gt;&lt;em&gt;Good coaching isn’t about having all the answers - it’s about asking the right questions at the right moment, in the right way. That’s exactly what Situational Leadership teaches us.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;what-is-situational-leadership&quot;&gt;What Is Situational Leadership?&lt;/h2&gt;

&lt;p&gt;In 1977, Paul Hersey and Ken Blanchard introduced a deceptively simple but powerful idea: there is no single best leadership style. Instead, effective leaders adapt their approach to match the &lt;strong&gt;readiness level&lt;/strong&gt; - or “maturity” - of the person in front of them.&lt;/p&gt;

&lt;p&gt;The model defines maturity not as age or seniority, but as a combination of three things: the person’s &lt;strong&gt;experience and skills&lt;/strong&gt;, their &lt;strong&gt;knowledge&lt;/strong&gt; of the task, and their &lt;strong&gt;motivation&lt;/strong&gt; to do it independently. Someone brand new to a role, even a seasoned professional who has switched domains, may score low on maturity for that particular context. Conversely, someone who has been in the same team for years may be highly skilled but low in confidence after a setback - which changes the coaching dynamic entirely.&lt;/p&gt;

&lt;p&gt;The core insight is that leadership behaviour needs to be adjusted continuously, not set once at the start of a relationship.&lt;/p&gt;

&lt;h2 id=&quot;the-four-styles&quot;&gt;The Four Styles&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/coaching-with-context-applying-situational-leadership-in-1-on-1s/image.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The model is built around two axes: &lt;strong&gt;directive behaviour&lt;/strong&gt; (task-focused, structured instruction) and &lt;strong&gt;supportive behaviour&lt;/strong&gt; (relationship-focused, emotional engagement). These combine into four distinct styles, each suited to a different developmental level:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;S1 - Directing (Telling).&lt;/strong&gt; High directive, low supportive. The leader makes the decisions, sets clear expectations, monitors closely, and provides step-by-step guidance. This style is appropriate for someone who is new to the task, lacks confidence, or is struggling to start. It is not about being controlling - it is about giving the structure needed to succeed.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;S2 - Coaching (Selling).&lt;/strong&gt; High directive, high supportive. The leader still drives decisions and provides clear instructions, but now actively listens, explains the reasoning behind tasks, and invites questions. The “selling” metaphor is apt: the coach needs to convince the coachee that they &lt;em&gt;can&lt;/em&gt; do this. This suits someone who is motivated and eager but still developing the skills to work independently.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;S3 - Supporting (Participating).&lt;/strong&gt; Low directive, high supportive. The leader steps back from task management but increases emotional support. Decisions are increasingly shared or handed over. This style fits someone who has the capability but is low in confidence - perhaps after a difficult project, a dip in motivation, or a period of high stress. The job here is not to instruct, but to restore belief.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;S4 - Delegating.&lt;/strong&gt; Low directive, low supportive. The leader trusts the person to own both the work and the approach. Check-ins exist mainly to stay informed and to celebrate achievements. This is the mode for experienced, motivated professionals who thrive on autonomy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One nuance worth noting: &lt;strong&gt;maturity is task-specific, not person-specific.&lt;/strong&gt; The same engineer might be an S4 in system design and an S1 when asked to lead a cross-functional project for the first time. A good coach holds this in mind constantly.&lt;/p&gt;

&lt;h2 id=&quot;your-first-meeting-diagnosing-where-someone-is&quot;&gt;Your First Meeting: Diagnosing Where Someone Is&lt;/h2&gt;

&lt;p&gt;Before you can coach someone well, you need to understand them. Camille Fournier, in &lt;a href=&quot;https://www.amazon.com/Managers-Path-Leaders-Navigating-Growth/dp/1491973897&quot;&gt;&lt;em&gt;The Manager’s Path&lt;/em&gt;&lt;/a&gt;, is direct about this: the first job when starting a new reporting relationship is to get to know the person quickly, so you can manage them well. She suggests a set of trust-building questions designed to surface the information a coach actually needs.&lt;/p&gt;

&lt;p&gt;Adapted for a first coaching session, these might sound like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;How do you like to receive feedback - in the moment, or with time to think first?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Are there management behaviours that you know, from experience, don’t work for you?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;What are you most excited about in your current role? What feels hardest?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Do you have any clear career goals you’d like me to know about?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;How will I know if you’re stuck, stressed, or frustrated?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These questions do two things at once. They build trust by showing genuine interest, and they give you the diagnostic signal you need to calibrate your style. Someone who says “I work best when I have clear expectations and regular check-ins” is probably in S1 or S2 territory. Someone who says “I just need to know what the goal is and then be left to figure out the path” is signalling S3 or S4.&lt;/p&gt;

&lt;p&gt;Fournier also recommends discussing &lt;strong&gt;how you’ll work together&lt;/strong&gt;: how often you’ll meet, how the coachee should flag problems, and what they can expect from you. This is not just administrative - it sets the tone that the 1-on-1 is &lt;em&gt;their&lt;/em&gt; time, not a reporting obligation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make this explicit.&lt;/strong&gt; After explaining the situational leadership framework, ask your coachee directly: &lt;em&gt;“Based on what I’ve described, where do you think you are right now - and how would you like me to coach you?”&lt;/em&gt; This conversation alone is enormously valuable. It positions the coachee as an active participant in the relationship, not a passive recipient of feedback. It also creates a shared language you can revisit as things evolve.&lt;/p&gt;

&lt;h2 id=&quot;the-1-on-1-by-level-structure-and-questions&quot;&gt;The 1-on-1 by Level: Structure and Questions&lt;/h2&gt;

&lt;p&gt;Once you’ve done the diagnostic work, the situational framework gives you concrete guidance on how to structure your regular 1-on-1s - not just &lt;em&gt;what&lt;/em&gt; to discuss, but &lt;em&gt;how&lt;/em&gt; to engage.&lt;/p&gt;

&lt;p&gt;Fournier describes several distinct styles of 1-on-1, including the to-do list meeting, the catch-up, the feedback meeting, and the progress report. The right blend depends heavily on where your coachee sits on the maturity spectrum.&lt;/p&gt;

&lt;h2 id=&quot;s1---directing-the-structured-check-in&quot;&gt;S1 - Directing: The Structured Check-In&lt;/h2&gt;

&lt;p&gt;At this level, the coachee needs clarity above everything else. The 1-on-1 should feel more structured and coach-led, at least initially. Fournier notes that a very junior person who has just joined the team “might appreciate more time” - and the purpose of that time is orientation, not open-ended conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended structure:&lt;/strong&gt; Open with a brief human check-in. Then move into a structured review of current tasks: what’s clear, what’s not, and where the coachee is stuck. Close with explicit agreements about next steps and what “done” looks like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Questions to ask:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Walk me through what you’re working on. Where are you right now?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;What’s the most confusing part of the task?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;What do you think you need to do next? Let me check whether that aligns with the goal.&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Is there anything about how this team or this role works that still feels unclear?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;What would help you feel more confident going into next week?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The directive element here is not about micromanagement - it is about making the path visible. If something is misunderstood, say it again in a different way.&lt;/p&gt;

&lt;h2 id=&quot;s2---coaching-the-exploratory-dialogue&quot;&gt;S2 - Coaching: The Exploratory Dialogue&lt;/h2&gt;

&lt;p&gt;At this level, the coachee has enthusiasm and growing capability, but still needs structure. The 1-on-1 shifts from instruction toward &lt;strong&gt;guided discovery&lt;/strong&gt;. The coach still makes the important calls, but increasingly invites the coachee to think through the reasoning alongside them.&lt;/p&gt;

&lt;p&gt;This is Fournier’s “feedback meeting” mode - where the manager uses the 1-on-1 to reinforce strengths, surface development areas, and help the coachee build a clearer picture of where they’re going. She recommends holding these kinds of sessions at regular intervals, especially for early-career team members.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended structure:&lt;/strong&gt; Start with what’s going well - specific, recent, genuine. Spend the bulk of the meeting exploring one or two areas of growth in depth, using questions rather than pronouncements. End with a short forward-looking conversation about what the coachee wants to tackle next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Questions to ask:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;You handled [specific situation] this week - what was your thought process there?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;If you were to approach that differently, what might you try?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;What’s one thing you want to get better at in the next month? What would that require?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Where do you feel the gap most - is it skills, knowledge, or confidence?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;What can I do to help you get there faster?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The S2 coach “sells” the coachee on their own potential. The 1-on-1 is the primary space to do that - not by flattering, but by asking questions that help the coachee see their own progress.&lt;/p&gt;

&lt;h2 id=&quot;s3---supporting-the-human-conversation&quot;&gt;S3 - Supporting: The Human Conversation&lt;/h2&gt;

&lt;p&gt;At this level, task direction is largely unnecessary. The coachee knows how to do the work. What they need is a coach who shows up as an ally, not a manager. Fournier describes this as the “catch-up” style: driven by the coachee’s agenda, with the coach primarily listening and supporting rather than directing.&lt;/p&gt;

&lt;p&gt;The risk at this level is that the 1-on-1 becomes a complaint session or a form of therapy. The goal is to give the coachee space to bring up what &lt;em&gt;they&lt;/em&gt; feel is important - but to gently redirect energy from problems toward solutions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended structure:&lt;/strong&gt; Let the coachee lead the agenda. Ask at the start what they want to cover. After listening, reflect back what you’ve heard, ask one or two deeper questions, and then discuss options together. Don’t over-direct - offer perspective when invited.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Questions to ask:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;What’s on your mind this week?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;You seem a bit stretched at the moment - what’s actually going on?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;What would help you feel more energised about this work again?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Is there a decision you’re stuck on that you want to think through together?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;What past wins can you draw on here? You’ve done hard things before.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fournier’s note on senior employees is relevant here: as you become more senior, the amount of personal feedback you get is likely to decrease. The S3 coachee may not need much task feedback, but they do need someone who genuinely cares and creates space for honest conversation. That’s what the 1-on-1 is for.&lt;/p&gt;

&lt;h2 id=&quot;s4---delegating-the-strategic-touchpoint&quot;&gt;S4 - Delegating: The Strategic Touchpoint&lt;/h2&gt;

&lt;p&gt;At this level, the coachee is operating with full autonomy. The 1-on-1 is not about task management - it shifts toward strategy, longer-term growth, and staying connected as humans. Fournier points out that for senior people, the 1-on-1 increasingly becomes the coachee’s responsibility to drive. The coach’s role is to remain available, celebrate progress, and ensure the relationship doesn’t atrophy simply because things are going well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A caution worth making explicit:&lt;/strong&gt; don’t spend all your attention on team members who are struggling while neglecting your strongest performers. Regular 1-on-1s with S4 coachees are still essential.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended structure:&lt;/strong&gt; Shorter and more flexible. Let the coachee fully own the agenda. The coach asks big-picture, forward-looking questions and offers perspective from the organisational level that the coachee may not have direct visibility into.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Questions to ask:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;What’s the most interesting challenge you’re working on right now?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Is there anything I could be doing to make your work easier or more impactful?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Looking 6-12 months out, what do you want to be doing? Are we on track?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Where do you want to stretch yourself next?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Is there anything happening in the team or company I should know about from your vantage point?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fournier’s advice rings true here: it doesn’t have to just be a focus on the next skill or promotion. Show that you are invested in helping them now and in the future.&lt;/p&gt;

&lt;h2 id=&quot;closing-thought-the-map-is-not-the-territory&quot;&gt;Closing Thought: The Map Is Not the Territory&lt;/h2&gt;

&lt;p&gt;Situational leadership is a framework, not a formula. Real people don’t sit neatly in quadrants, and the same person will move between levels across different tasks, projects, or life stages. The model’s value is not in labelling people - it is in reminding coaches to stay curious, to keep asking &lt;em&gt;what does this person actually need right now?&lt;/em&gt;, and to have the flexibility to shift their style in response.&lt;/p&gt;

&lt;p&gt;As Fournier writes in &lt;em&gt;The Manager’s Path&lt;/em&gt;, the best managers are the ones who actively help people grow, who help you navigate difficult situations, and who help you figure out what you need to learn. That kind of management doesn’t follow a fixed script. It follows the person.&lt;/p&gt;
</description>
        <pubDate>Mon, 09 Mar 2026 19:44:27 +0000</pubDate>
        <link>https://woutervanranst.github.io/coaching-with-context-applying-situational-leadership-in-1-on-1s/</link>
        <guid isPermaLink="true">https://woutervanranst.github.io/coaching-with-context-applying-situational-leadership-in-1-on-1s/</guid>
        
        
      </item>
    
      <item>
        <title>Classic Problems Have Classic Solutions</title>
        <description>&lt;p&gt;In 2006, sitting in my 2nd Bachelor year civil engineering in the course “Productinnovatie &amp;amp; IndustriEle Marketing”, by prof. &lt;a href=&quot;https://www.kuleuven.be/wieiswie/nl/person/00016263&quot;&gt;Joost Duflou&lt;/a&gt;, I encountered an idea that quietly rewired how I think. He drew a simple diagram:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Domain-specific problem  -&amp;gt;  Generalized (abstract) problem
                                         v
Domain-specific solution &amp;lt;-  TRIZ suggestions
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/classic-problems-have-classic-solutions/image-1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The gist&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That diagram stuck. It’s been twenty years and I still think about it.&lt;/p&gt;

&lt;h3 id=&quot;what-triz-is&quot;&gt;What TRIZ Is&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/TRIZ&quot;&gt;TRIZ&lt;/a&gt; (&lt;em&gt;Teoriya Resheniya Izobretatelskikh Zadach&lt;/em&gt; - Theory of Inventive Problem Solving) was developed by Soviet engineer Genrich Altshuller, working at the USSR navy patent office from 1946. He analyzed hundreds of thousands of patents across engineering disciplines and found something striking: virtually all hard technical problems are instances of a small, finite set of universal contradictions.&lt;/p&gt;

&lt;p&gt;The core tension TRIZ names is what it calls a &lt;strong&gt;system conflict&lt;/strong&gt;: improving one property of a system degrades another. Reliability vs. complexity. Productivity vs. accuracy. Strength vs. formability. These feel unique and local when you’re inside them. They’re not.&lt;/p&gt;

&lt;p&gt;Altshuller catalogued roughly &lt;strong&gt;1,250 typical system conflicts&lt;/strong&gt;, mapping them to &lt;strong&gt;39 engineering parameters&lt;/strong&gt; and showing that only &lt;strong&gt;40 inventive principles&lt;/strong&gt; are needed to resolve them. The &lt;strong&gt;contradiction matrix&lt;/strong&gt; is the practical tool: look up the parameter you want to improve and the one that suffers, and it tells you which principles have historically worked.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/classic-problems-have-classic-solutions/image-2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The TRIZ contradiction matrix&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/classic-problems-have-classic-solutions/image-3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The full contradiction matrix&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;TRIZ also identified &lt;strong&gt;technology evolution trends&lt;/strong&gt; - predictable trajectories that technical systems follow. The segmentation trend, for instance, runs from monolithic solid -&amp;gt; segmented -&amp;gt; granules -&amp;gt; powder -&amp;gt; aerosol. Sugar products follow it. Measuring tools follow it. Shoe soles follow it. Knowing where your technology sits on that curve tells you where it’s headed.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/classic-problems-have-classic-solutions/image-4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;TRIZ segmentation trend applied to sugar products&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;triz-is-everywhere&quot;&gt;TRIZ is everywhere&lt;/h3&gt;

&lt;p&gt;Twenty years later I’m a software architect, and the abstraction bridge shows up everywhere.&lt;/p&gt;

&lt;p&gt;The CAP theorem is pure TRIZ: you want to improve consistency, but that degrades availability under partition. It’s a system conflict. The theorem doesn’t give you a solution - it gives you a &lt;em&gt;class&lt;/em&gt; of problem, and decades of distributed systems research have filled in the 40 inventive principles for that specific contradiction. When I recognize a caching design as a consistency vs. availability trade-off, I’ve already stepped into the abstract problem space. The solution library is right there.&lt;/p&gt;

&lt;p&gt;The same pattern runs through much of the catalogue of &lt;a href=&quot;https://en.wikipedia.org/wiki/Design_Patterns&quot;&gt;design patterns&lt;/a&gt;. &lt;strong&gt;Mediator&lt;/strong&gt; resolves the contradiction between component autonomy and system coordination. The &lt;strong&gt;Circuit Breaker&lt;/strong&gt; resolves availability vs. resilience. &lt;strong&gt;CQRS&lt;/strong&gt; resolves the tension between write consistency and read performance by separating the two concerns entirely. The &lt;strong&gt;Saga pattern&lt;/strong&gt; navigates distributed transaction consistency vs. availability. These patterns aren’t clever tricks - they’re named solutions to named contradictions, which is exactly the TRIZ philosophy made concrete.&lt;/p&gt;

&lt;p&gt;And it’s not just old problems. Consider DeepSeek R1 achieving essentially the same reasoning quality as OpenAI’s o1, but at a fraction of the energy and compute cost: reasoning capability vs. resource consumption. DeepSeek resolved it by applying two TRIZ principles in combination. &lt;strong&gt;Principle 1 (Segmentation)&lt;/strong&gt; - through a Mixture of Experts architecture, only the relevant subset of the network activates per token, so you get full capability without burning the full model every time. &lt;strong&gt;Principle 10 (Prior Action)&lt;/strong&gt; - through knowledge distillation, the model pre-loads reasoning patterns from a stronger teacher model rather than discovering them from scratch through expensive training. The conflict isn’t solved by brute force. It’s resolved by changing the structure of the problem - exactly what TRIZ prescribes.&lt;/p&gt;

&lt;p&gt;That’s the quote I keep coming back to: &lt;strong&gt;classic problems have classic solutions&lt;/strong&gt;.&lt;/p&gt;
</description>
        <pubDate>Thu, 05 Mar 2026 20:34:52 +0000</pubDate>
        <link>https://woutervanranst.github.io/classic-problems-have-classic-solutions/</link>
        <guid isPermaLink="true">https://woutervanranst.github.io/classic-problems-have-classic-solutions/</guid>
        
        
      </item>
    
      <item>
        <title>Who&apos;s Got The Monkey</title>
        <description>&lt;h3 id=&quot;hbrs-whos-got-the-monkey&quot;&gt;HBR’s Who’s Got The Monkey&lt;/h3&gt;

&lt;p&gt;In 1974, William Oncken Jr. and Donald L. Wass published a short article in the Harvard Business Review called &lt;em&gt;“Management Time: Who’s Got the Monkey?”&lt;/em&gt; It went on to become one of the two best-selling HBR reprints of all time. I often mention this paper to my more junior colleagues but they are discouraged by the seven-page PDF, so here is the shorter version.&lt;/p&gt;

&lt;p&gt;The paper is still very relevant today because the problem it describes has not gone away. If anything, in a world of Slack messages, constant stand-ups, and always-on collaboration, the monkey has more places to jump than ever before.&lt;/p&gt;

&lt;h3 id=&quot;the-central-metaphor-the-monkey&quot;&gt;The Central Metaphor: The Monkey&lt;/h3&gt;

&lt;p&gt;A “monkey” is the next action on a problem. It is not the problem itself - it is &lt;em&gt;who has to make the next move&lt;/em&gt;. The entire article revolves around one observation: in most manager-subordinate interactions, the next move quietly shifts from the subordinate to the manager, without either party fully noticing.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/whos-got-the-monkey/image.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sad boss&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this looks like in practice:&lt;/strong&gt; A teammate catches you after standup and says, “Hey, we have a problem with the deployment pipeline - I’m not sure what to do.” You reply, “Let me think about it and get back to you.” Congratulations: the monkey just jumped from their back to yours. They walk away free. You walk away with another task on your list. And later, &lt;em&gt;they&lt;/em&gt; will be the one checking in on &lt;em&gt;you&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-three-types-of-time&quot;&gt;The Three Types of Time&lt;/h3&gt;

&lt;p&gt;Oncken and Wass break a manager’s time into three buckets:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Boss-imposed time&lt;/strong&gt; is the work your own manager requires of you.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;System-imposed time&lt;/strong&gt; covers cross-functional obligations - peer requests, process compliance, organisational coordination.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Self-imposed time&lt;/strong&gt; is everything else: things you initiate or agree to do. This is the only bucket you actually control.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight: your leverage as a manager lives in discretionary time. Every monkey you accept from a report shrinks that pool. Accept enough of them, and you have zero time left for the strategic work that only you can do.&lt;/p&gt;

&lt;h3 id=&quot;how-monkeys-jump-four-patterns-to-recognise&quot;&gt;How Monkeys Jump: Four Patterns to Recognise&lt;/h3&gt;

&lt;p&gt;The paper walks through four scenarios that are instantly recognisable in any modern workplace:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;The hallway handoff.&lt;/strong&gt; Someone raises a problem in passing. You say, “Let me think about it.” The monkey is now yours.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The memo trap.&lt;/strong&gt; You ask someone to write up their thinking. They do. Now the document is in your inbox, and the next move is yours.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The open-ended offer.&lt;/strong&gt; You tell someone, “Just let me know how I can help.” They cannot move until you review their work, but your review queue is perpetually full.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The draft-first mistake.&lt;/strong&gt; A new initiative needs objectives. You say, “I will draft something for us to discuss.” You now own the next move on &lt;em&gt;their&lt;/em&gt; project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In every case, the transfer is subtle. Both parties walk away believing they are being collaborative. But the operational reality is that the subordinate is now idle and the manager is now busier.&lt;/p&gt;

&lt;h3 id=&quot;the-fix-five-degrees-of-initiative&quot;&gt;The Fix: Five Degrees of Initiative&lt;/h3&gt;

&lt;p&gt;Oncken and Wass propose a simple ladder of initiative that any team member can operate on:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Wait until told what to do.&lt;/li&gt;
  &lt;li&gt;Ask what to do.&lt;/li&gt;
  &lt;li&gt;Recommend an action and execute once approved.&lt;/li&gt;
  &lt;li&gt;Act first and inform immediately after.&lt;/li&gt;
  &lt;li&gt;Act independently and report routinely.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The paper’s prescription is blunt: levels 1 and 2 should be off-limits for any professional. The goal is to operate at level 3 or above - come with a recommendation, not just a problem.&lt;/p&gt;

&lt;h3 id=&quot;the-five-rules-for-care-and-feeding-of-monkeys&quot;&gt;The Five Rules for “Care and Feeding of Monkeys”&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Rule 1 - Feed them or shoot them.&lt;/strong&gt; Every open item either gets active attention or gets killed.&lt;br /&gt;
&lt;strong&gt;Rule 2 - Cap the population.&lt;/strong&gt; You can only sustain a finite number of active items.&lt;br /&gt;
&lt;strong&gt;Rule 3 - Feed by appointment only.&lt;/strong&gt; Do not chase down status updates ad hoc.&lt;br /&gt;
&lt;strong&gt;Rule 4 - Feed face-to-face or by phone, never by email.&lt;/strong&gt; Asynchronous communication hands the next move back to the manager.&lt;br /&gt;
&lt;strong&gt;Rule 5 - Every monkey needs a next feeding time and an agreed level of initiative.&lt;/strong&gt; No ambiguity.&lt;/p&gt;

&lt;h3 id=&quot;the-covey-update-empowerment-is-harder-than-it-sounds&quot;&gt;The Covey Update: Empowerment Is Harder Than It Sounds&lt;/h3&gt;

&lt;p&gt;When HBR republished the article in 1999, Stephen Covey added a commentary acknowledging that the original advice - “give the monkey back” - was necessary but not sufficient. You cannot simply refuse to take on a subordinate’s problem if they lack the skill or confidence to solve it themselves. Effective delegation requires investing in people, building trust, and creating a culture where initiative is rewarded rather than punished.&lt;/p&gt;
</description>
        <pubDate>Thu, 26 Feb 2026 21:14:47 +0000</pubDate>
        <link>https://woutervanranst.github.io/whos-got-the-monkey/</link>
        <guid isPermaLink="true">https://woutervanranst.github.io/whos-got-the-monkey/</guid>
        
        
      </item>
    
      <item>
        <title>The Ackoff Lectures: A Blog Series on Systems Thinking, Management, and the World We&apos;re Building</title>
        <description>&lt;p&gt;&lt;em&gt;This series is based on the Ackoff Tapes - video lectures by Russell L. Ackoff, professor at the Wharton School of the University of Pennsylvania, consultant to over 250 corporations and 50 government agencies, and one of the twentieth century’s most consequential thinkers. The tapes were recorded for the Naval Education and Training Organization and cover systems thinking, management philosophy, organisational design, and education. Each post follows the arc of a different tape, but the ideas build on one another. Read them in order.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We are in the middle of a transition from one historical age (the Machine Age) to another (the Systems Age), and most organizations - including the Navy - are still managing using ideas inherited from the old one. The purpose of these days is to understand what’s changing and why, and to adopt a fundamentally different mode of thinking and planning.&lt;/p&gt;

&lt;p&gt;DISCLAIMER: I watched the Ackoff Tapes back in 2023 and found them so impactful that I wanted them in readable/searchable/sharable form. The below blog post(s) are based on Whisper transcriptions of the recordings and have been compiled into a series of posts by Claude Cowork, unedited.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/the-ackoff-lectures-a-blog-series-on-systems-thinking-management-and-the-world-were-building/image-1.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Russell L. Ackoff&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tapes 1 &amp;amp; 2&lt;/strong&gt; - &lt;em&gt;The philosophical foundation.&lt;/em&gt; Ackoff opens with an epistemological distinction that sets the tone for everything: information, knowledge, and understanding are not the same thing, and almost all education operates at the wrong level. He then launches his historical thesis: the Machine Age worldview - rooted in the Renaissance, shaped by Descartes and Newton - gave us analysis, reductionism, and the idea that you understand something by taking it apart. That worldview is now breaking down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tape 3&lt;/strong&gt; - &lt;em&gt;The technological shift.&lt;/em&gt; The Systems Age has its own technological expression: not mechanization (replacing human muscle) but automation (replacing human mind). Instrumentation, communication, and the computer - taken together - created a technology of the mind. Ackoff then pivots to the concept of &lt;em&gt;development&lt;/em&gt; (as distinct from growth), illustrated with a vivid personal story from WWII, building a rest camp in the Philippines with 250 native workers and no materials - a parable about resourcefulness over resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tape 4&lt;/strong&gt; - &lt;em&gt;How organizations and work must change.&lt;/em&gt; He traces the evolution of how companies have tried to manage human motivation: job rotation -&amp;gt; job enrichment -&amp;gt; semi-autonomous work groups (the Volvo Kalmar factory being the landmark example). The lesson: each solution works for a while, then stops - so you need a portfolio of approaches and a systems view, not a single fix. Education and training emerge as inseparable from good work design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tape 5&lt;/strong&gt; - &lt;em&gt;The “Reactive” organization.&lt;/em&gt; Ackoff introduces his three-way typology of organizational orientations. “Reactive” managers are past-oriented, nostalgic, problem-focused, and plan by cataloguing deficiencies and submitting project budgets upward. He dissects the budget ritual with sharp irony - the standard multiplier, the boss correcting spelling, the safety factor added at each level - and shows why this produces organizational paralysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tape 6&lt;/strong&gt; - &lt;em&gt;The “Preactive” organization.&lt;/em&gt; The dominant American management style of the postwar decades: technology-worshipping, forecast-obsessed, top-down, MBO-driven. Change is opportunity; youth is prized; the sports team is the metaphor. Ackoff shows why this fails too: forecasting accuracy degrades precisely as the environment becomes more turbulent - which is exactly when you need it most.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tape 7&lt;/strong&gt; - &lt;em&gt;The “Interactive” organization and idealized redesign.&lt;/em&gt; The synthesis and the heart of the seminar. Ackoff introduces “interactive planning” - not reactive (fix what’s broken) or preactive (predict and prepare) but designing the future you want and working backwards. The key tool is &lt;em&gt;idealized redesign&lt;/em&gt;: imagine the system was destroyed last night and you are free to replace it with anything. The touchstone telephone story (his accidental attendance at a Bell Labs meeting in 1951, which led to push-button dialing and eventually the entire digital telephony revolution) is his central illustration of how this method works in practice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tape 8&lt;/strong&gt; - &lt;em&gt;Education, creativity, and closing.&lt;/em&gt; He applies the same systems thinking to education - a Mexican university he helped redesign from scratch, the cooperative program, the trimester calendar, building for two-thirds occupancy. He returns to the epistemological theme from Tape 1: schools teach kids to answer questions (and give the answer the examiner expects), not to formulate and manage “messes.” The nine-dot problem with his daughter is the emblem of how institutions suppress creativity. He closes with a joke from his seven-year-old son.&lt;/p&gt;

&lt;h2 id=&quot;tape-1--2---the-age-were-leaving-behind-from-the-machine-age-to-the-systems-age&quot;&gt;Tape 1 &amp;amp; 2 - The Age We’re Leaving Behind: From the Machine Age to the Systems Age&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;(Based on&lt;/em&gt; &lt;a href=&quot;https://vimeo.com/257050065&quot;&gt;&lt;em&gt;Ackoff Tapes 1 &amp;amp; 2&lt;/em&gt;&lt;/a&gt;&lt;em&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There is a question Ackoff says his students keep asking him. It is, he admits, the simplest question in the world to ask and perhaps the most difficult to answer. The question is this: &lt;strong&gt;what in the world is happening in the world?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not what the latest quarterly figures say. Not what the news cycle is currently amplifying. At a very general level - philosophically, historically, culturally - what is actually going on?&lt;/p&gt;

&lt;p&gt;Ackoff’s answer, delivered with the unhurried confidence of someone who has spent decades thinking about it, is both clarifying and unsettling. We are, he says, in the early stages of a change of age. Not a change within an age - a change of age. A transformation of the kind that happens once every several hundred years, the kind that reshuffles the foundations of how human beings understand reality itself.&lt;/p&gt;

&lt;p&gt;To appreciate what that means, you need to understand what a historical age actually is.&lt;/p&gt;

&lt;h2 id=&quot;the-world-through-which-we-see-the-world&quot;&gt;The World Through Which We See the World&lt;/h2&gt;

&lt;p&gt;Every historical era, Ackoff explains, is defined not by its technology or its politics but by its dominant worldview - what the Germans call a &lt;em&gt;Weltanschauung&lt;/em&gt;. A shared, largely unarticulated set of assumptions about the nature of reality. We all have one. The trouble is that we absorb it by osmosis as we grow up, which means it becomes so deeply ingrained that we stop being able to see it. It is not the picture we look at. It is the lens we look through.&lt;/p&gt;

&lt;p&gt;When a worldview begins to crack - when the dilemmas it generates can no longer be resolved within its own terms - a historical age starts to end. The last time this happened to Western civilisation on a comparable scale was the Renaissance: the transformation from the Middle Ages to what Ackoff calls the Machine Age.&lt;/p&gt;

&lt;p&gt;The medieval worldview makes sense when you understand the conditions that produced it. Expected lifespan was 27 years. Forty percent of children died in childhood. The average person never traveled more than four miles from their place of birth. Over ninety-five percent of people lived in abject poverty. In that world, the most urgent question was not how to improve conditions here. It was what to make of the fact that this life is brutal and brief. The church’s answer - that earthly life is merely preparation for an infinite afterlife, and therefore not worth worrying too much about - was not cynicism. It was a rational response to the available evidence.&lt;/p&gt;

&lt;p&gt;What ended that worldview was exposure. The Crusades sent large numbers of European men across the face of a continent they had never seen, into contact with cultures they had never imagined. Curiosity - which had actually been declared a sin by the medieval church - became a virtue. The New Humanism emerged. Ancient Greek learning, transmitted back to Europe through Arab scholarship, was rediscovered. Aristotle and Plato returned. And with them came a new belief, both liberating and totalising: that the universe was capable of being completely understood.&lt;/p&gt;

&lt;p&gt;That belief - held with sincere conviction for nearly four hundred years - became the first pillar of the Machine Age worldview.&lt;/p&gt;

&lt;h2 id=&quot;the-machine-age-a-philosophy-built-on-two-pillars&quot;&gt;The Machine Age: A Philosophy Built on Two Pillars&lt;/h2&gt;

&lt;p&gt;The second pillar was a method: &lt;strong&gt;analysis&lt;/strong&gt;. Watch a child given an unfamiliar object - a clock, a toy, something they have never seen before. What do they do? They take it apart. They try to understand the parts separately. Then they try to piece the understanding of the parts back into an understanding of the whole. That intuitive process, systematised and elevated into scientific doctrine, is analysis. Break it down, understand the pieces, aggregate upward.&lt;/p&gt;

&lt;p&gt;From these two foundations - complete understandability and analysis - a worldview crystallised into four interlocking doctrines.&lt;/p&gt;

&lt;p&gt;The first was &lt;strong&gt;reductionism&lt;/strong&gt;: the belief that everything in the universe, if taken apart far enough, reduces to simple, indivisible elements. Atomic theory in physics. The cell in biology. The phoneme in linguistics. Science became, for four centuries, a crusade in search of the fundamental element - the holy grail from which understanding of everything else would flow.&lt;/p&gt;

&lt;p&gt;The second doctrine was &lt;strong&gt;determinism&lt;/strong&gt; - more specifically, the belief that all relationships in the universe reduce to a single type: cause and effect. A cause is something necessary and sufficient for its effect. This was taken to be the complete grammar of reality.&lt;/p&gt;

&lt;p&gt;From those two, two further doctrines followed almost automatically. The third was that &lt;strong&gt;explanation required no reference to the environment&lt;/strong&gt;. If a cause is sufficient for its effect, nothing outside the relationship is needed to explain it. Hence the Law of Freely Falling Bodies - notable for that word &lt;em&gt;freely&lt;/em&gt;, meaning in a vacuum, in the absence of an environment. All fundamental laws of physics describe behaviour in conditions no environment actually provides. That is not a bug; it is the design.&lt;/p&gt;

&lt;p&gt;The fourth doctrine was the &lt;strong&gt;inevitability of a First Cause&lt;/strong&gt; - an uncaused cause, something that set the chain in motion without itself being caused. Call it God, or call it the initial conditions of the universe. Either way, the logic required it.&lt;/p&gt;

&lt;p&gt;This worldview gave rise to extraordinary things. The scientific revolution. The industrial revolution. Germ theory. Engineering. Flight. Modern medicine. For four hundred years, the Machine Age worldview delivered compounding returns on its investment.&lt;/p&gt;

&lt;p&gt;But then, around the turn of the twentieth century, the cracks began to appear.&lt;/p&gt;

&lt;h2 id=&quot;the-dilemmas-the-machine-age-could-not-resolve&quot;&gt;The Dilemmas the Machine Age Could Not Resolve&lt;/h2&gt;

&lt;p&gt;The deterministic worldview, if taken seriously, eliminates free will. If every event is the necessary and sufficient consequence of its cause, then human choice is an illusion - your decision right now was determined at the Big Bang. Philosophers and theologians wrestled with this for centuries without resolution, because the dilemma cannot be resolved within the Machine Age framework. It is a dilemma, not a problem.&lt;/p&gt;

&lt;p&gt;Then came Heisenberg’s uncertainty principle, which demonstrated that complete knowledge of a physical system is not merely difficult but logically impossible. The act of observation changes what is observed. The foundational belief of the Machine Age - complete understandability - was shown to be false on its own terms, by science itself.&lt;/p&gt;

&lt;p&gt;And then came the insight that would eventually crystallise into systems theory: &lt;strong&gt;some properties of wholes cannot be found in any of their parts&lt;/strong&gt;. When you take a system apart to understand it through analysis, you destroy the very thing you are trying to understand. The life of a cell is not present in the molecules that compose it. The meaning of a word is not present in its letters. The personality of a person is not present in their organs. Analysis, the great method of the Machine Age, turns out to be capable of explaining everything &lt;em&gt;except&lt;/em&gt; the most interesting things.&lt;/p&gt;

&lt;h2 id=&quot;the-systems-age-and-the-new-conceptual-architecture&quot;&gt;The Systems Age and the New Conceptual Architecture&lt;/h2&gt;

&lt;p&gt;What replaces these doctrines is not their negation but their expansion.&lt;/p&gt;

&lt;p&gt;Reductionism gives way to &lt;strong&gt;expansionism&lt;/strong&gt; - the recognition that things can be understood not only by breaking them down but by seeing them as parts of larger containing systems. Understanding something now requires both analysis &lt;em&gt;and&lt;/em&gt; synthesis: you must look inward at the parts and outward at the whole in which it is embedded.&lt;/p&gt;

&lt;p&gt;Determinism gives way to a richer conception of causality. The philosopher Edgar Arthur Singer, Ackoff’s teacher, proposed a distinction that would reshape everything downstream. An acorn does not &lt;em&gt;cause&lt;/em&gt; an oak tree in the Machine Age sense - if it did, every acorn would become an oak, regardless of soil, water, or sunlight. The acorn is a &lt;em&gt;producer&lt;/em&gt; of the oak tree: necessary but not sufficient, requiring a cooperating environment. This might seem like wordplay, but its implications are profound. It breaks the closed loop of determinism. It makes purposefulness, and therefore genuine choice, conceptually possible again.&lt;/p&gt;

&lt;p&gt;From this flows a new taxonomy of systems:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Machines&lt;/strong&gt; have no purpose of their own. Their parts may have functions, but the system exists only to serve external purposes.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Organisms&lt;/strong&gt; have purposes of their own, but their parts do not.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Organisations&lt;/strong&gt; - the category most relevant to management - have purposes of their own, and so do their parts, and they are themselves embedded in larger purposeful systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not just a philosophical distinction. It is a diagnosis. And it explains why the management of organisations in the Machine Age went so systematically wrong - and why the problems it generated are the problems that now define our moment.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/the-ackoff-lectures-a-blog-series-on-systems-thinking-management-and-the-world-were-building/image-2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;why-this-matters-right-now&quot;&gt;Why This Matters Right Now&lt;/h2&gt;

&lt;p&gt;The crises of the modern era - what Ackoff calls the humanisation problem - are all, at their root, consequences of purposeful parts demanding that their larger systems serve &lt;em&gt;their&lt;/em&gt; purposes. The environmental movement is purposeful nature (or nature’s advocates) demanding that organisations serve it. The consumer movement is purposeful customers demanding that firms serve them. Third World nationalism, the women’s movement, the civil rights movement - all are purposeful parts of larger systems asserting that they will not be treated as mere components.&lt;/p&gt;

&lt;p&gt;You cannot manage organisations in this world using Machine Age thinking. You cannot break the challenges into isolated problems, optimise each part separately, and expect the whole to function. The parts are alive. They have purposes. They will resist. And they should.&lt;/p&gt;

&lt;p&gt;The next question - the question the rest of this series takes up - is: what does it look like to manage, plan, and lead in the Systems Age? What does it look like to design organisations that genuinely serve the purposes of society, of the organisation itself, and of every purposeful person within it?&lt;/p&gt;

&lt;p&gt;We are still, mostly, trying to answer that question with Machine Age tools. Ackoff spent his career proposing the alternative.&lt;/p&gt;

&lt;p&gt;The next post follows Tape 3, where he turns from philosophy to practice: what does the Systems Age demand of how we organise work, distribute wealth, and relate to the human beings inside our institutions?&lt;/p&gt;

&lt;h2 id=&quot;tape-3---organisations-as-living-systems-automation-development-and-the-stakeholder-firm&quot;&gt;Tape 3 - Organisations as Living Systems: Automation, Development, and the Stakeholder Firm&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;(Based on&lt;/em&gt; &lt;a href=&quot;https://vimeo.com/257526863&quot;&gt;&lt;em&gt;Ackoff Tape 3&lt;/em&gt;&lt;/a&gt;&lt;em&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the first post established the philosophical stakes - the transition from Machine Age to Systems Age thinking - this one is where the ideas meet the ground. Ackoff turns from worldviews to workplaces, and what he finds there is an institution - the corporation - that is almost perfectly designed for the wrong era.&lt;/p&gt;

&lt;p&gt;But he also finds, in the chaos and dissatisfaction of the late twentieth century, the raw materials for something better.&lt;/p&gt;

&lt;h2 id=&quot;the-technological-underpinning-why-automation-changes-everything&quot;&gt;The Technological Underpinning: Why Automation Changes Everything&lt;/h2&gt;

&lt;p&gt;Before getting to organisations, Ackoff pauses to name the technological force that makes the transition from Machine Age to Systems Age not merely philosophical but historically inevitable. That force is automation.&lt;/p&gt;

&lt;p&gt;To understand automation, you have to understand what preceded it. &lt;strong&gt;Mechanisation&lt;/strong&gt; - the first industrial revolution - applied energy to matter. It replaced human muscle. Steam engines, looms, locomotives: all of these extended the physical capacity of the body. Measurement and instrumentation came next, creating tools that could observe and record in ways human senses could not. Then came communication technologies that could transmit information across distance.&lt;/p&gt;

&lt;p&gt;But automation is something qualitatively different. When you combine instrumentation (sensing), computation (manipulating symbols), and communication (transmitting information), you get a system that can replace not muscle but &lt;em&gt;mind&lt;/em&gt;. Automation does not merely extend human capacity - it substitutes for human cognitive work. The bookkeeper, the typesetter, the draughtsman, the navigator: all find that their function can now be performed by a machine.&lt;/p&gt;

&lt;p&gt;This is not an argument about whether automation is good or bad. It is a statement about what it does structurally to work and to organisations. If machines can do an expanding range of cognitive work, then the nature of human value in organisations must change - or large numbers of people will simply be unnecessary.&lt;/p&gt;

&lt;p&gt;Ackoff identifies unemployment as the central social crisis generated by automation, and he frames it in a way that most economic commentators miss. A corporation does two things for society simultaneously: it &lt;strong&gt;produces wealth&lt;/strong&gt; and it &lt;strong&gt;distributes wealth&lt;/strong&gt;. Employment is the only mechanism that does both at once. When you pay someone to do work, you create value (production) and you put money in their pocket (distribution). Welfare, by contrast, only distributes - and it does so in a way that tends to corrode the dignity and development of the recipient.&lt;/p&gt;

&lt;p&gt;The political consequence is predictable and visible: governments find themselves under pressure to nationalise industries or expand public employment not because state ownership is ideologically preferred but because the alternative is a growing proportion of citizens with no mechanism through which to participate in economic life. This is not primarily a political problem. It is a systems design problem.&lt;/p&gt;

&lt;h2 id=&quot;development-vs-growth-the-most-important-distinction-youre-not-making&quot;&gt;Development vs. Growth: The Most Important Distinction You’re Not Making&lt;/h2&gt;

&lt;p&gt;Here Ackoff introduces a distinction that deserves far wider currency than it has received.&lt;/p&gt;

&lt;p&gt;We tend to talk about development and growth as though they are the same thing, or at least as though more growth automatically means more development. Ackoff insists they are fundamentally different concepts, and confusing them has enormous consequences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Growth&lt;/strong&gt; means an increase in size or amount. A tumour grows. A landfill grows. Growth is morally and practically neutral; whether it is desirable depends entirely on what is growing and why.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Development&lt;/strong&gt;, by contrast, is an increase in &lt;em&gt;capacity and desire to satisfy one’s own needs and the needs of others using whatever resources are available&lt;/em&gt;. It is about what you can do, not what you have. It is about the quality and effectiveness of your relationship to your circumstances.&lt;/p&gt;

&lt;p&gt;To illustrate the difference, Ackoff offers the figure of Robinson Crusoe - a man with almost no resources who nonetheless exhibits extraordinary development. Faced with an unprecedented situation, without tools, without infrastructure, without supply chains, he improvises solutions, learns rapidly, builds capabilities, and survives. Now contrast him with a billionaire who cannot manage their own emotional life, cannot sustain meaningful relationships, and cannot function without an elaborate support system. The billionaire has resources; Crusoe has development. They are not the same thing.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/the-ackoff-lectures-a-blog-series-on-systems-thinking-management-and-the-world-were-building/image-3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The implications are radical. &lt;strong&gt;No government can develop a population.&lt;/strong&gt; A government can provide resources, remove obstacles, and create enabling conditions. But development is inherently self-development. It happens when people encounter real challenges and engage with them using their own intelligence and agency. It is precisely what organisations - and educational institutions, as we will see in later posts - tend to destroy when they treat people as components to be managed rather than purposeful beings to be supported.&lt;/p&gt;

&lt;h2 id=&quot;the-stakeholder-map-seeing-the-organisation-from-outside-in&quot;&gt;The Stakeholder Map: Seeing the Organisation from Outside In&lt;/h2&gt;

&lt;p&gt;Ackoff now offers a reframing of the corporation that was, at the time he was delivering these lectures, genuinely radical, and that has since become mainstream in name if not always in practice. He asks us to think about the organisation not from the inside out - not from the perspective of what the firm is trying to achieve - but from the outside in: who are all the parties who have a stake in what this organisation does, and what does the relationship look like from each of their perspectives?&lt;/p&gt;

&lt;p&gt;He identifies six stakeholder groups:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Employees&lt;/strong&gt; bring their time and labour in; they expect money, purpose, and meaningful work out. &lt;strong&gt;Suppliers&lt;/strong&gt; bring goods and services in; they expect payment out. &lt;strong&gt;Customers&lt;/strong&gt; bring money in; they expect value-added goods or services out. &lt;strong&gt;Creditors&lt;/strong&gt; bring money now; they expect more money later. &lt;strong&gt;Investors&lt;/strong&gt; put money in; they expect returns over time. &lt;strong&gt;Government&lt;/strong&gt; provides infrastructure, the rule of law, and a functional society; it expects taxes and compliance in return.&lt;/p&gt;

&lt;p&gt;What this map reveals is that the corporation is not a thing - it is a web of relationships, each with its own logic, its own obligations, and its own legitimate claims. The Machine Age firm optimised for one stakeholder (shareholders, or sometimes management) and treated the rest as constraints to be managed. The Systems Age firm recognises that it must genuinely serve the purposes of all its stakeholders, not as a PR exercise but as a condition of its continued existence and social legitimacy.&lt;/p&gt;

&lt;h2 id=&quot;from-cog-to-colleague-the-evolution-of-how-we-treat-workers&quot;&gt;From Cog to Colleague: The Evolution of How We Treat Workers&lt;/h2&gt;

&lt;p&gt;One of the most practically valuable sections of this tape is Ackoff’s account of the successive waves of thinking about how workers should be treated within organisations. He traces an evolution - tentative, uneven, often reversed - from the purely mechanical view to something approaching genuine humanity.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;mechanical view&lt;/strong&gt; saw the worker as a replaceable part. The worker’s job was specified precisely, the motions standardised, the variability eliminated. Frederick Taylor’s scientific management is the archetype. It worked - up to a point - when the work was genuinely repetitive and when workers had no alternative. It generated efficiency in a narrow, measurable sense and alienation in every other sense.&lt;/p&gt;

&lt;p&gt;The first response was &lt;strong&gt;work structuring&lt;/strong&gt; - the recognition that giving workers some control over their immediate environment improved morale and reduced absenteeism. This was an improvement, but Ackoff is honest that its effects were temporary. Boredom returned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job rotation&lt;/strong&gt; - moving workers between tasks - addressed boredom somewhat, but again the relief was short-lived. The work itself remained unchallenging; you were just doing different unchallenging things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job enrichment&lt;/strong&gt; gave workers more complex and meaningful tasks - assembling a complete product rather than performing a single operation, for example. The Motorola pager plant is Ackoff’s example: when workers were given responsibility for assembling complete devices rather than working on assembly lines, productivity increased by thirty to sixty percent. This is not a marginal improvement. It is transformative.&lt;/p&gt;

&lt;p&gt;But Ackoff pushes further, to the most radical and most effective intervention: &lt;strong&gt;semi-autonomous work groups&lt;/strong&gt;. At Volvo’s Kalmar plant, there were no assembly lines. Groups of workers organised their own work, made their own decisions about scheduling and task allocation, and took collective responsibility for a complete segment of the production process. The result was a thirty-three percent improvement in output per person compared with conventional plants.&lt;/p&gt;

&lt;p&gt;The lesson is not that specific techniques work. The lesson is that the direction of travel matters. Every step that moves toward treating workers as purposeful beings with their own intelligence and judgment - rather than as components to be optimised - generates real and substantial gains. Not because it makes workers happy (though it often does), but because it unlocks capacities that the mechanical model systematically suppresses.&lt;/p&gt;

&lt;h2 id=&quot;the-circular-organisation-democracy-at-work&quot;&gt;The Circular Organisation: Democracy at Work&lt;/h2&gt;

&lt;p&gt;Ackoff’s most ambitious organisational proposal is what he calls the &lt;strong&gt;circular&lt;/strong&gt; or &lt;strong&gt;democratic organisation&lt;/strong&gt; - a structural innovation that builds genuine participation into the architecture of governance itself.&lt;/p&gt;

&lt;p&gt;The mechanism is simple. Every manager in the organisation has a board, consisting of themselves, their immediate superior, and all of their direct reports. Every manager sits on at least two boards - the one they chair and the one their superior chairs. The boards perform several functions: policy-making within their domain, planning, coordination across parallel units, and integration between levels.&lt;/p&gt;

&lt;p&gt;The fifth function - the most consistently resisted - is evaluation. The board evaluates the performance of its manager. In other words, no one can hold a position of authority without the ongoing consent of those they lead.&lt;/p&gt;

&lt;p&gt;This is not a utopian proposal. It is a functional mechanism for something organisations desperately need but rarely achieve: accountability that flows not just downward and upward but in all directions. It means that authority must be earned continuously through the quality of leadership, not merely held by virtue of title.&lt;/p&gt;

&lt;p&gt;Real examples exist. Ackoff describes a packaging company that implemented a ten-board system and Mexico’s Ministry of Public Works, where a similar structure transformed what had been a deeply dysfunctional bureaucracy. The common thread was that people at every level began to take genuine responsibility for the quality of the whole - because the structure gave them genuine power to affect it.&lt;/p&gt;

&lt;h2 id=&quot;what-this-adds-up-to&quot;&gt;What This Adds Up To&lt;/h2&gt;

&lt;p&gt;By the end of Tape 3, a portrait of the Systems Age organisation is beginning to emerge. It is an organisation that:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Recognises automation as an irreversible structural force and designs work around human development rather than human substitution&lt;/li&gt;
  &lt;li&gt;Thinks of growth and development as different objectives and prioritises the latter&lt;/li&gt;
  &lt;li&gt;Maps its obligations to all stakeholders, not just shareholders&lt;/li&gt;
  &lt;li&gt;Designs work so that the people doing it are genuinely engaged, developing, and contributing their full intelligence&lt;/li&gt;
  &lt;li&gt;Structures governance so that authority requires and depends on ongoing consent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is easy. Ackoff does not pretend it is. But each component can be shown, with concrete examples, to produce better results than the Machine Age alternative - not just for workers but for the organisations themselves.&lt;/p&gt;

&lt;p&gt;The next post follows Tape 4, where Ackoff turns to the question of planning - or rather, to why most of what organisations call planning is not really planning at all, and what a genuine alternative would look like.&lt;/p&gt;

&lt;h2 id=&quot;tape-4---planning-as-design-messes-dilemmas-and-the-radical-act-of-imagining-what-you-actually-want&quot;&gt;Tape 4 - Planning as Design: Messes, Dilemmas, and the Radical Act of Imagining What You Actually Want&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;(Based on&lt;/em&gt; &lt;a href=&quot;https://vimeo.com/257229228&quot;&gt;&lt;em&gt;Ackoff Tape 4&lt;/em&gt;&lt;/a&gt;&lt;em&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most organisational planning, Ackoff suggests with barely concealed impatience, is an elaborate ritual that produces impressive-looking documents that nobody uses. This is not a minor operational failing. It is a symptom of a fundamental conceptual error about what planning is for.&lt;/p&gt;

&lt;p&gt;To understand why planning goes wrong so systematically, you have to start with how organisations think about the problems they face.&lt;/p&gt;

&lt;h2 id=&quot;problems-messes-and-the-difference-that-changes-everything&quot;&gt;Problems, Messes, and the Difference That Changes Everything&lt;/h2&gt;

&lt;p&gt;In ordinary language, we tend to use the word “problem” loosely, as a synonym for “difficulty” or “challenge.” Ackoff wants to be more precise, because the distinctions he draws carry real practical consequences.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;problem&lt;/strong&gt;, properly speaking, is a solvable difficulty with a clear formulation and a definable solution. You can state what is unsatisfactory, you can imagine what satisfactory would look like, and you can find a path from here to there within your existing conceptual framework.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;dilemma&lt;/strong&gt; is different in kind. It is a difficulty that cannot be resolved within the existing framework. The free will problem for the Machine Age worldview is a dilemma, not a problem: no solution is possible without questioning the foundational assumptions. You cannot solve a dilemma; you can only dissolve it, by changing the framework within which it appears.&lt;/p&gt;

&lt;p&gt;But the most important concept is the &lt;strong&gt;mess&lt;/strong&gt; - and this is where Ackoff’s thinking becomes most directly relevant to anyone trying to lead an organisation. A mess is a &lt;em&gt;system of interacting problems&lt;/em&gt;. It is what organisations and institutions actually face. Every real challenge - declining market share, employee disengagement, regulatory pressure, technological disruption - is entangled with every other challenge. You cannot address one in isolation without changing the conditions affecting the others.&lt;/p&gt;

&lt;p&gt;The catastrophic mistake that organisations make is to extract individual problems from the mess, hand them to specialist teams or departments, solve them optimally in isolation, and then expect the organisation to perform better. It does not work. It cannot work. Optimal solutions to individual problems, when those problems are part of a system, will not add up to an optimal solution for the system. They may not even add up to an adequate one.&lt;/p&gt;

&lt;p&gt;The car park that is optimised separately from the building it serves, the supply chain optimised separately from the sales function, the training programme designed without reference to the work it is meant to support - these are all examples of the same error. Ackoff’s term for it is &lt;strong&gt;suboptimisation&lt;/strong&gt;, and it is the dominant mode of organisational problem-solving in the Machine Age.&lt;/p&gt;

&lt;h2 id=&quot;what-planning-usually-is---and-why-it-fails&quot;&gt;What Planning Usually Is - and Why It Fails&lt;/h2&gt;

&lt;p&gt;Most corporate planning follows a predictable logic. It begins with an analysis of the current situation, identifies deficiencies, sets targets for improvement, and builds a plan to close the gap. This sounds reasonable. The problem is that it begins with the present - with what is wrong - and tries to project outward from there.&lt;/p&gt;

&lt;p&gt;Ackoff calls this &lt;strong&gt;reactive&lt;/strong&gt; planning when it is oriented toward the past, and &lt;strong&gt;preactive&lt;/strong&gt; planning when it is oriented toward a predicted future. Both share the same fundamental flaw: they take existing constraints as given and build within them.&lt;/p&gt;

&lt;p&gt;The reactive planner says: here are the things that are broken; let us fix them. The preactive planner says: here is our forecast of where the market is going; let us position ourselves to capture it. Both treat the future as something to be extrapolated from or braced against, rather than something to be designed.&lt;/p&gt;

&lt;p&gt;There is also a practical problem that Ackoff identifies with characteristic directness. In one memorable anecdote, a CEO confesses that his organisation’s elaborate annual planning process produces a thick document that is never actually consulted. When asked why they continue to do it, he thinks for a moment and says: “I suppose it’s like studying Latin - it’s good for you.” This is a damning diagnosis. Planning has become a ritual that signals seriousness rather than a process that produces usable guidance.&lt;/p&gt;

&lt;h2 id=&quot;idealized-design-the-radical-alternative&quot;&gt;Idealized Design: The Radical Alternative&lt;/h2&gt;

&lt;p&gt;Ackoff’s alternative begins with a disarmingly simple question: &lt;strong&gt;if your organisation were destroyed tonight and you had to rebuild it from scratch tomorrow, what would you build?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the entry point to what he calls &lt;strong&gt;idealized design&lt;/strong&gt;. The exercise asks participants to set aside all existing constraints - all the inherited structures, legacy systems, political compromises, and accumulated scar tissue - and design the organisation they would actually want, subject only to two conditions: that the design is technically feasible (no perpetual motion machines) and that it is operationally viable (it could actually sustain itself).&lt;/p&gt;

&lt;p&gt;This may sound impractical. It is, in fact, the most practical thing most organisations never do.&lt;/p&gt;

&lt;p&gt;The first reason is consensus. When groups are asked to plan within existing constraints, every constraint is a potential point of conflict. People defend what they have. They protect their territory. They argue about what is realistic. When you remove the constraints entirely and ask what would be ideal, something remarkable happens: people find it much easier to agree. The closer you get to fundamental values and ultimate purposes, the more consensus emerges. Disagreements that seemed irresolvable at the operational level dissolve when you approach them from the level of principle.&lt;/p&gt;

&lt;p&gt;The second reason is that idealized design breaks the tyranny of the &lt;em&gt;sacred cow&lt;/em&gt; - the entrenched assumption that nobody questions because it has been there so long that it seems like part of the natural order. Every organisation is full of sacred cows: legacy processes, inherited reporting structures, unquestioned policies. Idealized design creates a legitimate space to question all of them simultaneously, which tends to surface both the arbitrariness of some constraints and the genuine importance of others.&lt;/p&gt;

&lt;p&gt;The third reason is motivational. Planning toward a vision you actually want is energising in a way that planning to close a gap is not. The first is about creation; the second is about remediation. Both matter, but the first tends to generate commitment while the second tends to generate compliance.&lt;/p&gt;

&lt;h2 id=&quot;from-ideal-to-real-backward-planning&quot;&gt;From Ideal to Real: Backward Planning&lt;/h2&gt;

&lt;p&gt;The idealized design is not a fantasy or an aspiration to be filed and forgotten. It becomes the reference point for all subsequent planning activity.&lt;/p&gt;

&lt;p&gt;Ackoff proposes what he calls &lt;strong&gt;backward planning&lt;/strong&gt;: start from the ideal, identify where the world is likely to be at the end of your planning horizon, and then work backward from the ideal toward the present. This reversal matters. When you plan forward, you are constrained at every step by what is currently possible. When you plan backward from an ideal, you begin with clarity about where you want to go, and the question becomes how to close the gap from the other direction.&lt;/p&gt;

&lt;p&gt;He illustrates the power of this approach with a simple puzzle. Suppose you need to trace a path through a maze. Which is easier: starting at the entrance and working toward the exit, or starting at the exit and working toward the entrance? Experimentally, starting from the exit is dramatically faster. The reason is that when you start from the entrance, every wrong path opens into more wrong paths - the decision tree fans out exponentially. When you start from the exit and work backward, wrong paths collapse back to nothing. The solution space shrinks rather than expanding.&lt;/p&gt;

&lt;p&gt;Planning from an ideal future works the same way.&lt;/p&gt;

&lt;h2 id=&quot;who-should-plan-and-why-it-matters&quot;&gt;Who Should Plan, and Why It Matters&lt;/h2&gt;

&lt;p&gt;There is a final dimension to Ackoff’s planning philosophy that is as important as any of the tools: the question of who does the planning.&lt;/p&gt;

&lt;p&gt;In most organisations, planning is done by planners - a specialist function, operating with access to data and methodology that frontline managers and workers do not share. The plan is then handed down for implementation. This division of labour seems efficient, but it generates two problems. First, the people implementing the plan have no ownership of it and no deep understanding of the reasoning behind it. Second, the people doing the planning have no access to the tacit knowledge, the lived experience, the ground-level understanding that would make the plan actually workable.&lt;/p&gt;

&lt;p&gt;Ackoff’s principle is straightforward: &lt;strong&gt;those who will be affected by a plan should be involved in making it&lt;/strong&gt;. Not consulted. Not informed. Involved. This is not a concession to democratic sentiment - it is a systems requirement. The knowledge needed to make a good plan is distributed throughout the system, and the commitment needed to implement it will only come from people who feel genuine authorship over it.&lt;/p&gt;

&lt;p&gt;This is the bridge to the next part of the series. In Tapes 5 and 6, Ackoff examines the different management philosophies that determine how organisations relate to their futures - and argues that the dominant approaches in most organisations are not just imperfect but logically self-defeating.&lt;/p&gt;

&lt;h2 id=&quot;tape-5---the-three-faces-of-management-why-looking-backward-keeps-us-stuck&quot;&gt;Tape 5 - The Three Faces of Management: Why Looking Backward Keeps Us Stuck&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;(Based on&lt;/em&gt; &lt;a href=&quot;https://vimeo.com/257237896&quot;&gt;&lt;em&gt;Ackoff Tape 5&lt;/em&gt;&lt;/a&gt;&lt;em&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One of Ackoff’s most clarifying contributions is a taxonomy of management styles. He identifies three dominant orientations - and then proposes a fourth that transcends them. Understanding the first three is not merely academic. It explains, with uncomfortable precision, why so many organisations are so good at creating elaborate structures for doing the wrong things efficiently.&lt;/p&gt;

&lt;p&gt;Before he gets to the taxonomy, however, Ackoff returns to the question of organisational governance - specifically, to a phenomenon that managers find deeply counterintuitive but that turns out to be one of the most empirically reliable observations in organisational life.&lt;/p&gt;

&lt;h2 id=&quot;the-paradox-of-authority&quot;&gt;The Paradox of Authority&lt;/h2&gt;

&lt;p&gt;Here is the paradox: &lt;strong&gt;the more authority a manager commands, the less actual power they typically have&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Ackoff calls this the distinction between &lt;em&gt;power over&lt;/em&gt; and &lt;em&gt;power to&lt;/em&gt;. Power over means the ability to compel behaviour through positional authority - to give orders and expect compliance. Power to means the actual capacity to get things done - to make things happen in the world.&lt;/p&gt;

&lt;p&gt;These are inversely related. The Shah of Iran had essentially unlimited power over his population. He could, in principle, order anything. In practice, he found himself unable to implement almost any policy that required genuine engagement and cooperation from his people. The more he commanded, the more resistance he generated. The more resistance, the less actually changed.&lt;/p&gt;

&lt;p&gt;The explanation is simple. In an organisation of intelligent, purposeful people, compliance without commitment produces at best minimal performance and at worst active sabotage. Managers who rely on command authority get the letter of their instructions obeyed and the spirit systematically undermined. Managers who earn genuine consent - whose people genuinely understand, agree with, and feel ownership of the direction - get energy, creativity, and initiative.&lt;/p&gt;

&lt;p&gt;This is not a soft observation about morale. It is a structural point about how intelligent systems respond to different types of governance. And it connects directly to the circular, democratic organisational structures Ackoff outlined in Tape 3: the point of those structures is precisely to align authority with consent, to make it impossible to hold power over people without also having secured their genuine agreement.&lt;/p&gt;

&lt;h2 id=&quot;reactive-management-the-allure-of-the-golden-age&quot;&gt;Reactive Management: The Allure of the Golden Age&lt;/h2&gt;

&lt;p&gt;Now to the taxonomy.&lt;/p&gt;

&lt;p&gt;The first management orientation is what Ackoff calls &lt;strong&gt;reactive management&lt;/strong&gt;. The reactive manager is oriented toward the past. Not toward history in an academic sense, but toward a particular past - a perceived golden age in which things worked better, relationships were simpler, and problems had the decency to be tractable.&lt;/p&gt;

&lt;p&gt;Reactive managers are not stupid or malicious. They are people who have seen things go wrong and, quite reasonably, want to understand what went wrong and correct it. Their error is not in looking at what is broken. Their error is in believing that fixing what is broken is the same thing as getting what you want.&lt;/p&gt;

&lt;p&gt;Ackoff’s illustration is crisp. Imagine a farmer whose friends have left and who sits alone wishing they would come back. Every wish is oriented toward removing something bad - the loneliness, the emptiness, the loss. But none of those wishes, even if granted, would actually produce what the farmer wants, which is companionship and community. Removing bad things does not guarantee producing good things. This seems obvious in the abstract, but in organisations it is the dominant planning logic: identify deficiencies, address deficiencies, hope the result is success.&lt;/p&gt;

&lt;p&gt;The reactive manager’s planning approach follows the same logic. Conduct a gap analysis. Find what is underperforming. Develop a programme to close the gap. Review and repeat. This produces a constant stream of firefighting, of problem-solving, of addressing symptoms. What it does not produce is forward movement toward anything you actually want, because what you actually want has never been specified. You have been too busy fixing what you do not want.&lt;/p&gt;

&lt;h2 id=&quot;why-reactive-planning-fails-systemically&quot;&gt;Why Reactive Planning Fails Systemically&lt;/h2&gt;

&lt;p&gt;There is a deeper problem with reactive management, and it runs straight back to the systems thinking Ackoff introduced in Tapes 1 and 2.&lt;/p&gt;

&lt;p&gt;In a complex organisation, a mess (to use his term) consists of interacting problems. Address one problem in isolation and you change the conditions for every other problem. Solve the logistics problem and you change the demand pattern on manufacturing. Solve the customer complaints problem and you change the requirements on product development. The system shifts every time you intervene in one of its parts.&lt;/p&gt;

&lt;p&gt;Reactive managers, working through analytical decomposition, hand each problem to the appropriate specialist, optimise each piece, and then wonder why the whole is not improving. The answer is that the whole was never the unit of analysis. The parts were. And optimised parts, in an interacting system, do not sum to an optimal whole.&lt;/p&gt;

&lt;p&gt;Ackoff uses the automobile as his example. Take all the best engines in the world - the finest from Ferrari, from BMW, from Mercedes, from every great manufacturer. Pull each one from its car and assemble them under a single chassis. What do you have? Not the best automobile in the world. You have no automobile at all, because the components were designed for different systems and will not fit together. The best system is not the one with the best components. It is the one with the best &lt;em&gt;fit&lt;/em&gt; among components that work together toward a common purpose.&lt;/p&gt;

&lt;h2 id=&quot;inactive-and-preactive-management&quot;&gt;Inactive and Preactive Management&lt;/h2&gt;

&lt;p&gt;The second type is &lt;strong&gt;inactive management&lt;/strong&gt; - oriented toward the present, toward stability, toward the perpetuation of what exists. Inactive managers are not lazy; they are risk-averse. They have seen what change can do - the disruption, the unintended consequences, the ways that good intentions produce bad outcomes - and they have concluded that the safest approach is to do nothing that is not strictly necessary.&lt;/p&gt;

&lt;p&gt;Ackoff’s assessment is not entirely unsympathetic. He acknowledges that inactive managers have often correctly identified the real risk: that the cure can be worse than the disease. Many organisational change initiatives do more harm than good. The inactive manager’s instinct - be careful about what you change - is not wrong. What is wrong is the inference that the right amount of change is therefore zero.&lt;/p&gt;

&lt;p&gt;The third type is &lt;strong&gt;preactive management&lt;/strong&gt; - the dominant mode in American business life, particularly in the mid-to-late twentieth century. The preactive manager is oriented toward the future, is energised by change rather than threatened by it, and sees technology as the primary driver of opportunity. Where the reactive manager is nostalgic, the preactive manager is evangelical about the future.&lt;/p&gt;

&lt;p&gt;The preactive manager forecasts, prepares, and positions. She invests in understanding where the market is going, develops capabilities in advance of demand, and structures the organisation to move quickly when opportunities appear.&lt;/p&gt;

&lt;p&gt;This sounds right - and compared with the reactive and inactive approaches, it is an improvement. But Ackoff identifies a fatal logical flaw at its core. He saves the full dissection for Tape 6, which is the subject of the next post. But the flaw can be stated simply here: &lt;strong&gt;the future you are trying to forecast is the same future your planning process is helping to create&lt;/strong&gt;. You cannot be both an objective observer of the future and an active participant in shaping it. The two roles are logically incompatible.&lt;/p&gt;

&lt;h2 id=&quot;the-conditions-for-something-better&quot;&gt;The Conditions for Something Better&lt;/h2&gt;

&lt;p&gt;By the end of Tape 5, Ackoff has established why the three dominant management orientations each fail - the reactive because it confuses removing bad with producing good, the inactive because stability is not a viable long-term strategy in a changing world, the preactive because its logical foundations are self-undermining.&lt;/p&gt;

&lt;p&gt;What remains is the fourth type. Ackoff calls it &lt;strong&gt;interactive management&lt;/strong&gt;, and it is the subject of the next post. But its outline is already visible: it will be oriented not toward the past, the present, or the predicted future, but toward a &lt;em&gt;designed&lt;/em&gt; future - a future you have chosen, articulated, and committed to creating. Not extrapolation. Design.&lt;/p&gt;

&lt;p&gt;The shift from planning-as-extrapolation to planning-as-design is the hinge on which this entire series turns. And in Tape 6, Ackoff makes the case for it with both logical rigour and one of the most compelling organisational case studies in the literature.&lt;/p&gt;

&lt;h2 id=&quot;tape-6---the-forecasting-trap-and-the-promise-of-interactive-management&quot;&gt;Tape 6 - The Forecasting Trap and the Promise of Interactive Management&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;(Based on&lt;/em&gt; &lt;a href=&quot;https://vimeo.com/257239590&quot;&gt;&lt;em&gt;Ackoff Tape 6&lt;/em&gt;&lt;/a&gt;&lt;em&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the previous post established why reactive management fails, this one takes aim at its more sophisticated cousin: the preactive manager who forecasts, prepares, and tries to ride the wave of the future. Ackoff has considerable respect for this orientation - it is, after all, forward-looking and change-embracing - but he argues that it rests on a logical paradox that no amount of methodological sophistication can resolve.&lt;/p&gt;

&lt;h2 id=&quot;the-paradox-hidden-in-every-forecast&quot;&gt;The Paradox Hidden in Every Forecast&lt;/h2&gt;

&lt;p&gt;Consider what forecasting requires. You are trying to predict the future state of something - a market, a technology, a regulatory environment. In order to do this reliably, you need to assume that the future depends on forces you can identify and model, and that your forecasting process is not itself one of those forces.&lt;/p&gt;

&lt;p&gt;But that assumption is false.&lt;/p&gt;

&lt;p&gt;When a large organisation forecasts that a particular market segment will grow and then invests heavily in that segment, the investment itself changes the market. Other organisations see the investment and respond to it. The market that actually emerges is not the market that was forecast - it has been shaped, in part, by the forecast itself. The act of forecasting is an intervention in the system being forecasted.&lt;/p&gt;

&lt;p&gt;This is not a minor technical problem. It is a fundamental logical issue. Ackoff poses it as a dilemma: &lt;strong&gt;the better you can predict, the less you need to prepare; the better you prepare, the less you need to predict&lt;/strong&gt;. If you can perfectly forecast the future, you can make exactly the right moves and preparation is unnecessary. If you prepare perfectly - building robust, flexible capabilities that work in any future - you do not need to forecast, because you can handle whatever comes. But trying to do both - to forecast &lt;em&gt;and&lt;/em&gt; to prepare - creates a circular logic that undermines both activities.&lt;/p&gt;

&lt;p&gt;And there is a further, more practical problem. What do forecasting methods actually consist of? Ackoff’s answer is blunt: at base, virtually every forecasting method reduces to fitting a curve to past data and extrapolating. The assumptions may be sophisticated; the statistical machinery may be elaborate. But the underlying logic is always the same: the past contains a pattern, and the future will continue that pattern.&lt;/p&gt;

&lt;p&gt;This assumption is becoming less true, not more true. The faster the environment changes, the shorter the half-life of historical patterns, and the less reliable extrapolation becomes. Preactive management, which relies most heavily on forecasting, becomes less viable precisely when it is most urgently needed.&lt;/p&gt;

&lt;h2 id=&quot;interactive-management-planning-as-design&quot;&gt;Interactive Management: Planning as Design&lt;/h2&gt;

&lt;p&gt;Ackoff’s alternative is &lt;strong&gt;interactive management&lt;/strong&gt; - the fourth type, promised at the end of the last post. Where the reactive manager orients toward the past, the inactive toward the present, and the preactive toward the predicted future, the interactive manager orients toward a &lt;em&gt;designed&lt;/em&gt; future.&lt;/p&gt;

&lt;p&gt;This is a crucial distinction. A designed future is not a forecast. It is not a prediction of what will happen. It is a specification of what you &lt;em&gt;want&lt;/em&gt; to happen - the future you are actively working to create. You are not bracing against an incoming wave; you are deciding what you want to build and working backward from that vision to understand what needs to happen next.&lt;/p&gt;

&lt;p&gt;The key practice is what Ackoff calls &lt;strong&gt;idealized redesign&lt;/strong&gt; - which we introduced in Post 3, but which becomes fully operational here. The question is: if your organisation were destroyed tonight and you had to start fresh tomorrow, what would you design? What system would best serve the purposes of all its stakeholders, given everything you know about what you are trying to accomplish?&lt;/p&gt;

&lt;p&gt;And the most important thing about idealized redesign is that it does not require expertise. You do not need to be a specialist in any particular domain to participate meaningfully. The question “what would be ideal?” is one that anyone who understands the purpose of the system can engage with genuinely. This is what makes it participatory in a deep sense: not a consultation exercise where specialists design and others are informed, but a genuine co-creation process where all the stakeholders’ knowledge and values are in the room.&lt;/p&gt;

&lt;h2 id=&quot;the-bell-laboratories-story&quot;&gt;The Bell Laboratories Story&lt;/h2&gt;

&lt;p&gt;Ackoff illustrates interactive management with one of the great case studies in organisational learning - his account of a project at Bell Laboratories that demonstrates what happens when intelligent people are freed from inherited constraints and asked to design from scratch.&lt;/p&gt;

&lt;p&gt;A Bell Labs vice president invited a group of researchers and engineers to a retreat with an unusual brief. Suppose, he said, that Bell’s entire telephone system was destroyed overnight - all the infrastructure, all the exchanges, all the equipment. You need to rebuild the system from scratch by tomorrow morning. What would you build?&lt;/p&gt;

&lt;p&gt;The instruction was not to improve the existing system. Not to fix what was broken. Not to add features to what already existed. But to design, unconstrained, the best possible telephone system you could imagine.&lt;/p&gt;

&lt;p&gt;The group, freed from the assumption that the existing system was the baseline, began to think in genuinely new ways. They questioned assumptions that had seemed fixed for decades. Why does a telephone number have to be tied to a location? Why does a call have to be routed through a central exchange? Why does the handset have to be the form factor?&lt;/p&gt;

&lt;p&gt;Out of this exercise came, among other things, the conceptual foundations for push-button dialling - a change that had been technically possible for years but had never been implemented because the existing system had never been seriously questioned. When you start from scratch, you discover that many of the constraints you thought were technical are actually habitual. They are there because they were there before, not because they need to be.&lt;/p&gt;

&lt;p&gt;The Bell Labs story is not just an anecdote about a clever design exercise. It is evidence for a general principle: &lt;strong&gt;the greatest obstacle to innovation is not the absence of good ideas but the presence of unconsidered assumptions&lt;/strong&gt;. The assumptions do not feel like constraints because they are invisible. Idealized redesign makes them visible by removing them.&lt;/p&gt;

&lt;h2 id=&quot;working-backward-is-easier-than-working-forward&quot;&gt;Working Backward Is Easier Than Working Forward&lt;/h2&gt;

&lt;p&gt;Ackoff returns to the maze analogy to explain why designing from the future is not only philosophically preferable but practically more tractable.&lt;/p&gt;

&lt;p&gt;When you plan forward from the present, you face a decision tree that fans out exponentially. Every choice opens into multiple further choices, and the space of possible futures grows with every step. When you plan backward from a specified ideal future, the decision tree converges. Every step back from the ideal toward the present reduces the space of required actions. The problem becomes smaller, not larger, as you work through it.&lt;/p&gt;

&lt;p&gt;This is the mathematical reason why planning-as-design tends to generate better plans more efficiently than planning-as-extrapolation. It is not mysticism. It is the basic geometry of search through possibility spaces.&lt;/p&gt;

&lt;p&gt;And there is a human dimension too. Planning toward a future you have genuinely chosen and genuinely want produces a different quality of commitment than planning toward a future that has been forecast for you. The first is creative. The second is adaptive. Both have their place, but organisations that only ever adapt - that only ever try to keep pace with an environment they have not chosen - will always be reactive in the deepest sense, regardless of how forward-looking their rhetoric.&lt;/p&gt;

&lt;h2 id=&quot;what-interactive-management-demands&quot;&gt;What Interactive Management Demands&lt;/h2&gt;

&lt;p&gt;Interactive management is not easy. It requires the intellectual honesty to acknowledge that you cannot predict the future with confidence, and to plan accordingly. It requires the political courage to challenge sacred cows - to question assumptions that feel load-bearing but are actually just inherited. It requires genuine participation from people who are not used to being asked what they actually want.&lt;/p&gt;

&lt;p&gt;And it requires a tolerance for the discomfort of operating toward an ideal that will never be fully achieved. The idealized design is not a destination. It is a reference point - a magnetic north that orients all subsequent decisions. As the organisation moves toward it, the ideal itself evolves, because understanding of what is truly desirable deepens through the act of pursuing it.&lt;/p&gt;

&lt;p&gt;In the next two posts, we follow Ackoff into practice. Tape 7 is rich with case studies - urban planning, organisational redesign, brewery management - that show what interactive management looks like when it is actually done. And then, in one of his most provocative moves, he turns the same analytical lens onto education, with consequences that are both devastating and, ultimately, hopeful.&lt;/p&gt;

&lt;h2 id=&quot;tape-6---idealized-design-in-practice-from-breweries-to-cities&quot;&gt;Tape 6 - Idealized Design in Practice: From Breweries to Cities&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;(Based on&lt;/em&gt; &lt;a href=&quot;https://vimeo.com/257241270&quot;&gt;&lt;em&gt;Ackoff Tape 7&lt;/em&gt;&lt;/a&gt;&lt;em&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There is a difference between understanding an idea and believing it will work. The previous posts in this series have laid out Ackoff’s conceptual architecture: the Machine Age worldview and its failures, the Systems Age as its successor, the organisational and planning implications that follow. By this point, a sympathetic reader might say: the framework is compelling, but how does it actually function when you try to apply it to real organisations with real people, real politics, and real constraints?&lt;/p&gt;

&lt;p&gt;Tape 7 is Ackoff’s answer to that question. It is, in many ways, the most vivid and convincing of the tapes - a sequence of case studies that show idealized design and interactive management working in practice, including in conditions that should have made them impossible.&lt;/p&gt;

&lt;h2 id=&quot;the-nine-dots-and-the-real-constraint&quot;&gt;The Nine Dots and the Real Constraint&lt;/h2&gt;

&lt;p&gt;Before the case studies, Ackoff opens with a puzzle that has become something of a touchstone in creative problem-solving literature: the nine-dot problem.&lt;/p&gt;

&lt;p&gt;Nine dots are arranged in a three-by-three grid. The challenge is to connect all nine dots using four straight lines, without lifting the pen from the paper. Most people find it nearly impossible - until they realise that the constraint preventing them is not in the problem statement but in their own heads. They are unconsciously assuming that the lines must stay within the square formed by the outer dots. Remove that assumption and the solution is straightforward.&lt;/p&gt;

&lt;p&gt;Ackoff does not use this puzzle to make a generic point about thinking outside the box. He uses it to make a specific and more uncomfortable point: &lt;strong&gt;the binding constraints in most organisational problems are not external&lt;/strong&gt;. They are not in the market, not in the technology, not in the regulatory environment. They are in the mental models of the people trying to solve the problem. The problem is not the mess. The problem is the assumptions we bring to the mess.&lt;/p&gt;

&lt;p&gt;He illustrates this with a story about urban planning in Mexico City in the early 1970s. The city was struggling with catastrophic traffic congestion. Ackoff was brought in to consult. He suggested several interventions: relocating the federal capital (which would remove the concentration of government workers who generated enormous commuter traffic), adjusting the siesta schedule (which produced two rush hours instead of one), and other structural changes.&lt;/p&gt;

&lt;p&gt;The urban planners dismissed every suggestion. “Impossible,” they said. “You cannot move a capital. You cannot change the siesta.” Within months of that conversation, Mexico’s new president - elected on a platform that included, apparently, the impossible - had begun relocating government functions and adjusting the siesta schedule.&lt;/p&gt;

&lt;p&gt;The lesson was not that Ackoff was smarter than the planners. The lesson was that the planners had internalised constraints that were entirely contingent - they felt permanent because they had been stable, but they were not inherent to the problem. The barrier to solving the mess was not the mess. It was the assumption that certain things could not be questioned.&lt;/p&gt;

&lt;p&gt;This is the deepest justification for idealized design: it forces you to start from a position where nothing is assumed, and therefore everything that turns out to be a genuine constraint becomes visible as such, while everything that turns out to be contingent is revealed as open to question.&lt;/p&gt;

&lt;h2 id=&quot;the-brewery-when-janitors-and-engineers-design-together&quot;&gt;The Brewery: When Janitors and Engineers Design Together&lt;/h2&gt;

&lt;p&gt;One of the most memorable case studies in Tape 7 involves a brewery in Mexico. Ackoff was working with the company on organisational redesign, and the process he describes illustrates several of his key principles operating simultaneously.&lt;/p&gt;

&lt;p&gt;He organised the redesign process so that different groups worked on different parts of the operation: workers on the production floor, janitors on the brewery itself, engineers and managers on other elements. Each group was asked the same question: if you were designing this part of the operation from scratch, what would you create?&lt;/p&gt;

&lt;p&gt;The janitors, asked to design the brew house, came up with a radically innovative layout - one that would have significantly simplified cleaning and maintenance. They had spent years navigating the existing layout’s inefficiencies and had, over that time, developed a clear intuitive sense of what would work better. They had never been asked.&lt;/p&gt;

&lt;p&gt;The production workers, asked to design the production line, came up with their own set of changes. Their design was also innovative, built from years of intimate knowledge of what slowed things down, what caused bottlenecks, what generated errors.&lt;/p&gt;

&lt;p&gt;When the two groups brought their designs together, they discovered an immediate problem: their designs were incompatible. The janitors’ brew house layout and the workers’ production line layout could not both be implemented simultaneously; they made conflicting assumptions about workflow and physical arrangement.&lt;/p&gt;

&lt;p&gt;This incompatibility was not a failure. It was the beginning of the most valuable part of the process. The two groups now had to talk to each other - to understand how each other’s work actually functioned, how their separate domains interacted, what the constraints were that each imposed on the other. For the first time, workers and janitors were talking not about their complaints but about the logic of the whole system in which they both worked. The process of resolving the conflict generated more genuine understanding of the organisation’s functioning than any formal training programme had.&lt;/p&gt;

&lt;p&gt;This is a general pattern in Ackoff’s approach: the value of participative planning is not primarily the plan that results. It is the learning that happens during the planning process. When people engage seriously with the question of what would be ideal, they begin to understand the system they are part of in a way that no briefing or induction could produce.&lt;/p&gt;

&lt;h2 id=&quot;the-bankguard-uniform&quot;&gt;The Bankguard Uniform&lt;/h2&gt;

&lt;p&gt;Another case study from this tape has a pleasing elegance. A bank was suffering from internal theft - money disappearing from vaults, cash drawers coming up short, losses accumulating in ways that were expensive and demoralising.&lt;/p&gt;

&lt;p&gt;The conventional response would have been to tighten security: more surveillance cameras, more access controls, more auditing. All of these are expensive and all of them signal distrust to the entire workforce, including the overwhelming majority who are perfectly honest.&lt;/p&gt;

&lt;p&gt;During an idealized design exercise, someone in the group - Ackoff does not specify who - proposed a different framing. The problem was not that people were dishonest. The problem was that people had the opportunity to be dishonest without detection, and some proportion of people, in any large population, will take opportunities they are given. The question was not how to catch thieves after the fact but how to remove the opportunity.&lt;/p&gt;

&lt;p&gt;The solution: provide all staff with uniforms, including changing rooms where they change into and out of them at the beginning and end of shifts. No personal bags or clothing in the work area. No way to carry cash out of the building. The cost was a fraction of the security systems under consideration. The effect was to make theft essentially impossible without requiring any surveillance, any accusation, or any deterioration in the trust culture of the workplace.&lt;/p&gt;

&lt;p&gt;This kind of solution - simple, structural, non-adversarial - emerges naturally from idealized design because idealized design asks “what would we want the system to be?” rather than “how do we respond to what the system is doing?” It produces systemic fixes rather than symptomatic responses.&lt;/p&gt;

&lt;h2 id=&quot;paris-when-political-opponents-agree-on-the-ideal&quot;&gt;Paris: When Political Opponents Agree on the Ideal&lt;/h2&gt;

&lt;p&gt;Perhaps the most striking case study in Tape 7 involves urban planning at the largest scale. Ackoff worked with a planning group in Paris in the early 1970s, attempting to develop a long-term vision for the city. The group included representatives of all twelve major political parties - from the extreme right to the extreme left - who agreed on almost nothing at the operational level.&lt;/p&gt;

&lt;p&gt;When asked what they wanted Paris to look like in twenty years, the political disagreements immediately surfaced. But Ackoff kept asking the group to move toward more fundamental values, more ultimate purposes. What is Paris &lt;em&gt;for&lt;/em&gt;? What would be ideal?&lt;/p&gt;

&lt;p&gt;And gradually, across sessions, something remarkable happened. On the question of what Paris should ideally become, the group converged. All twelve parties, across the entire political spectrum, agreed that Paris should be the cultural capital of the world - a city of extraordinary beauty, intellectual life, and civilised living, a place that set the standard for what urban life at its best could be.&lt;/p&gt;

&lt;p&gt;They disagreed entirely on how to achieve this - on economic policy, on urban planning mechanisms, on the role of the state. But they agreed on the ideal. And that agreement on the ideal created, for the first time, a shared framework within which their operational disagreements could be productive rather than merely paralyzing.&lt;/p&gt;

&lt;p&gt;This is Ackoff’s most important practical argument for idealized design: it moves disagreement from the level where it is irresolvable to the level where resolution is possible. Opponents who cannot agree on means can often agree on ends. And agreement on ends creates the possibility of a genuine conversation about means.&lt;/p&gt;

&lt;h2 id=&quot;the-turn-toward-education&quot;&gt;The Turn Toward Education&lt;/h2&gt;

&lt;p&gt;In the later portion of Tape 7, Ackoff makes a pivot that initially surprises but, in retrospect, is entirely logical. Having argued that the greatest constraint on organisational improvement is the mental models of the people doing the improving, he asks: where do those mental models come from? What institution is responsible for forming the way people think?&lt;/p&gt;

&lt;p&gt;The answer, obviously, is education. And having praised organisations for their capacity to change and innovate when given the right conditions, he turns on education systems with what can only be described as controlled fury.&lt;/p&gt;

&lt;p&gt;The argument in full is reserved for the final post in this series, which covers Tape 8. But the central charge is introduced here: educational institutions, far from developing the capacity for creative, systemic, participatory thinking that the Systems Age requires, are specifically and systematically designed to suppress it. They teach people what to think. They punish alternative solutions. They reward conformity and call it learning.&lt;/p&gt;

&lt;p&gt;If you want to understand why organisations find it so difficult to implement the approaches Ackoff has been describing - why participative design feels foreign, why idealized thinking feels impractical, why systems thinking feels counterintuitive - the answer, he suggests, begins in school.&lt;/p&gt;

&lt;h2 id=&quot;tape-8--the-education-catastrophe-how-schools-destroy-the-very-capacity-they-claim-to-build&quot;&gt;Tape 8- The Education Catastrophe: How Schools Destroy the Very Capacity They Claim to Build&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;(Based on&lt;/em&gt; &lt;a href=&quot;https://vimeo.com/257529225&quot;&gt;&lt;em&gt;Ackoff Tape 8&lt;/em&gt;&lt;/a&gt;&lt;em&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The final post in this series is also, in some ways, the most personal. Ackoff spent his career arguing that organisations could be redesigned to serve the full range of human purposes - that the gap between how institutions function and how human beings actually flourish could be closed, if only people were willing to question fundamental assumptions. By Tape 8, he turns that same unflinching attention to the institution most responsible for forming human beings before they reach organisations at all.&lt;/p&gt;

&lt;p&gt;What he finds in education systems is, in his assessment, not a collection of problems to be solved but a catastrophe to be understood and, where possible, circumvented.&lt;/p&gt;

&lt;h2 id=&quot;problems-exercises-and-real-questions&quot;&gt;Problems, Exercises, and Real Questions&lt;/h2&gt;

&lt;p&gt;Ackoff begins with a set of distinctions that echo the terminology introduced in Tape 4 - problems versus messes - but applies it now to the educational context.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;question&lt;/strong&gt; is something that has an expected, known answer - a factual lookup. “What is the capital of France?” is a question. A &lt;strong&gt;puzzle&lt;/strong&gt; is a problem in which the difficulty arises from a wrongly assumed constraint: remove the assumption and the solution becomes obvious. A &lt;strong&gt;problem&lt;/strong&gt; is something where the formulation itself is not given - where part of the challenge is figuring out what the question actually is and what kind of answer would constitute a solution.&lt;/p&gt;

&lt;p&gt;Real life, Ackoff observes, consists almost entirely of the third type. The situations we actually face in organisations, families, and communities are rarely well-formulated. They are ambiguous, entangled, value-laden, and resistant to clear solution criteria. The work of engaging with them requires creativity, judgment, the ability to tolerate uncertainty, and the capacity to question the framing of the problem itself.&lt;/p&gt;

&lt;p&gt;What do schools primarily teach? The first type - questions with known answers - and a watered-down version of the second type - problems in which the teacher already knows the solution and the student is expected to reproduce it. The third type, the real thing, is almost entirely absent.&lt;/p&gt;

&lt;h2 id=&quot;how-schools-destroy-creativity&quot;&gt;How Schools Destroy Creativity&lt;/h2&gt;

&lt;p&gt;Ackoff’s account of how schools suppress creative thinking is illustrated by a story about his daughter Karen.&lt;/p&gt;

&lt;p&gt;Karen was given the nine-dot problem in school - the same puzzle introduced in the previous post. She found several solutions: the conventional one (four lines, extending beyond the grid), but also others that required questioning different assumed constraints. You can solve it with three lines if you draw them wide enough to cover multiple dots. You can solve it with one line if you fold the paper. You can solve it without any constraint about straight lines if the instructions are interpreted differently.&lt;/p&gt;

&lt;p&gt;Karen presented these solutions. Her teacher was not delighted. Her teacher was irritated. There was one correct solution - the one in the answer key - and Karen’s alternatives, however logically valid, were disruptive. She was told to sit down.&lt;/p&gt;

&lt;p&gt;Eventually, unable to contain herself, Karen asked the teacher directly: what if you folded the paper? The teacher said that was not allowed. Karen pointed out that the instructions said nothing about folding. The teacher said it was obvious you were not supposed to fold the paper. Karen said she did not think it was obvious. The teacher threatened punishment.&lt;/p&gt;

&lt;p&gt;This is not an isolated story of a bad teacher. It is, Ackoff argues, the systematised consequence of designing an educational institution around the goal of transmitting predetermined content. When the measure of success is whether students produce the expected answer, then students who produce unexpected answers - even correct, creative, more sophisticated answers - are failures by definition. The institution is not rewarding thought; it is rewarding compliance with thought that has already been done.&lt;/p&gt;

&lt;p&gt;The practical consequence is that children, who enter school with genuine curiosity and a natural capacity for creative problem-solving, leave school with those capacities diminished. Not because the teachers are malicious, but because the system is structurally oriented toward a form of learning that requires suppressing exactly the faculties that make people genuinely effective.&lt;/p&gt;

&lt;h2 id=&quot;the-university-designed-for-faculty-not-students&quot;&gt;The University: Designed for Faculty, Not Students&lt;/h2&gt;

&lt;p&gt;Ackoff extends his critique to higher education with equal force. The university, he argues, is not primarily designed to educate students. It is primarily designed to provide faculty with the kind of working conditions they find desirable: research time, minimal teaching loads, scheduling arranged for faculty convenience, intellectual communities organised around disciplinary specialisation.&lt;/p&gt;

&lt;p&gt;The evidence he cites is behavioural. In faculty meetings, students are almost never discussed. The meetings are about curriculum, about departmental resources, about research priorities, about administrative processes. The curriculum is organised not around the integrated problems that students will actually face in their lives but around the disciplinary divisions that make sense for specialised research communities. A student studying management encounters economics in the economics department, psychology in the psychology department, sociology in the sociology department, engineering in the engineering school - each taught without reference to the others, as though reality were similarly divided.&lt;/p&gt;

&lt;p&gt;It is not. Real problems are interdisciplinary. They are messy in exactly the sense Ackoff has been using throughout this series. The student who wants to understand why organisations fail needs economics and psychology and sociology and systems theory and history - not as separate subjects to be examined separately, but as lenses that need to be used together on the same object.&lt;/p&gt;

&lt;h2 id=&quot;the-univac-experiment-children-teaching-computers&quot;&gt;The Univac Experiment: Children Teaching Computers&lt;/h2&gt;

&lt;p&gt;Ackoff describes an experiment - conducted in the 1960s with second-graders and an early Univac computer - that reveals something important about how learning actually works, as opposed to how we design it to work.&lt;/p&gt;

&lt;p&gt;The children were not taught to use the computer. They were given a challenge: teach the computer to do your homework. In order to accomplish this, they had to understand what they were doing well enough to explain it to a system that had no intuitive understanding at all. If they could not articulate the mathematical steps clearly enough for the computer to execute them, the computer produced the wrong answer, and they immediately knew their instructions were incomplete.&lt;/p&gt;

&lt;p&gt;The results were striking. Children who had struggled with arithmetic in conventional instruction became highly competent when the goal was to teach rather than to learn. The act of explaining forced a depth of understanding that passive reception could not produce.&lt;/p&gt;

&lt;p&gt;This is a consistent finding across many contexts, and it points to a general principle: &lt;strong&gt;people learn most effectively when they have to teach&lt;/strong&gt;. When you must explain something to someone else - especially someone who will push back if your explanation is wrong - you are forced to achieve a quality of understanding that studying for an examination simply does not require.&lt;/p&gt;

&lt;p&gt;Most educational design ignores this. The role of teacher is reserved for the professional teacher. Students are always the receivers. Ackoff’s proposal is to reverse this: structure educational experiences so that students are frequently in the role of teaching each other, with the teacher’s role shifting to facilitating that process and providing the resources needed to support it.&lt;/p&gt;

&lt;h2 id=&quot;working-alternatives-the-mexican-experiment&quot;&gt;Working Alternatives: The Mexican Experiment&lt;/h2&gt;

&lt;p&gt;Ackoff is not content to criticise without offering alternatives. He describes a programme in rural Mexico in which high school graduates - not trained teachers, not specialists - served as instructors in village schools. They received support through college-level education provided to them in exchange for their service, creating a genuine exchange of value. The villages provided spaces for learning - not dedicated school buildings, which were expensive and often underused, but community spaces adapted for educational purposes.&lt;/p&gt;

&lt;p&gt;The students, taught by people only slightly older than themselves, learned in a more horizontal relationship. The age gap was small enough that the teacher’s authority was not overwhelming, but large enough that the teacher had genuine experience and knowledge to offer. The setting was familiar and community-embedded rather than institutional and alienating.&lt;/p&gt;

&lt;p&gt;The programme achieved parity with urban schools in measured learning outcomes, at a fraction of the cost, and with dramatically higher levels of engagement and community buy-in.&lt;/p&gt;

&lt;p&gt;The lesson is not that trained teachers are unnecessary or that institutional resources do not matter. The lesson is that the specific combination of institutional design features that characterise conventional schools - large buildings, trained specialist teachers, age-segregated cohorts, discipline-divided curricula, individual assessment - is not the only way to achieve educational outcomes, and may not even be the most effective way for most students.&lt;/p&gt;

&lt;h2 id=&quot;the-student-who-rotated-the-page&quot;&gt;The Student Who Rotated the Page&lt;/h2&gt;

&lt;p&gt;Ackoff closes with a story that captures, in miniature, everything he has been arguing throughout this series.&lt;/p&gt;

&lt;p&gt;A student was given an assignment that asked him to diagram something on a rectangular page - the normal orientation, the expected format. The student, feeling playful (or perhaps principled), turned the page sideways and completed the assignment in landscape orientation rather than portrait. The information was all there, correctly and elegantly presented. Nothing was missing. The only non-compliance was with an assumption - that pages are held portrait-side up - that had never been stated in the instructions.&lt;/p&gt;

&lt;p&gt;His teacher gave him an A. Not despite the rotation, but because of it. Because the rotation demonstrated something the teacher valued above mechanical compliance: the student had noticed that the orientation was a constraint, had questioned it, had decided it was arbitrary, and had acted on that judgment. He had done exactly what the nine-dot problem is supposed to teach. He had found a solution outside the implicitly assumed boundary.&lt;/p&gt;

&lt;p&gt;That teacher was exceptional. Most would have marked the student down. And in that difference - between a system that rewards the questioning of arbitrary constraints and one that punishes it - lies the entire argument Ackoff has been making across eight tapes and hundreds of hours of thinking.&lt;/p&gt;

&lt;h2 id=&quot;the-thread-that-runs-through-everything&quot;&gt;The Thread That Runs Through Everything&lt;/h2&gt;

&lt;p&gt;This series began with a question: what in the world is happening in the world? Ackoff’s answer was a change of age - the slow, difficult, incomplete transition from a worldview built on analysis, reductionism, and determinism to one built on synthesis, systems, and purposefulness.&lt;/p&gt;

&lt;p&gt;Everything that followed was an elaboration of what that transition demands in practice. It demands organisations that recognise all their stakeholders as purposeful beings with legitimate claims, not just as inputs and outputs. It demands planning that designs desirable futures rather than forecasting unavoidable ones. It demands management that earns consent rather than commanding compliance. It demands education that develops the capacity for creative, systemic, independent thinking rather than suppressing it in favour of predetermined answers.&lt;/p&gt;

&lt;p&gt;None of these demands has been fully met. Most organisations, most of the time, still operate with Machine Age assumptions. Most planning processes still extrapolate from the past. Most managers still confuse authority with effectiveness. Most schools still reward compliance over creativity.&lt;/p&gt;

&lt;p&gt;But the evidence Ackoff marshals across these tapes - the case studies, the experiments, the examples - suggests that the alternative is not merely philosophically preferable. It works. It works better, produces more, generates more commitment, and makes organisations more genuinely capable of dealing with the messy, purposeful, entangled reality they actually inhabit.&lt;/p&gt;

&lt;p&gt;The age is changing. The question is whether we are willing to change our thinking fast enough to meet it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This blog series is based on the Ackoff Tapes, a series of lectures delivered by Russell L. Ackoff to the Naval Education and Training Organization. Ackoff (1919-2009) was Professor Emeritus at the Wharton School, University of Pennsylvania, founder of the INTERACT Institute, and one of the founding figures of systems thinking and operations research. His major works include&lt;/em&gt; Re-Creating the Corporation, &lt;em&gt;The Art of Problem Solving,&lt;/em&gt; Ackoff’s Fables, &lt;em&gt;and&lt;/em&gt; Redesigning the Future. &lt;em&gt;His ideas remain as urgent as when he first delivered them.&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 25 Feb 2026 14:18:12 +0000</pubDate>
        <link>https://woutervanranst.github.io/the-ackoff-lectures-a-blog-series-on-systems-thinking-management-and-the-world-were-building/</link>
        <guid isPermaLink="true">https://woutervanranst.github.io/the-ackoff-lectures-a-blog-series-on-systems-thinking-management-and-the-world-were-building/</guid>
        
        
      </item>
    
      <item>
        <title>Developing Arius 5 during the dawn of AI assisted code</title>
        <description>&lt;p&gt;&lt;img src=&quot;/assets/posts/developing-arius-5-during-the-dawn-of-ai-assisted-code/image.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;End of September, I finally did it. I said goodbye to my ~3 year old codebase of Arius 3 and &lt;a href=&quot;https://github.com/woutervanranst/Arius/pull/74&quot;&gt;merged&lt;/a&gt; the new &lt;a href=&quot;https://github.com/woutervanranst/Arius/releases/tag/v5.0.112&quot;&gt;v5 code&lt;/a&gt; into main. (&lt;a href=&quot;https://github.com/woutervanranst/Arius/tree/feat/arius4-latest&quot;&gt;v4&lt;/a&gt; was a botched attempt at a pure clean architecture, but that suffered from the &lt;a href=&quot;https://en.wikipedia.org/wiki/Second-system_effect&quot;&gt;second-system effect&lt;/a&gt;, was too rigid, so I &lt;a href=&quot;https://en.wikipedia.org/wiki/Winamp#Winamp_5&quot;&gt;skipped&lt;/a&gt; a &lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_Stream_Protocol&quot;&gt;version&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;I initially started the rewrite about 1.5 years ago as my insights about how modern .NET code is written had evolved, and I wanted to give it a spin. It has been an on-and-off effort during that time and during those 2 years, how code is coded has changed dramatically.&lt;/p&gt;

&lt;p&gt;This blog post is about the whys and hows of it.&lt;/p&gt;

&lt;h2 id=&quot;the-old-days-of-writing-code&quot;&gt;The old days of writing code&lt;/h2&gt;

&lt;p&gt;The first versions of Arius were written before the ChatGPT launch of November 2022. I had limited experience in writing larger .NET codebases, but it always itched. Arius was my pet project, a stab at making a larger, production-ready codebase and eating my own dog food.&lt;/p&gt;

&lt;p&gt;I learned to write proper C# code through the first version of Arius, and so I am very grateful to that old codebase. I spent days trying to debug and understand how &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async/await&lt;/code&gt; works, how concurrency works, how exceptions in async context work, inheritance, and more, so this image really strikes a nerve:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/developing-arius-5-during-the-dawn-of-ai-assisted-code/image-1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-emergence-of-ai-generated-code&quot;&gt;The emergence of AI generated code&lt;/h2&gt;

&lt;p&gt;Serendipitously, in November 2022, I was working closely with somebody who was deeply involved with the cutting edge of AI, so I was there when it happened. The day after ChatGPT launched we were using it to write code.&lt;/p&gt;

&lt;p&gt;It was magical.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/developing-arius-5-during-the-dawn-of-ai-assisted-code/image-2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But after a while you start to learn the quirks of the system, and looking back the experience was characterized by 1. the small context window and 2. very much hit-or-miss. Either it worked and it was nice, or it didn’t. If you were lucky it didn’t compile, but there were nasty bugs that were really hard to pin down because you didn’t write or fully understand the code in the first place.&lt;/p&gt;

&lt;p&gt;This improved dramatically with the launch of ChatGPT 4 in March 2023. I experienced it as a real jump in capability despite people claiming in hindsight it was only a moderate improvement. Problems that 3.5 couldn’t handle, ChatGPT 4 breezed right through them if they fit the context window. My &lt;a href=&quot;https://github.com/woutervanranst/utils/blob/0a8081037a9f2174ad07dc216dca45028b5cc64b/src/WouterVanRanst.Utils/Builders/MermaidGraphBuilder.cs&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MermaidGraphBuilder.cs&lt;/code&gt;&lt;/a&gt; stems from that time and is probably my first vibe-coded thing: to this day I don’t fully understand why it works, but it does.&lt;/p&gt;

&lt;p&gt;Context windows grew larger somewhat, but looking back it felt a bit like a plateau. All in all, AI generated code was very much a thing on the side, but very separate from my main dev workflow.&lt;/p&gt;

&lt;h2 id=&quot;the-deepseek-moment&quot;&gt;The DeepSeek moment&lt;/h2&gt;

&lt;p&gt;In January 2025, DeepSeek took the world by storm for &lt;a href=&quot;https://arstechnica.com/ai/2025/01/china-is-catching-up-with-americas-best-reasoning-ai-models/&quot;&gt;multiple reasons&lt;/a&gt; with their DeepSeek-R1 model and chatbot. Simulated reasoning models already existed with o1, but were very rate limited and the reasoning chain was not visible. DeepSeek blew this all open and it was the first time we could see the model think. It had a 128k context window and was available for free.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/developing-arius-5-during-the-dawn-of-ai-assisted-code/image-3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As the world jumped on it and they didn’t have the capacity, I discovered &lt;a href=&quot;https://openrouter.ai/&quot;&gt;OpenRouter&lt;/a&gt;. Before, I was tied to the ChatGPT interface or the DeepSeek interface, but with OpenRouter it clicked how the models were different from the chat interface.&lt;/p&gt;

&lt;p&gt;After that watershed moment, my coding workflow started to change, as the context windows grew increasingly large and the models increasingly powerful enough to handle multiple classes and rather complex refactors and features. However, it remained largely copy-from-Visual-Studio-into-the-browser-and-paste-the-response-back.&lt;/p&gt;

&lt;h2 id=&quot;the-advent-of-the-coding-agent&quot;&gt;The advent of the Coding Agent&lt;/h2&gt;

&lt;p&gt;I had heard of Cursor but as it didn’t support .NET I didn’t really dive into it further. I found Copilot in VS and VS Code slow, cumbersome and not that good, so I preferred the copy-paste way of working.&lt;/p&gt;

&lt;p&gt;In June 2025 I decided to give &lt;a href=&quot;https://cline.bot/&quot;&gt;Cline&lt;/a&gt; a spin to see what all the fuss was about. In combination with my OpenRouter account I discovered the way of working that was taking the world by storm. Instead of copy-pasting, the changes were made right there in the IDE. You could see the LLM fetching the required context, reasoning, and making changes. It was magical.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Promising but expensive, and not that good&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The enthusiasm subsided somewhat after a couple of day-long programming stints. I used &lt;a href=&quot;https://openrouter.ai/google/gemini-2.5-flash&quot;&gt;Gemini 2.5 Flash&lt;/a&gt; on the cheap and the results were meh. What we later came to call AI slop. It wrote a lot of code that was mostly beside the point. And yet it was still expensive: until then, I had used the flat-fee ChatGPT subscription of 20 USD per month and in one day I already blew through 10 USD. Furthermore, Cline seemed to stall at times, get stuck, and hang Visual Studio; I gave Roo Code and Kilo Code a try, but it was all same same but different.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/developing-arius-5-during-the-dawn-of-ai-assisted-code/image-4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In retrospect, I think that choosing a more powerful model at the time would have yielded better results, but I wasn’t emotionally ready to pay big bucks.&lt;/p&gt;

&lt;h2 id=&quot;then-came-claude&quot;&gt;Then came Claude&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=AJpK3YTTKZ4&quot;&gt;Claude Code was introduced in February 2025&lt;/a&gt; and the hype really got underway after Anthropic launched the Max plan for 100 USD or 200 USD per month. This was my reaction when &lt;a href=&quot;https://arstechnica.com/ai/2025/04/anthropic-launches-200-claude-max-ai-plan-with-20x-higher-usage-limits/&quot;&gt;I read about it back&lt;/a&gt; in April 2025:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/developing-arius-5-during-the-dawn-of-ai-assisted-code/image-5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;“What an insane amount of money”.&lt;/p&gt;

&lt;p&gt;However, late August I couldn’t hold it any longer. I felt like I was missing out. I entered my credit card number, and just like that it happened:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/developing-arius-5-during-the-dawn-of-ai-assisted-code/image-6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This was it; this was the culmination of where I had seen the industry evolving towards since the DeepSeek moment. And it made sense. Based on the best practices of some coworkers, I configured &lt;a href=&quot;https://github.com/oraios/serena&quot;&gt;Serena&lt;/a&gt; and &lt;a href=&quot;https://github.com/upstash/context7&quot;&gt;Context 7&lt;/a&gt; MCP servers. In contrast to the earlier Cline experience, it was spot on nearly all the time.&lt;/p&gt;

&lt;p&gt;My new workflow became Visual Studio in a large window, with Claude Code CLI in the terminal next to it and doing frequent, small commits and reverting when it was going in the wrong direction. At first, I prompted for small tasks, like I had to do with my earlier copy-paste workflow, but quickly I felt it could also take on larger tasks. Hell, even more daunting tasks across multiple files that would be very cumbersome in my old workflow:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/woutervanranst/Arius/commit/ae791ba995d97f5fb4bde934bdb6ded1b7c3eee2&quot;&gt;Replace FluentAssertions by Shouldly&lt;/a&gt; in my testing suite&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/woutervanranst/Arius/commit/01c9d9fab12ffb3235fa0fcb5dfa605703c13f4c&quot;&gt;Replace MediatR by Mediator&lt;/a&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Arius.Core&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are both low-value and nasty refactors that used to be sources of accumulating technical debt, since both libraries changed their licensing.&lt;/p&gt;

&lt;h2 id=&quot;vibe--coding-wpf&quot;&gt;Vibe (?) coding WPF&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/developing-arius-5-during-the-dawn-of-ai-assisted-code/image-7.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Arius Explorer&lt;/code&gt; is a rather simple WPF application I wrote in 3 days back in 2023. I love XAML and the declarative nature of it, but I always struggle to get the syntax right. Since I had gutted the core Arius library, it broke Arius Explorer quite hard so I went for a full rewrite.&lt;/p&gt;

&lt;p&gt;I prompted Claude to take a good look at the existing Views and ViewModels and it just one-shotted the &lt;a href=&quot;https://github.com/woutervanranst/Arius/commit/a0c9ad5ef4e857daf9ee99714b29fa49b2950ec3&quot;&gt;RepositoryExplorerView&lt;/a&gt; and the &lt;a href=&quot;https://github.com/woutervanranst/Arius/commit/e08a9ab6b765f549a61471a2c855c9651b2ce382&quot;&gt;ChooseRepositoryView&lt;/a&gt;. They were right there and they just worked. Dario Amodei’s quote of March 2025 came to mind, where he claimed that in 3-6 months, &lt;a href=&quot;https://www.businessinsider.com/anthropic-ceo-ai-90-percent-code-3-to-6-months-2025-3&quot;&gt;AI would be writing 90% of the code&lt;/a&gt;. Looking back, it probably comes close.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Most of Arius 5 is AI written, but 100% checked by a human.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, this isn’t vibe coding. I still care about the code, and I still very much understand what is going on, but I do see the slippery slope.&lt;/p&gt;

&lt;p&gt;This is the long list of topics I wanted to properly integrate in this blog post, but never came around to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I have a love/hate relationship with tests; evolving the test suite as the codebase grew larger was a joy, especially the repetitive and verbose parts that really nail down every execution branch of a complex method and were good to do TDD with.&lt;/li&gt;
  &lt;li&gt;A novel concept: &lt;a href=&quot;https://openai.com/index/introducing-codex/&quot;&gt;Codex&lt;/a&gt; in June 2025, an agent in the cloud, rather autonomously writing a broader test suite.&lt;/li&gt;
  &lt;li&gt;The evolution of tokens on OpenRouter was going exponential as these coding agents became more powerful:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/developing-arius-5-during-the-dawn-of-ai-assisted-code/image-8.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Despite the backlash at launch, I found the model to be quite good. I used it in Codex Web mainly.&lt;/li&gt;
  &lt;li&gt;Editing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CI.yml&lt;/code&gt; workflows becomes a breeze, often one-shotting what is needed.&lt;/li&gt;
  &lt;li&gt;Arius 5 coding innovations over v3:
    &lt;ul&gt;
      &lt;li&gt;Modern .NET: &lt;a href=&quot;https://github.com/martinothamar/Mediator&quot;&gt;Mediator&lt;/a&gt; for loose coupling instead of a facade pattern.&lt;/li&gt;
      &lt;li&gt;AI helps with the syntax; I know what I want, but I struggle with the syntax.&lt;/li&gt;
      &lt;li&gt;Getting the right abstraction level is crucial; I searched a lot for a proper File/Directory/FileSystem abstraction because C# native is all strings.&lt;/li&gt;
      &lt;li&gt;Eventually landed with &lt;a href=&quot;https://github.com/xoofx/zio&quot;&gt;Zio&lt;/a&gt;; it’s platform independent, and I don’t want to deal with slash issues everywhere.&lt;/li&gt;
      &lt;li&gt;I asked ChatGPT for a proper abstraction and it suggested Zio.&lt;/li&gt;
      &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FilePair&lt;/code&gt; is a major innovation that helps a lot in the archive pipeline and simplifies indexing and ZIP dynamics.&lt;/li&gt;
      &lt;li&gt;Arius 4 had SpecFlow, later upgraded to ReqNRoll, but I found the abstraction too cumbersome, so I did away with that.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HandlerContext&lt;/code&gt; in the handlers is an innovation that I like because it benefits maximally from DI for parameters only known after CLI parsing.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 06 Nov 2025 07:05:20 +0000</pubDate>
        <link>https://woutervanranst.github.io/developing-arius-5-during-the-dawn-of-ai-assisted-code/</link>
        <guid isPermaLink="true">https://woutervanranst.github.io/developing-arius-5-during-the-dawn-of-ai-assisted-code/</guid>
        
        
      </item>
    
      <item>
        <title>Modern .NET Configuration Practices (Talk Summary)</title>
        <description>&lt;p&gt;This talk is well worth the watch if you want a clear overview of modern .NET configuration practices.&lt;/p&gt;

&lt;div class=&quot;embed-responsive embed-responsive-16by9 mb-4&quot;&gt;
  &lt;iframe class=&quot;embed-responsive-item&quot; src=&quot;https://www.youtube.com/embed/qGnOnPMFDv0&quot; title=&quot;Modern .NET Configuration Practices&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;All talk materials are publicly available on GitHub. The slides themselves are written in Markdown.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Resource:&lt;/strong&gt; &lt;a href=&quot;https://github.com/codebytes/dotnet-configuration-in-depth&quot;&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Slides:&lt;/strong&gt; &lt;a href=&quot;https://github.com/codebytes/dotnet-configuration-in-depth/blob/main/slides/Slides.md&quot;&gt;Slides.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At NDC Copenhagen, Chris Ayers, a Principal Software Engineer at Microsoft, walked through the architecture of the .NET configuration pipeline. While configuration is often considered a solved problem, misconfigured settings are one of the most common causes of silent runtime failures.&lt;/p&gt;

&lt;p&gt;This post serves as a technical deep dive into how .NET reads, binds, validates, and manages configuration in modern distributed architectures.&lt;/p&gt;

&lt;h2 id=&quot;what-exactly-is-configuration&quot;&gt;What Exactly is “Configuration”?&lt;/h2&gt;

&lt;p&gt;Configuration is not a monolith; rather, it is a collection of distinct operational values divided into three primary categories:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Settings&lt;/strong&gt;: Non-sensitive values that govern runtime behavior, such as retry delays, request timeouts, and maximum queue lengths.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Feature Flags&lt;/strong&gt;: Dynamic toggles that conditionally activate or deactivate code paths (e.g., rolling out a new portal to 10% of users).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Secrets&lt;/strong&gt;: Sensitive data requiring absolute security, such as database connection strings, TLS certificates, and OAuth client credentials.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;the-evolution-compile-time-vs-run-time-configuration&quot;&gt;The Evolution: Compile-Time vs. Run-Time Configuration&lt;/h2&gt;

&lt;p&gt;A classic anti-pattern in the legacy .NET Framework era was baking configurations at compile-time. Different build configurations (such as Debug, Staging, and Release) compiled distinct binaries using XML transformations (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web.config&lt;/code&gt;) with tools like SlowCheetah.&lt;/p&gt;

&lt;h3 id=&quot;legacy-compile-time-pattern&quot;&gt;Legacy Compile-Time Pattern&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/modern-dotnet-configuration-practices-talk-summary/image-1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Under the compile-time approach, the binary tested in QA was technically unique compared to the binary shipped to production, increasing the likelihood of environment-specific bugs.&lt;/p&gt;

&lt;h3 id=&quot;modern-run-time-pattern&quot;&gt;Modern Run-Time Pattern&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/modern-dotnet-configuration-practices-talk-summary/image-2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Modern .NET enforces a &lt;strong&gt;build once, deploy many&lt;/strong&gt; pattern. The binary remains immutable across all environments. Environment-specific configurations are resolved at runtime, allowing the identical binary to run on a developer’s laptop, a staging server, and a production Kubernetes cluster without modification.&lt;/p&gt;

&lt;h2 id=&quot;the-pain-of-the-past-webconfig-and-xml&quot;&gt;The Pain of the Past: Web.config and XML&lt;/h2&gt;

&lt;p&gt;In legacy .NET Framework applications, configuration relied heavily on the verbose &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web.config&lt;/code&gt; XML schema. Settings were limited to raw string-based key-value pairs and were typically accessed through the static &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigurationManager&lt;/code&gt; class.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;appSettings&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ClientValidationEnabled&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Greeting&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello, Everyone!&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/appSettings&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This model suffered from major architectural flaws:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The static nature of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigurationManager&lt;/code&gt; made unit testing incredibly difficult, requiring mock wrappers to isolate tests.&lt;/li&gt;
  &lt;li&gt;XML transformations were fragile and complex.&lt;/li&gt;
  &lt;li&gt;It lacked out-of-the-box dependency injection.&lt;/li&gt;
  &lt;li&gt;Placing secrets directly in the file system made it highly prone to credential leakage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-modern-abstraction-pipeline&quot;&gt;The Modern Abstraction Pipeline&lt;/h2&gt;

&lt;p&gt;Modern .NET replaces static configuration with an extensible, provider-driven pipeline. The architecture decouples the raw configuration sources from how your application consumes them, relying on three foundational interfaces:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IConfigurationSource&lt;/code&gt;: Defines where the data lives (e.g., JSON, XML, INI, Environment Variables, or Key Vault).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IConfigurationProvider&lt;/code&gt;: Pulls the raw data and flattens it into an in-memory dictionary of string key-value pairs.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IConfigurationBuilder&lt;/code&gt;: Orchestrates these providers to build the final &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IConfiguration&lt;/code&gt; container.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;precedence-and-last-provider-wins&quot;&gt;Precedence and “Last Provider Wins”&lt;/h3&gt;

&lt;p&gt;When you instantiate a host, .NET registers default configuration providers in a specific order. Because providers are evaluated sequentially, any key collisions are resolved by the last provider loaded.&lt;/p&gt;

&lt;p&gt;For example, if a setting is defined in both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appsettings.json&lt;/code&gt; and as an environment variable, the environment variable overrides the JSON setting because it is registered later in the builder pipeline.&lt;/p&gt;

&lt;h3 id=&quot;hierarchical-keys-and-os-delimiters&quot;&gt;Hierarchical Keys and OS Delimiters&lt;/h3&gt;

&lt;p&gt;Nested JSON structures are flattened internally into colon-separated strings:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Database&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Connection&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Host=localhost;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The configuration engine represents this as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Database:Connection&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, colons are invalid characters in environment variable names on many operating systems (such as Linux/Bash). To bridge this gap, the .NET environment variable provider automatically translates double underscores (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__&lt;/code&gt;) into colons. In a production container, you can override this connection string by setting an environment variable named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Database__Connection&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;strongly-typed-configuration--the-options-pattern&quot;&gt;Strongly Typed Configuration &amp;amp; the Options Pattern&lt;/h2&gt;

&lt;p&gt;Directly querying &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IConfiguration&lt;/code&gt; using string indexers, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config[&quot;Database:Connection&quot;]&lt;/code&gt;, is fragile and error-prone. The Options Pattern addresses this by allowing you to bind a configuration section directly to a strongly typed POCO class.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConnectionSettings&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConnectionString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeoutSeconds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConnectionSettings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Database&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By registering this with your service collection, you decouple your business logic from the underlying configuration system.&lt;/p&gt;

&lt;h3 id=&quot;choosing-the-right-ioptions-lifetime&quot;&gt;Choosing the Right &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOptions&lt;/code&gt; Lifetime&lt;/h3&gt;

&lt;p&gt;.NET provides three different interfaces for injecting these settings, each with distinct behaviors and lifetimes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOptions&amp;lt;T&amp;gt;&lt;/code&gt;: Registered as a &lt;strong&gt;Singleton&lt;/strong&gt;. It reads the configuration once during startup and never updates. This is ideal for static settings, offering the highest performance with zero overhead.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOptionsSnapshot&amp;lt;T&amp;gt;&lt;/code&gt;: Registered with a &lt;strong&gt;Scoped&lt;/strong&gt; lifetime, recomputed per HTTP request. If your configuration changes on disk, this interface ensures the next HTTP request receives the updated values.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOptionsMonitor&amp;lt;T&amp;gt;&lt;/code&gt;: Registered as a &lt;strong&gt;Singleton&lt;/strong&gt; but actively listens for change notifications from the provider. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CurrentValue&lt;/code&gt; property is always up to date. This is ideal for background services that need to adapt to configuration updates in real time without restarting.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;preventing-silent-failures-fail-fast-validation&quot;&gt;Preventing Silent Failures: Fail-Fast Validation&lt;/h2&gt;

&lt;p&gt;A major issue with dynamic configuration is a missing key that only triggers an exception hours after deployment. Modern .NET enables &lt;strong&gt;Validation on Start&lt;/strong&gt; to verify your settings as the host boots up, executing a fail-fast pattern.&lt;/p&gt;

&lt;p&gt;You can apply standard data annotations directly to your Options models:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WebHookSettings&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Endpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MaxRetries&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, configure the builder to validate on start:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WebHookSettings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WebHook&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ValidateDataAnnotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ValidateOnStart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Endpoint&lt;/code&gt; is empty or invalid, the host throws an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OptionsValidationException&lt;/code&gt; on startup, blocking the deployment pipeline immediately instead of letting a broken service silently go live.&lt;/p&gt;

&lt;h2 id=&quot;microservices-centralized-configuration-and-the-sentinel-key-pattern&quot;&gt;Microservices: Centralized Configuration and the Sentinel Key Pattern&lt;/h2&gt;

&lt;p&gt;In microservices architectures, managing individual &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appsettings.json&lt;/code&gt; files across dozens of services becomes unmanageable. Azure App Configuration offers a centralized repository, but polling the cloud provider constantly for updates can result in rate-limiting issues.&lt;/p&gt;

&lt;p&gt;To resolve this, use the &lt;strong&gt;Sentinel Key Pattern&lt;/strong&gt;. Rather than observing all keys for updates, the application is configured to monitor a single “sentinel” key (e.g., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Settings:Sentinel&lt;/code&gt;) at a set interval (such as every 5 minutes).&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddAzureAppConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureRefresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;refresh&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;refresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Settings:Sentinel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;refreshAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetCacheExpiration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromMinutes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Whenever you modify any key-value pair in Azure App Configuration, you increment the sentinel value. When the application detects a change in the sentinel, it automatically reloads all registered configuration keys in a single transaction.&lt;/p&gt;

&lt;h2 id=&quot;cloud-native-orchestration-with-net-aspire&quot;&gt;Cloud-Native Orchestration with .NET Aspire&lt;/h2&gt;

&lt;p&gt;With .NET Aspire, Microsoft leverages the existing configuration pipeline to manage service discovery in distributed systems. In a traditional setup, you must manually coordinate URLs and port bindings between microservices. Aspire abstracts this complexity.&lt;/p&gt;

&lt;p&gt;In the Aspire orchestrator (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppHost&lt;/code&gt;), we register a backend API and pass it as a reference to a frontend project:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apiService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddProject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Projects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApiService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;apiservice&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddProject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Projects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WebFrontend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;webfrontend&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;apiService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Behind the scenes, Aspire injects this reference into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;webfrontend&lt;/code&gt; container using .NET’s double-underscore environment variable pattern:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;services__apiservice__http__0=http://localhost:5461
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the frontend C# code, resolving the service location is entirely seamless:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddHttpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WeatherClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseAddress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://apiservice&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The underlying service discovery provider intercepts &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;http://apiservice&quot;&lt;/code&gt;, parses the auto-injected environment variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;services__apiservice...&lt;/code&gt;, and routes the traffic directly to the correct address.&lt;/p&gt;

&lt;h2 id=&quot;core-security-best-practices-for-net-configuration&quot;&gt;Core Security Best Practices for .NET Configuration&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Do Not Commit Secrets&lt;/strong&gt;: Never allow passwords, API keys, or certificates to enter your git history.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Use User Secrets Locally&lt;/strong&gt;: The local &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secrets.json&lt;/code&gt; file is stored outside your repository folder under your OS user profile, eliminating the risk of accidental commits.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Passwordless in Production&lt;/strong&gt;: Combine Azure Key Vault with Managed Identities (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DefaultAzureCredential&lt;/code&gt;). This shifts authentication from passwords in connection strings to RBAC-based Azure AD authentication.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Avoid Logging Entire Configurations&lt;/strong&gt;: Be cautious when logging configuration states. Directly serializing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IConfiguration&lt;/code&gt; can easily leak keys and secrets straight into your application insights or syslog servers.&lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Wed, 05 Nov 2025 10:09:12 +0000</pubDate>
        <link>https://woutervanranst.github.io/modern-net-configuration-practices/</link>
        <guid isPermaLink="true">https://woutervanranst.github.io/modern-net-configuration-practices/</guid>
        
        
      </item>
    
      <item>
        <title>Choose Boring Technology</title>
        <description>&lt;p&gt;Like wine, (some) technology gets better with age. The blog &lt;a href=&quot;https://boringtechnology.club/&quot;&gt;Choose Boring Technology&lt;/a&gt; advocates to make technology choices as boring as possible, unless you deliberately choose or have room for the unknown. “Boring” is not a lack of ambition, but a deliberate way to spend your innovation tokens where they matter most.&lt;/p&gt;

&lt;p&gt;I would have loved to see the talk in a YouTube video, but only the slides are available - so I made a longform instead. Enjoy the read.&lt;/p&gt;

&lt;p&gt;In the world of software engineering, few things are as seductive as the promise of the new. Fresh frameworks, databases, languages, and infrastructure solutions flood tech news and conference talks. The underlying message? &lt;strong&gt;Adopt the latest and greatest, or risk falling behind.&lt;/strong&gt; But for every team that leaps onto the new, there’s another quietly delivering value with so-called “boring” technology. Dan McKinley, in his seminal talk and essay “Choose Boring Technology,” champions the latter approach, not out of nostalgia or fear, but from hard-won experience and clear-eyed analysis.&lt;/p&gt;

&lt;p&gt;What follows is not an argument against innovation, but a story about understanding the real cost of our choices. It’s about recognizing that software is as much about people, organizations, and attention as it is about lines of code. And it’s a call to rethink the urge to chase every new shiny tool.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;the-real-constraints-attention-not-hardware&quot;&gt;The Real Constraints: Attention, Not Hardware&lt;/h2&gt;

&lt;p&gt;McKinley’s thesis starts with a question many teams overlook: &lt;strong&gt;What is truly scarce in your engineering organization?&lt;/strong&gt; Contrary to popular belief, it’s rarely CPU cycles or disk space. The scarcest resource is always &lt;strong&gt;attention&lt;/strong&gt; - the focused, creative energy of the people who keep systems running and features shipping.&lt;/p&gt;

&lt;p&gt;Much like Maslow’s hierarchy of needs, engineering organizations have a pyramid of priorities. At the base: keeping systems operational, secure, and reliable. Only when these are satisfied can developers focus on the higher needs - delivering product features, optimizing user experience, innovating in the business domain. When teams squander attention by endlessly revisiting basic infrastructure problems, they stall or even regress.&lt;/p&gt;

&lt;p&gt;The lesson? &lt;strong&gt;Every non-essential technology choice is a tax on your attention.&lt;/strong&gt; And when you’re taxed enough, you’re no longer shipping value - you’re just firefighting.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/choose-boring-technology/image-1.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-unknown-unknowns-when-novelty-bites-back&quot;&gt;The Unknown Unknowns: When Novelty Bites Back&lt;/h2&gt;

&lt;p&gt;“Boring” technology is not without flaws, but its risks are known and manageable. By contrast, adopting new technology brings &lt;strong&gt;unknown unknowns&lt;/strong&gt; - subtle bugs, untested edge cases, and poor documentation.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Category&lt;/th&gt;
      &lt;th&gt;Known Tech (e.g., MySQL)&lt;/th&gt;
      &lt;th&gt;New Tech (e.g., Latest NoSQL DB)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Failure Modes&lt;/td&gt;
      &lt;td&gt;Documented, predictable&lt;/td&gt;
      &lt;td&gt;Undocumented, sometimes catastrophic&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Support Community&lt;/td&gt;
      &lt;td&gt;Large, active&lt;/td&gt;
      &lt;td&gt;Sparse, slow to respond&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Tooling&lt;/td&gt;
      &lt;td&gt;Mature, stable&lt;/td&gt;
      &lt;td&gt;Immature, rapidly changing&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Hiring&lt;/td&gt;
      &lt;td&gt;Easy, large talent pool&lt;/td&gt;
      &lt;td&gt;Difficult, niche skillset&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Compliance/Security&lt;/td&gt;
      &lt;td&gt;Proven, best practices&lt;/td&gt;
      &lt;td&gt;Unproven, higher audit risk&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;This doesn’t mean you should never innovate, but recognize that every new dependency is a bet with uncertain odds. The more dependencies, the more complex the system, the more attention you will pay for the privilege.&lt;/p&gt;

&lt;h2 id=&quot;the-cautionary-tale-of-microservices-mania&quot;&gt;The Cautionary Tale of Microservices Mania&lt;/h2&gt;

&lt;p&gt;A real-world example is the microservices boom of the mid-2010s. Many organizations, inspired by Netflix and Amazon, rushed to break monoliths into dozens of services, each potentially using its own stack. &lt;strong&gt;For Netflix, with armies of SREs and deep pockets, this worked. For most companies, it led to operational chaos:&lt;/strong&gt; constant integration pain, fragile deployments, and a hiring bottleneck for each niche technology. In the end, many quietly reverted to a simpler architecture, having learned the cost of complexity the hard way.&lt;/p&gt;

&lt;h2 id=&quot;shipping-value-not-complexity&quot;&gt;Shipping Value, Not Complexity&lt;/h2&gt;

&lt;p&gt;In a field obsessed with reinvention, “boring” is a badge of honor. It signals that your team is focused, disciplined, and free to spend its limited attention on what matters - delivering value to users and customers. When you embrace boring technology, you reclaim the ability to innovate where it counts: not in your tool choices, but in your product.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Production Is Hard&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/choose-boring-technology/image-2.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In McKinley’s words, &lt;strong&gt;“Shipping things consistently is the path to developer happiness.”&lt;/strong&gt; The next time you feel the itch to adopt the latest and greatest, ask yourself: is this where my team’s attention belongs?&lt;/p&gt;
</description>
        <pubDate>Wed, 11 Jun 2025 08:17:50 +0000</pubDate>
        <link>https://woutervanranst.github.io/choose-boring-technology/</link>
        <guid isPermaLink="true">https://woutervanranst.github.io/choose-boring-technology/</guid>
        
        
      </item>
    
  </channel>
</rss>
