<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Software etc.</title>
        <link>https://blog.waleson.com/</link>
        <description>Random thoughts on software by Jouke Waleson</description>
        <lastBuildDate>Sat, 03 Oct 2026 14:16:35 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <copyright>© Jouke Waleson</copyright>
        <atom:link href="https://blog.waleson.com/rss.xml" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Just play Factorio]]></title>
            <link>https://blog.waleson.com/2026/10/just-play-factorio/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2026/10/just-play-factorio/</guid>
            <pubDate>Sat, 03 Oct 2026 14:10:22 GMT</pubDate>
            <description><![CDATA[Factorio is my favorite game of all time. I've now spent close to 700 hours on it according to Steam, and that doesn't include a couple hundred hours where I played an illegal copy in 2014. I was just "trying it out", I…]]></description>
            <content:encoded><![CDATA[<p>Factorio is my favorite game of all time. I've now spent close to 700 hours on it according to Steam, and that doesn't include a couple hundred hours where I played an illegal copy in 2014. I was just &quot;trying it out&quot;, I swear.</p>
<p>Whenever Factorio comes up on Hacker News, some software engineer inevitably complains &quot;<em>it's too much like my day to day work</em>&quot;. Building things by hand, designing new architectures, blueprints and interfaces, the endless refactoring, coding and Factorio are very much the same. The complaint is that it's like a truck driver playing Truck Driver Simulator at night after driving the truck all day. It will just feel like too much of the same motions.</p>
<p>Well, that was before AI coding. Now the complaint of developers is &quot;I just chat to Codex/Cursor/Claude and hit enter all the time, the craft is gone!&quot;. We no longer experience this coding, puzzling, refactoring loop that we really enjoyed pre Opus 4.6.</p>
<p>So lately, I've gone back to playing Factorio a couple of hours per month. Startup + family life doesn't allow for much more unfortunately.</p>
<p>Coding as it was is no longer economically viable, but playing Factorio was never about that. Also its game mechanics are probably better than those in your IDE ;)</p>
<p>you can discuss here but due to all the blogger spam, <a href="https://news.ycombinator.com/item?id=49944421">the thread on HN</a> is better</p>
]]></content:encoded>
            <category>personal</category>
        </item>
        <item>
            <title><![CDATA[How we brought our Rust CI from 20 minutes to less than 5 for Comper]]></title>
            <link>https://blog.waleson.com/2026/08/how-we-brought-our-rust-ci-from-20/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2026/08/how-we-brought-our-rust-ci-from-20/</guid>
            <pubDate>Mon, 17 Aug 2026 20:16:02 GMT</pubDate>
            <description><![CDATA[This post was written by GPT-Sol 5.6 mimicking my writing style. After multiple 12h+ days of optimizing CI I could not muster the energy to write a full on blog post, but I did want to share our optimizations with the…]]></description>
            <content:encoded><![CDATA[<h3>AI Disclaimer</h3>
<p>This post was written by GPT-Sol 5.6 mimicking my writing style. After multiple 12h+ days of optimizing CI I could not muster the energy to write a full on blog post, but I did want to share our optimizations with the world. The contents are correct and it's based on our commit history and our codebase.</p>
<p>Ultimately it's just a list of things we did to optimize our Rust compilation, which is a problem that everyone that builds large Rust projects has. Our compilation jobs alone sometimes took 9 minutes. Now we reliably do it in just over 2 minutes. The tests then run for another 2.5.</p>
<h2>Introduction</h2>
<p>Comper currently contains 176,496 lines of Rust code across 975 files and 18 crates.</p>
<p>The test suite contains:</p>
<ul>
<li>762 Rust unit tests</li>
<li>377 Rust integration tests</li>
<li>985 frontend unit and component tests</li>
<li>91 Playwright end-to-end tests</li>
<li>9 additional ignored Rust tests</li>
</ul>
<p>Our merge request pipeline builds the frontend, runs the frontend tests, compiles every Rust test binary, runs three backend test shards, and runs two end-to-end test shards.</p>
<p>The complete pipeline now takes about 4 minutes and 40 seconds. It used to take around 20 minutes.</p>
<p>There was no single compiler flag that fixed it. The largest improvement came from running GitLab runners on our own Kubernetes cluster and deliberately using their local disks as persistent caches.</p>
<h2>The biggest hack: node-local caches</h2>
<p>GitLab normally restores a cache before a job and uploads it afterwards. This works well when a cache consists of a few large files. It works much less well for Rust directories containing tens of thousands of small files.</p>
<p>For every job GitLab had to:</p>
<ol>
<li>Walk the cache directory</li>
<li>Package or compress it</li>
<li>Upload it</li>
<li>Download it in another job</li>
<li>Extract it again</li>
</ol>
<p>At some point we were spending almost as much time moving Rust caches as compiling Rust.</p>
<p>Our GitLab runners run on a dedicated Kubernetes node pool. Every job pod mounts a directory from the local filesystem of its node:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-toml"><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[[</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">runners</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  request_concurrency = </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">4</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  cache_dir = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/cache"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  [</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">runners</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">kubernetes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    namespace = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"gitlab-runner"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    [</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">runners</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">kubernetes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">node_selector</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      nodepool = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"gitlab-runners"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  [[</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">runners</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">kubernetes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">host_path</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    name = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"cache"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    mount_path = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/cache"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    host_path = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/var/lib/gitlab-runner/cache"</span></span></code></pre>
<p>This is an anti-pattern. Kubernetes pods are supposed to be disposable and independent of the node on which they run. Our cache is neither.</p>
<p>We accepted this because the cache is only an optimization. When a node disappears, the next build starts cold but still works. Our jobs are scheduled on a small dedicated runner pool, so they usually return to a node that already has the data.</p>
<p>Cargo and kache use separate directories on this mounted disk:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-yaml"><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">variables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  CARGO_HOME</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/cache/comper/cargo"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  KACHE_CACHE_DIR</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/cache/comper/kache"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  KACHE_MAX_SIZE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"100GiB"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  CARGO_INCREMENTAL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"0"</span></span></code></pre>
<p><code>CARGO_HOME</code> contains the registry and git dependencies. Kache contains compiler outputs.</p>
<p>We no longer cache the complete Cargo <code>target/</code> directory. Kache gives us most of its useful contents without requiring GitLab to zip, transfer and unzip the whole directory.</p>
<p>Coverage is the exception. It still caches a limited set of Cargo fingerprints and dependencies.</p>
<h2>Replacing sccache with kache</h2>
<p>We originally used sccache. It worked, but its Rust cache keys included the compilation directory.</p>
<p>Every GitLab job gets a fresh checkout path. We also use lots of local Git worktrees. In both cases sccache only achieved about a 60% cross-directory hit rate.</p>
<p>We replaced it with <a href="https://github.com/kunobi-ninja/kache">kache</a>, a new and mostly vibe-coded compiler cache. Kache's keys are independent of the checkout directory, so an artifact compiled in one worktree can be reused in another.</p>
<p>Our measurements were:</p>
<ul>
<li>Cross-worktree build: 84 seconds with sccache, 40 seconds with kache</li>
<li>Warm CI compile: 59 seconds with sccache, 34 seconds with kache</li>
</ul>
<p>CI wraps every rustc invocation with kache:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-yaml"><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">variables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  RUSTC_WRAPPER</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"kache"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  KACHE_S3_BUCKET</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"comper-sccache"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  KACHE_S3_ENDPOINT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"https://s3.sbg.io.cloud.ovh.net"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  KACHE_S3_REGION</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sbg"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  KACHE_S3_PREFIX</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"kache"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  KACHE_NAMESPACE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"comper"</span></span></code></pre>
<p>The node-local directory is the fast path. S3 shares artifacts between nodes and keeps the cache available when a node is replaced.</p>
<p>Kache requires a daemon for remote cache access:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-yaml"><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">.ensure_kache</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x26;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ensure_kache</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mkdir -p "${CARGO_HOME}" "${KACHE_CACHE_DIR}"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">command -v kache</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">kache --version</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">kache daemon start</span></span></code></pre>
<p>We also tried bulk-prefetching the S3 cache at the start of every job. It downloaded too much and was not reliably faster, so we removed it.</p>
<h2>Incremental compilation is disabled in CI</h2>
<p>Incremental compilation is great when a developer repeatedly changes the same checkout. CI jobs start with a clean Cargo target directory.</p>
<p>In that situation incremental compilation added 15 to 20% overhead, created larger artifacts and prevented kache from caching some compiler outputs.</p>
<p>We explicitly disable it:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-yaml"><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">variables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  CARGO_INCREMENTAL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"0"</span></span></code></pre>
<p>Locally we make the opposite choice. More on that later.</p>
<h2>Compile all tests once</h2>
<p>Originally every backend test shard compiled the Rust workspace. Each shard also restored and uploaded its own caches.</p>
<p>That multiplied several minutes of compilation and cache transfer by the number of shards.</p>
<p>Now one job compiles all test binaries and creates a nextest archive:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-yaml"><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">build-test-binaries</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  stage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">test</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${CI_DOCKER_IMAGE}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  needs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: []</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  variables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    FF_USE_FASTZIP</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"true"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ARTIFACT_COMPRESSION_LEVEL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"fastest"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ensure_kache</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">cargo build --profile test -p migration --bin migration-cli --locked</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>-</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      cargo-nextest nextest archive</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      --workspace</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      --cargo-profile test</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      --profile ci</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      --archive-file nextest-archive.tar.zst</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  artifacts</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    paths</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">nextest-archive.tar.zst</span></span></code></pre>
<p>The archive contains:</p>
<ul>
<li>All Rust test binaries</li>
<li><code>comper-server</code></li>
<li>The migration CLI</li>
</ul>
<p>This is the only large Rust artifact transferred between jobs.</p>
<p>We initially built <code>comper-server</code> separately before creating the nextest archive. That caused Cargo to compile the root crate twice because feature unification differed between the normal build and the test build.</p>
<p>Removing one innocent-looking <code>cargo build</code> command saved a complete compilation of the application.</p>
<h2>Our nextest fork</h2>
<p>Our integration tests make HTTP requests to a real Comper server. Starting a separate server for every test would give excellent isolation, but it would also make the suite extremely slow.</p>
<p>Starting one server outside nextest was not ideal either. Every test would have to wait for it, including tests that did not need it. We would also lose nextest's normal process management, output capture and test scheduling.</p>
<p>We built a server-wrapper feature in <a href="https://github.com/comper-io/nextest">our nextest fork</a>. A server wrapper belongs to a group of tests rather than one test. Nextest starts it lazily when the first matching test is ready, probes it until it is healthy, captures its output and shuts it down when the run finishes.</p>
<p>Our configuration looks like this:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-toml"><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">experimental = [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"server-wrappers"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">scripts</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">server-wrapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">test-server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">command = [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"./scripts/start_test_server.sh"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">probe = {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  url = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"http://127.0.0.1:5150/_readiness"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  interval = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"100ms"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  timeout = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"500s"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">capture-stdout = </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">capture-stderr = </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[[</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">profile</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">scripts</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">filter = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"all()"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">server-wrapper = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"test-server"</span></span></code></pre>
<p>One Comper server is started per CI shard and shared by the tests in that shard. The tests still run in parallel, which also exercises concurrency inside the application.</p>
<p>At the time we introduced it, more than 150 integration tests ran in about 40 seconds with one shared server. Running every test with its own server would have taken around 15 minutes.</p>
<p>There are trade-offs. A test can corrupt shared in-process state and affect another test. Our database and fixtures have to support concurrent tests. A crashed server also affects the rest of the shard.</p>
<p>For our suite this is a good trade-off. The shared server is part of the system under test, and concurrent requests are closer to how it runs in production.</p>
<p>We proposed the feature upstream in <a href="https://github.com/nextest-rs/nextest/discussions/3330">nextest discussion #3330: Feature request: sidecar / server wrapper for client/server tests</a>. The discussion also covers alternatives such as creating an on-disk seed and starting a fresh service for each test, process lifecycle questions, and use cases with heavyweight servers that can take 90 seconds to start.</p>
<p>The maintainers were interested in accepting some form of the feature. Until its lifecycle and isolation contracts are worked out, we are happy to maintain the fork.</p>
<h2>Run the backend tests without Cargo</h2>
<p>The backend tests are divided between three GitLab jobs.</p>
<p>Those jobs download the archive and run their assigned partition:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-yaml"><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">back-end-tests</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  stage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">test</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${CI_DOCKER_IMAGE}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  parallel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  needs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">job</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">build-test-binaries</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      artifacts</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mkdir -p "${NEXTEST_ARCHIVE_DIR}"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>-</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      cargo-nextest nextest run</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      --archive-file nextest-archive.tar.zst</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      --extract-to "${NEXTEST_ARCHIVE_DIR}"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      --extract-overwrite</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      --workspace-remap .</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      --partition "count:${CI_NODE_INDEX}/${CI_NODE_TOTAL}"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      --profile ci</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      --no-fail-fast</span></span></code></pre>
<p>These jobs never invoke rustc.</p>
<p>They extract the binaries, start PostgreSQL, Keycloak and Mailhog, and run their part of the suite.</p>
<h2>Reuse the binaries for end-to-end tests</h2>
<p>Our Playwright jobs use the same precompiled server and migration CLI. There is no additional Rust build for E2E.</p>
<p>The browser suite runs in two GitLab shards. Each shard runs two Playwright workers:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-typescript"><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> ciNodeIndex</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> process.env.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">CI_NODE_INDEX</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> ciNodeTotal</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> process.env.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">CI_NODE_TOTAL</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> defineConfig</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    fullyParallel: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    retries: process.env.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">CI</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ?</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> :</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    workers: process.env.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">CI</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ?</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 2</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> :</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> undefined</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    shard:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        ciNodeIndex </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x26;&#x26;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ciNodeTotal</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            ?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                  current: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ciNodeIndex),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                  total: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ciNodeTotal),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">              }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            :</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> undefined</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span></code></pre>
<p>We also changed the fixtures to create one board per Playwright worker instead of one board per test. Creating and deleting a board took between 300 and 600 milliseconds. Repeating that for every test added up.</p>
<p>Navigation helpers use <code>domcontentloaded</code> where waiting for every image and asset provides no additional confidence.</p>
<h2>Mold and the parallel rustc frontend</h2>
<p>CI uses LLVM for code generation, mold for linking and rustc's parallel frontend:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-yaml"><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">variables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  RUSTFLAGS</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"-C link-arg=-fuse-ld=mold -Z threads=8"</span></span></code></pre>
<p>The parallel frontend reduced a full compilation by about 25%.</p>
<p>Eight and sixteen threads performed about the same in our measurements. We use eight in CI to reduce peak memory when multiple jobs are running on the same node.</p>
<p>Mold only changes the linker. It does not change Rust's code-generation backend or reduce the quality of the generated code.</p>
<h2>Linux and macOS development</h2>
<p>Local development has different priorities. We care about the edit-compile-run loop more than runtime performance.</p>
<p>Linux uses wild and sixteen rustc frontend threads. Apple Silicon uses the platform linker and ten frontend threads:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-toml"><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">target</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">x86_64-unknown-linux-gnu</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">linker = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"clang"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">rustflags = [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"-C"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"link-arg=--ld-path=wild"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"-Z"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"threads=16"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">target</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">aarch64-apple-darwin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">rustflags = [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"-Zthreads=10"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span></code></pre>
<p>We tested multiple combinations with the compile cache disabled:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-text"><span class="line"><span>config                          full build   incremental</span></span>
<span class="line"><span>cranelift + wild                145s / 155s  8s</span></span>
<span class="line"><span>cranelift, no fast linker       161s         20s</span></span>
<span class="line"><span>LLVM + wild                     158s / 169s  8s</span></span>
<span class="line"><span>LLVM, no fast linker            182s         23s</span></span>
<span class="line"><span>LLVM + mold                     157s         8s</span></span>
<span class="line"><span>LLVM + wild + threads           121s         8s</span></span>
<span class="line"><span>LLVM + wild + threads, deps O3  285s         6s</span></span></code></pre>
<p>The fast linker saves approximately 12 to 15 seconds on every rebuild. The parallel frontend mainly improves full builds.</p>
<p>We previously used Cranelift for local development. A year ago it made a large difference. With our current nightly toolchain, Cranelift and LLVM both produced an eight-second incremental result. Cranelift only saved about 8% on a full build.</p>
<p>We removed it.</p>
<p>Local development, CI and production now all use LLVM. The difference is in profiles, linkers and caching, not in code-generation quality.</p>
<h2>Cache dependencies locally, keep our crates incremental</h2>
<p>Wrapping every local rustc invocation with kache caused another problem.</p>
<p>Kache cannot cache incremental output, so it strips Cargo's incremental compilation flags. This meant every edit to one of our own crates paid for a full code-generation pass.</p>
<p>Kache's own exclude patterns did not solve this. Excluded crates still went through its passthrough path, which also removed the incremental flags.</p>
<p>Cargo only enables incremental compilation for workspace crates. Registry and git dependencies are not incremental. We use that distinction in a small dispatcher:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-bash"><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">case</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">CARGO_MANIFEST_DIR</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:-</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> in</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  *</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF">/registry/src/</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*|*</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF">/git/checkouts/</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">)</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> exec</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> kache</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">$@</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ;;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  *)</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> exec</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">$@</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ;;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">esac</span></span></code></pre>
<p>Dependencies go through kache. Our workspace crates go directly to rustc and keep incremental compilation.</p>
<p>This also allows a new worktree to reuse dependency artifacts compiled in another worktree without slowing down the normal editing loop.</p>
<p>A warm full local build measured around 112 seconds. A one-line edit measured around 19 seconds before the final linker changes and eight seconds for an edit isolated to <code>comper-api</code> afterwards.</p>
<h2>Splitting one giant crate into 18 crates</h2>
<p>We also split the application into 18 Rust crates.</p>
<p>This was by far the largest change.</p>
<p>The original root crate contained HTTP controllers, database access, background workers, LLM pipelines, authentication, repository analysis and runtime infrastructure. Everything depended on everything else.</p>
<p>The root crate went from approximately 54,600 lines to 34,300 and eventually to 5,700 lines. The workspace now contains crates such as:</p>
<ul>
<li><code>comper-api</code></li>
<li><code>comper-db</code></li>
<li><code>comper-domain</code></li>
<li><code>comper-workers</code></li>
<li><code>comper-runtime</code></li>
<li><code>comper-auth</code></li>
<li><code>comper-agent</code></li>
<li><code>comper-repo-intelligence</code></li>
</ul>
<p>This helped local compilation by limiting invalidation when changing code near the top of the dependency graph. It also gave rustc more independent compilation units that can be built in parallel.</p>
<p>It did not produce the spectacular clean-CI improvement we expected.</p>
<p>CI still builds the complete workspace. Changing <code>comper-db</code>, for example, invalidates most crates above it. Splitting a large crate does not help when all the resulting crates still need to be rebuilt.</p>
<p>The architectural improvement was enormous. The direct clean-build improvement was much smaller. Linkers, caching and removing duplicated builds produced clearer benchmark results than the monumental crate split.</p>
<h2>We do not optimize dependencies in development</h2>
<p>A commonly recommended Cargo configuration is:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-toml"><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">profile</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dev</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">"*"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">opt-level = </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3</span></span></code></pre>
<p>This makes dependencies run faster but also makes them much slower to compile.</p>
<p>In our benchmark it changed a cold build from 121 seconds to 285 seconds. The incremental build only changed from eight to six seconds.</p>
<p>Dependencies are not rebuilt when editing our own crates, so we would pay more than two additional minutes on a cold build for a two-second improvement that rarely matters.</p>
<p>Normal development therefore uses unoptimized dependencies. We have a separate release-profile task when we need the server itself to run quickly.</p>
<h2>Production still gets full LLVM optimization</h2>
<p>Production is built with Cargo's release profile:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-yaml"><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">build-release</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ensure_kache</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">cargo build --release --bin comper-server --locked</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">cp target/release/comper-server ./comper-server</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">cargo build --release -p migration --bin migration-cli --locked</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">cp target/release/migration-cli ./migration-cli</span></span></code></pre>
<p>This is a normal optimized LLVM release build. It uses mold for linking, but mold does not change LLVM's optimizations or the generated machine code before linking.</p>
<p>The development optimizations do not reduce production quality.</p>
<h2>Smaller improvements</h2>
<p>The large changes account for most of the reduction, but we removed smaller pieces of unnecessary work too:</p>
<ul>
<li>Only one pipeline runs per change. An MR does not also get a redundant branch pipeline.</li>
<li>Backend jobs use <code>rules: changes</code> and can be skipped for frontend-only changes.</li>
<li>Coverage only runs for tags.</li>
<li>Merge request pre-commit checks only inspect changed files.</li>
<li>The CI image contains Chromium instead of Chromium, Firefox and WebKit.</li>
<li>The CI image is built with nix2container and split into approximately 80 reusable OCI layers.</li>
<li>Jobs use a content-hashed CI image and do not wait for the image-building job.</li>
<li><code>libeatmydata</code> disables <code>fsync</code> inside CI containers.</li>
<li>The Mermaid validation bundle is stored by content hash and reused.</li>
<li>We transfer one nextest archive instead of the archive plus duplicate server binaries.</li>
<li>Artifact compression uses the fastest setting because the compiled binaries do not compress enough to justify the CPU time.</li>
<li>We removed Axum's <code>debug_handler</code> attributes from frequently changed API code. That saved another one or two seconds per check.</li>
<li>The backend model drift check runs once rather than once per test shard.</li>
<li>Playwright browsers are already present in the CI image, so E2E jobs do not download Chromium.</li>
</ul>
<p>None of these changes reduced the pipeline from 20 minutes to less than five by itself. Together they removed a lot of waiting.</p>
<h2>The resulting pipeline</h2>
<p>The critical path is now:</p>
<ol>
<li>Start jobs immediately with a prebuilt CI image.</li>
<li>Read Cargo dependencies and kache artifacts directly from the runner node.</li>
<li>Compile all Rust test binaries once using LLVM, mold and the parallel frontend.</li>
<li>Transfer one nextest archive.</li>
<li>Run three backend test shards without a compiler.</li>
<li>Run two Playwright shards using the same precompiled server.</li>
<li>Run frontend tests, linting and other checks in parallel.</li>
</ol>
<p>The full merge request pipeline, including the browser tests, now finishes in approximately 4 minutes and 40 seconds.</p>
<p>The biggest improvement did not come from making Rust dramatically faster. It came from compiling things once, keeping caches close to the CPU, and no longer transferring the same thousands of files between every job.</p>
]]></content:encoded>
            <category>infrastructure</category>
            <category>programming</category>
        </item>
        <item>
            <title><![CDATA[My one in a million patent collision - easee vs ibis vision]]></title>
            <link>https://blog.waleson.com/2026/06/my-one-in-million-patent-collision/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2026/06/my-one-in-million-patent-collision/</guid>
            <pubDate>Sun, 07 Jun 2026 10:28:02 GMT</pubDate>
            <description><![CDATA[Back in 2021 I was a bit down as a CTO of easee, but then decided to do what I do best: innovate. It turns out that roughly around the same time, IBIS Vision, one of our UK competitors, was doing the same thing. They…]]></description>
            <content:encoded><![CDATA[<p>Back in 2021 I was a bit down as a CTO of easee, but then decided to do what I do best: innovate. It turns out that roughly around the same time, IBIS Vision, one of our UK competitors, was doing the same thing. They filed &quot;<a href="https://worldwide.espacenet.com/patent/search/family/078828464/publication/GB2612366A">GB2612366A - Method and system for eye testing</a>&quot; and we filed &quot;<a href="https://worldwide.espacenet.com/patent/search/family/078536539/publication/NL2029579B1">NL2029579A - Guiding a user to a test position for performing a computer-based eye exam</a>.&quot; . This describes almost exactly the same ingenious solution and completely randomly, has EXACTLY the same priority date of November 1st 2021.</p>
<h2>Legal implications</h2>
<p>What follows is a bit of a legal quagmire. Easee would not be able to attack IBIS, and neither could IBIS attack easee, because both had valid patents for the same invention. Who would be able to expand into other regions though? And could one of the parties license their patent to other entities? So many questions.</p>
<h2>The invention</h2>
<p>We were trying to figure out how to estimate the distance of the user to the screen at which they performed our vision test. We relied on heel-to-toe steps and the shoe size, but this was a bit clunky. So I came up with a better method using the blind spot in your vision.</p>
<figure><img src="https://blog.waleson.com/images/2026/06/my-one-in-million-patent-collision/e2b93a63-37189bdf-f127-4fa6-98bfa5c06647e857-source.webp" alt="Image copyright by Scientific American" loading="lazy"><figcaption>Image copyright by Scientific American</figcaption></figure>
<p>The ingenious parts of this invention are:</p>
<ul>
<li>
<p>Pre-calibrating the screen with a physical measure (like a plastic bank / ID card)</p>
</li>
<li>
<p>Blind spot distance check</p>
</li>
<li>
<p>the angle between the eye's blind spot vs center-of-vision (fovea) is very uniform in the population</p>
</li>
<li>
<p>you can do a blind spot check (click when the circle in the screen disappears) on a pre-calibrated screen to determine the exact distance to the screen</p>
</li>
<li>
<p>Then use the webcam to determine the pupillary distance (the distance between the two pupils) and track that to measure the distance to the screen while taking the rest of the vision test</p>
</li>
</ul>
<p>This magical combination is identical in both patents.</p>
<p>Inventing this required a couple of weeks of deep thinking and doing research on my side, and I'm still pretty proud of it. We did not have any communication with IBIS so no clue what the process for invention looked like on their side.</p>
<h2>Better execution</h2>
<p>Just a couple of days ago the US patent in the family was granted as US20260151029A1. I'm still the inventor but hold no rights of course. This event triggered this blog post, which had been on my to-do list for quite some time. Since this invention a lot happened. I quit my job as CTO of easee back in 2023, and in 2025 easee was bought by its main competitor in the USA - Visibly. This was after years of fighting each other via their respective patent portfolios. Notably the <a href="https://www.juve-patent.com/cases/bird-bird-client-visibly-wins-against-dutch-competitor-over-online-eye-test/">landmark case of Visibly vs Easee</a> in 2020 in which Bird &amp; Bird and Taylor Wessing went head to head, and which I remember from being on vacation in August 2020 in the south of France, and taking calls with very expensive lawyers from my tent. Fun times ;)</p>
<p>It's clear that easee (and now Visibly) executed their patent strategy way better. Where easee has expanded this family from NL into WO, EP, and now the US, IBIS is stuck with their GB patent. Yves was very strategic from the start. Invention is one, but executing on your strategy is another thing, as this case shows.</p>
<p>Now I'm not a patent attorney and have not studied this field, but I do know that this case makes for a rare legal anomaly. We can only wonder what would have happened if IBIS decided to execute international expansion in parallel with easee, and both would have hit all their filing deadlines.</p>
]]></content:encoded>
            <category>startups</category>
            <category>easee</category>
        </item>
        <item>
            <title><![CDATA[Navigating the Fog of War: Age of Empires, AI Agents, and the Rise of Dark Code]]></title>
            <link>https://blog.waleson.com/2026/05/navigating-fog-of-war-age-of-empires-ai/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2026/05/navigating-fog-of-war-age-of-empires-ai/</guid>
            <pubDate>Sun, 24 May 2026 15:43:10 GMT</pubDate>
            <description><![CDATA[This was a 1 hour round-table discussion with CTOs from Canada ( TrueNorthCTO ) on May 6th 2026. I gave the main talk and during certain parts we went into discussion mode. It was a lot of fun and afterwards there was…]]></description>
            <content:encoded><![CDATA[<p><em>This was a 1 hour round-table discussion with CTOs from Canada (<a href="https://truenorthcto.ca/">TrueNorthCTO</a>) on May 6th 2026. I gave the main talk and during certain parts we went into discussion mode. It was a lot of fun and afterwards there was plenty of discussion on their Slack channel, or so I heard, I'm not on there as I'm not a Canadian CTO ;)</em></p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/f5df8899-slide-01.png" alt="" loading="lazy"></p>
<p>Welcome, everyone. Today, we’re going to talk about &quot;dark code,&quot; the value of system visibility, and what a classic real-time strategy game can teach us about managing modern software organizations. We’re living in one of the wildest eras of technology shifting under our feet, and as technology leaders, we need a new mental model to survive it.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/a581e2f7-slide-02.png" alt="" loading="lazy"></p>
<p>To give you a bit of context, I'm 38 years old and have spent my career navigating different software paradigms. I spent seven years at Mendix, a low-code platform pioneer, which started in the Netherlands and then moved it's HQ to Boston. After that, I became the CTO of an online eye-testing medical device company in Amsterdam, where I learned a massive amount about regulations, building teams, and writing patents. I then spent three years working as a fractional CTO across 8 different companies, and today, I am the co-founder and CTO of Comper, which, if you look at my career timeline (7, 5, 3 years), will likely only be around for 1 year ;)</p>
<p>Jokes aside, we’re building a company to solve the very visibility issues I faced at every single company I was at before. At Comper, we make maps for software systems. It’s a simple philosophy: if you are lost, the very first thing you want to get your hands on is a map so you know where you’re going. We are trying to bring that exact utility to software.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/e248598b-slide-03.png" alt="" loading="lazy"></p>
<p>We started the company in October, and things are moving incredibly fast. We are currently a team of seven full-time people, plus a few part-time contributors.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/91e0a818-slide-04.png" alt="" loading="lazy"></p>
<p>Our product provides a collaborative digital canvas that ingests your Git data to visually surface various aspects of your code landscape. You can zoom out to see the macro architecture or zoom all the way down to individual code details, allowing both human teams and AI agents to explore and explain code systems together.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/2e152e1e-slide-05.png" alt="" loading="lazy"></p>
<p>Before diving into the core analogy, I want to pose a couple of questions. These aren't for immediate answers, but we'll come back to them later:</p>
<ul>
<li><strong>What organizational issues have you seen stem from people simply not understanding how the technology in their own company works?</strong></li>
<li><strong>How are you dealing with the enormous, unprecedented volume of code being created today via AI?</strong></li>
<li><strong>What do you wish you had deep insights into that currently stands as a blind spot for you; and what do you wish <em>other</em> business leaders understood about your stack?</strong></li>
</ul>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/8515234a-slide-06.png" alt="" loading="lazy"></p>
<p>If you’ve ever played <em>Age of Empires</em>, you know it's a game entirely driven by a mechanic called the <strong>Fog of War</strong>.</p>
<p>When the game starts, the entire map is pitch black, it is completely unknown. When you send a scout out on a horse, you can suddenly see exactly what that unit sees in real-time. But as soon as your scout moves away, that territory relapses into a state of gray fog. You can still see the permanent outline of the terrain and buildings, but you have no idea if enemy units are moving through it. To maintain visibility, you have to strategically build watchtowers and position outposts to ensure the enemy cannot maneuver in the dark.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/595dba96-slide-07.png" alt="" loading="lazy"></p>
<p>The absolute best <em>Age of Empires</em> players master this mechanic to dominate the game. They possess distinct traits:</p>
<ul>
<li><strong>Extreme Velocity:</strong> They move so fast that they hit you before you even realize they're coming.</li>
<li><strong>Proactive Control:</strong> They dictate the pace of the game and take the fight to you; they are virtually impossible to surprise because they see threats miles away.</li>
<li><strong>Resource Efficiency:</strong> If you look at their resource graphs at the end of a match, they don't sit on vast, unspent hoards of gold or wood. They gather exactly what they need and build an army that is <em>just good enough</em> to win.</li>
</ul>
<p>As CTOs and VPs of Engineering, our CEOs always demand that we build things good, fast, and cheap. We usually tell them to pick two. Yet, top strategy players manage to achieve all three simultaneously.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/bf7160a6-slide-08.png" alt="" loading="lazy"></p>
<p>Let’s look at three perspectives on how we can apply these lessons to our software organizations.</p>
<p>In the grand scheme, the game map is the broader market, and you are the player. Your army is your engineering team, your resources are capital (raised via investments or generated through sales), and your tech tree is your software stack.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/e0fb2004-slide-09.png" alt="" loading="lazy"></p>
<p>To win against the world, the best companies optimize their internal feedback loops. They eliminate friction ruthlessly, ensuring pull requests are merged rapidly and customer feedback is captured instantly. In game terms, they place their villagers directly next to the gold mine to accelerate the economic engine.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/00ae1301-slide-10.png" alt="" loading="lazy"></p>
<p>They stay in control by investing heavily in research and scouting. They don't just build blindly; they set up watchtowers to evaluate shifting technology. This is incredibly critical right now, as the tech industry has never been more in flux due to AI. Furthermore, they maintain strategic reserves of time and money to absorb sudden market disturbances. Finally, they build cheaply by validating and selling a concept before over-engineering a massive full-feature product when a simpler solution would have sufficed.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/9309db7b-slide-11.png" alt="" loading="lazy"></p>
<p>Interestingly, the top companies also use the Fog of War to their advantage outside their walls. Keeping your internal roadmaps and strategic pivots hidden from competitors and vendors provides immense bargaining power.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/e670576d-slide-12.png" alt="" loading="lazy"></p>
<p>While the Fog of War is a useful defensive tool against the outside world, it is an absolute disaster when it exists inside your own company.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/e3aa0bc5-slide-13.png" alt="" loading="lazy"></p>
<p>Whenever I’ve stepped into a new company as a fractional CTO, I've felt like I was navigating in the dark with nothing but a tiny torch. You can see a small sliver of code here, a tiny team interaction there, and you slowly try to construct a holistic mental model.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/564cab22-slide-14.png" alt="" loading="lazy"></p>
<p>In a massive, complex enterprise, it takes an incredibly long time to piece that puzzle together.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/d2b53c21-slide-15.png" alt="" loading="lazy"></p>
<p>The real danger occurs when different leaders operate on completely conflicting mental models of the same technology stack. I've witnessed immense organizational drama because of this misalignment:</p>
<ul>
<li><strong>The Oblivious CEO:</strong> I once worked with a CEO who spent five years funding a single-tenant, gig-economy application. He suddenly decided he wanted to instantly pivot it into a multi-tenant, white-labeled platform, genuinely believing it would be an effortless switch. He had zero grasp of the profound architectural implications.</li>
<li><strong>The Blind CPO:</strong> I saw a Chief Product Officer make a disastrous decision to merge two entirely separate platforms. One stack was built on C# and Azure; the other was Java on AWS. He assumed that simply shifting engineers between teams would allow them to blend the platforms seamlessly. The engineering teams knew it would fail due to wildly disparate APIs, stacks, and cultures, but leadership pushed ahead blindly.</li>
<li><strong>The Reckless VP:</strong> I watched an incoming VP of Engineering immediately lay off a handful of engineers solely to make a visible corporate impact, entirely unaware that he had just fired the explicit linchpins holding the core infrastructure together.</li>
</ul>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/8154cd74-slide-16.png" alt="" loading="lazy"></p>
<p><em>We now went into a roundtable discussion on this topic, an audience member noted that a massive portion of a technology leader's role is balancing tech clarity for the C-suite without causing their eyes to glaze over. Another participant pointed out that architectural analogies are our best defense. Explaining that you wouldn't try to build ten concrete floors on top of a single-family residential house because it would obviously implode.</em></p>
<p><em>Another leader shared the acute friction of running a nimble, greenfield AI startup division inside a larger scale-up. Business executives frequently point to tiny, three-person external competitors and demand to know why an enterprise engineering organization supporting a ten-year-old codebase can't match that exact turnaround speed.</em></p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/42cc2f9c-slide-17.png" alt="" loading="lazy"></p>
<p>So my conclusion is that this is how the best technology organizations work. They have systems for onboarding and documentation, and measure and maintain it very well.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/c52715d8-slide-18.png" alt="" loading="lazy"></p>
<p>The third perspective centers on how we manage AI agents.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/aae09cca-slide-19.png" alt="" loading="lazy"></p>
<p>A couple of months ago, this blogpost did the rounds. The &quot;five&quot; or actually six levels of agentic engineering. It takes inspiration from the car industry where there are also distinct levels of autonomy:</p>
<ul>
<li><strong>Level 1: Spicy Autocomplete</strong> — The initial wave of IDE code completion assistants we grew used to a few years ago.</li>
<li><strong>Level 2: The Coding Intern</strong> — Delegating minor isolated tasks, such as asking an AI to rewrite a specific function.</li>
<li><strong>Level 3: The Junior Developer</strong> — Handing off simple, end-to-end tasks and reviewing the output later.</li>
<li><strong>Level 4: The Developer</strong> — Treating the AI as a functional team member that autonomously investigates an issue and submits a pull request for human verification.</li>
<li><strong>Level 5: The Agentic Team</strong> — Deploying multiple sub-agents that collaborate uninterrupted for 12 hours or more on complex objectives, updating documentation, and fixing code across boundaries.</li>
<li><strong>Level 6: The Dark Software Factory</strong> — An operating model where code is autonomously generated, compiled, and deployed without a human ever reading or reviewing it.</li>
</ul>
<p>The clear litmus test for moving into the Dark Software Factory is token expenditure; companies operating at this level burn $1,000 a day in LLM tokens per engineer. Very few companies are there, I am certainly not.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/602b285d-slide-20.png" alt="" loading="lazy"></p>
<p>This brings us to the core definition of <strong>Dark Code</strong>: <em>software that no human has ever written, read, or reviewed.</em> While this is accelerating rapidly due to AI, dark code also exists in legacy systems. Many enterprises rely on ancient mainframe software where the original human authors left the company decades ago, leaving a grey box that nobody truly understands.</p>
<p>The terrifying reality of agentic velocity is that our human management tools have failed to keep pace.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/5a32febe-slide-21.png" alt="" loading="lazy"></p>
<p>Consider this real-world example from our own team: our designer used an AI agent to adjust frontend layouts and test the visual output directly on screen. Once satisfied, the agent generated a single pull request spanning <strong>211 files and over 7,000 lines of code</strong>.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/1071f9c0-slide-22.png" alt="" loading="lazy"></p>
<p>When Git platforms present this change as a standard, flat wall of code text, a comprehensive human line-by-line review becomes literally impossible. In practice, engineers look at the green checkboxes, see that the automated tests passed, verify the UI looks fine, and hit merge. We are losing granular visibility of what is happening under the hood. For a hardcore software engineer who prides themselves on elegant architecture, this reality is deeply uncomfortable, even if the resulting development speed provides an undeniable dopamine hit.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/f8487860-slide-23.png" alt="" loading="lazy"></p>
<p>So again, in the agentic world, we can learn a couple of things from AoE. Make sure your codebases are top-notch: CI/CD, linters, quick reviews. Keep docs up to-date automatically. Explore new solutions. Make sure tech debt is tracked, and keep your context windows up to date.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/5f186545-slide-24.png" alt="" loading="lazy"></p>
<p><em>During our discussion, an audience member posed a fundamental question: &quot;If AI is generating and parsing code at this velocity, is traditional, human-consumable documentation even relevant anymore?&quot; One participant revealed that their organization completely abandoned writing standard internal readmes for humans. Instead, they write &quot;meta-documentation&quot; specifically formatted for AI consumption; explicitly mapping out service boundaries, architectural rules, and information flows so agents don't waste millions of tokens rediscovering the environment. To combat the sloppy code duplication that agents naturally introduce when working in isolation, they run nightly cleanup agents to ruthlessly refactor and prune the codebase.</em></p>
<p><em>However, as another roundtable member argued, &quot;epistemic debt&quot;—the risk of completely losing the human understanding of how a system functions, is incredibly dangerous. We have already seen major cloud outages extend for hours simply because the on-call human engineers didn't understand the underlying automation well enough to fix it when it broke. Senior developers must maintain high-level conceptual maps of the stack, even if they cede line-by-line execution to the machines.</em></p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/9827d4fc-slide-26.png" alt="" loading="lazy"></p>
<p>We have arrived at a fascinating, paradoxical crossroads: <strong>as a society, we have never relied more on software, yet we have also never understood it less.</strong> To understand this transition, we can look back at early 20th-century manufacturing.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/be21a572-slide-27.png" alt="" loading="lazy"></p>
<p>When electrical power first became available to industrial factories, managers didn't immediately change how they built plants. For the first 20 to 30 years, factories kept the exact layout of the old steam-engine era. They maintained a single, massive central drive shaft running through the ceiling of the factory, powered by a massive central motor, and mechanically hooked individual electric tools to that turning shaft.</p>
<p>It took decades for industrialists to realize the true paradigm shift of electricity: you could entirely eliminate the complex central shaft, run flexible wires through the grid, and place a tiny, independent electromotor inside every single machine.</p>
<p>Right now, we are in that exact same awkward middle phase with AI engineering. We are using incredibly advanced agentic capabilities, but we are still forcing them to work within the legacy &quot;central drive shaft&quot; paradigms of flat file repositories and line-by-line text pull requests. We desperately need entirely new visual and architectural tools to manage this paradigm. Fortunately, unlike <em>Age of Empires</em>, the rules of the real world aren't fixed. We have the agency to invent the new tools ourselves.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/514b81f9-slide-28.png" alt="" loading="lazy"></p>
<p>This brings us back to the map. To navigate this sea of dark code, tech leaders need an altitude-adjusted view of their infrastructure.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/c64dbea3-slide-29.png" alt="" loading="lazy"></p>
<p>When Comper connects to your GitHub or GitLab accounts, it parses the entire system into a live, multiplayer canvas. Instead of cloning fifty separate repositories and running throwaway terminal commands to understand an ecosystem, teams can look at a unified landscape.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/208eedf3-slide-30.png" alt="" loading="lazy"></p>
<p>We allow you to toggle various data overlays instantly:</p>
<ul>
<li><strong>Code Complexity &amp; Health:</strong> Highlighting highly complex or problematic areas in red so you can visualize structural risk</li>
</ul>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/6d65c499-slide-31.png" alt="" loading="lazy"></p>
<ul>
<li>
<p><strong>The Bus Factor:</strong> Color-coding the canvas based on authorship so you can instantly see knowledge silos. For example, you can filter by a specific engineer to see exactly which components they've touched over the past year.</p>
</li>
<li>
<p><strong>Code Velocity &amp; Age:</strong> Shading components based on how recently they were modified, providing an instant heatmap of where active engineering hours are being spent.</p>
</li>
</ul>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/c11c2ca9-slide-32.png" alt="" loading="lazy"></p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/21ddcdf9-slide-33.png" alt="" loading="lazy"></p>
<p>We also auto generate documentation and keep it up to date when the codebase changes.</p>
<p>This was the end of the talk, we then had another 20 minutes or so of discussion.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/b435958e-slide-34.png" alt="" loading="lazy"></p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/e9fb62b5-slide-35.png" alt="" loading="lazy"></p>
<p>When an audience member asked how to translate these visual &quot;red zones&quot; into language a CEO actually cares about, the answer came down to product roadmapping. If we map explicit software features directly onto this canvas as first-class citizens, a CTO can tangibly show business leadership: <em>&quot;You want to build feature X. To do that, we have to cut directly through this highly complex, technical-debt-heavy red zone. This is exactly why it will take 20% longer and cost more money.&quot;</em> Visualizing true cross-application dependencies gives engineering leaders the exact empirical backing they need to justify architectural refactoring to boards and executives.</p>
<p>When you begin measuring lines of code or raw engineering hours directly, the metrics inevitably break down under Goodhart's Law, especially when AI can generate thousands of lines in seconds. But by tracking feature cycle times across a visual system map, we can accurately measure true organizational efficiency.</p>
<p>To leave you with a final bit of irony: when we originally started building Comper, we built a mini-game inside it inspired by <em>Geoguessr</em>. The tool would show an engineer a random snippet of code from their company's repository, and the engineer had to guess where on the corporate software map it belonged. It was an amazing, fun way to test codebase literacy.</p>
<p>But as AI adoption skyrockets and engineering teams produce vast oceans of code they've never personally read, we've noticed something funny: the game has become completely impossible to play. Nobody knows the literal text anymore. The era of dark code is officially here, and it’s time we built the maps to navigate it.</p>
<p><img src="https://blog.waleson.com/images/2026/05/navigating-fog-of-war-age-of-empires-ai/961a291d-slide-36.png" alt="" loading="lazy"></p>
<p>Thank you very much for your time.</p>
]]></content:encoded>
            <category>ai</category>
            <category>dark-code</category>
            <category>talks</category>
            <category>leadership</category>
        </item>
        <item>
            <title><![CDATA[Three Thoughts on Dark Code]]></title>
            <link>https://blog.waleson.com/2026/03/three-thoughts-on-dark-code/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2026/03/three-thoughts-on-dark-code/</guid>
            <pubDate>Thu, 19 Mar 2026 17:21:00 GMT</pubDate>
            <description><![CDATA[A couple of weeks ago Dan Shapiro's blog post " The 5 Levels" (about Agentic Engineering) did the rounds. He coined Level 5 the "Dark Software Factory". So what comes out of those factories? I'm calling it Dark Code…]]></description>
            <content:encoded><![CDATA[<p>A couple of weeks ago Dan Shapiro's blog post &quot;<a href="https://www.danshapiro.com/blog/2026/01/the-five-levels-from-spicy-autocomplete-to-the-software-factory/">The 5 Levels&quot;</a> (about Agentic Engineering) did the rounds. He coined Level 5 the &quot;Dark Software Factory&quot;. So what comes out of those factories? I'm calling it Dark Code.</p>
<blockquote>
<p><strong>Dark Code</strong>: lines of software that no human has written, read or even reviewed.</p>
</blockquote>
<p>Two weeks ago I presented a talk &quot;<a href="https://blog.waleson.com/2026/03/mapping-out-dark-code/">Mapping out Dark Code</a>&quot; at the OpenAI Codex meetup in Amsterdam, and now people start conversations with me and mention Dark Code. So before this goes out of control, let me write some thoughts.</p>
<p>Tldr:</p>
<ol>
<li>Will my codebase become an unholy mess if no one looks at the code?: Yes, this is an unsolved problem.</li>
<li>Will there be new programming languages if humans don't care about code anymore?: Doubtful, and that's a shame!</li>
<li>Dark Code is already here: In enterprises no one knows the code anyways.</li>
</ol>
<h3>1 - Will my codebase become an unholy mess if no one looks at the code?</h3>
<p>Agents take prompts and implement this in your codebase. I see parallels with evolution chipping away at our DNA ACGT base pairs. Evolution is just trying something randomly until it works. Likewise, in software, an agent will just find the easiest way to achieve its goal.</p>
<p>If the system is big enough, a random request in a prompt will not trigger a full refactoring or a new architecture. I feel architecture will get stuck in local maximums quite quickly.</p>
<p>Yes, without new tooling for architecture, backpressure on the agents when complexity goes too high, your codebase will become an unholy mess. However, there is a lot of development in this field now, so some things will emerge.</p>
<h3>2 - Will there be new programming languages if humans don't care about code anymore?</h3>
<p>The SWE Bench performance on new or unknown languages by LLMs is abysmal. I saw an article on this recently but can't find it anymore. There's no or little training data on these new languages, so the LLMs have to use their reasoning and can't rely on &quot;muscle memory&quot;, to put it in layman's terms.</p>
<p>What does this mean for programming language research?</p>
<p>A) All coding agents are generating ever more code in current, general purpose, languages, and it's a bit of a vicious cycle. The more code is generated, the more models will be trained on that. It's hard to get good model performance on a new language, and thus it's harder to generate enough code to be trained on.</p>
<p>B) Previously humans cared about their code. When they saw a way to create a better language, some people were crazy enough to create a new one! Because they cared so much about the software systems they built. No wonder, they were living in their codebases all day. Now that we let most code be generated by our agents, do we still care enough to create a new language? I don't think so.</p>
<p>But, as I've written in <a href="https://blog.waleson.com/2024/09/the-future-of-programming-systems-four/">the future of programming systems</a> I believe there's a LOT of room left for better programming languages. I believe the Low-Code movement was partially about this. Standardizing what we were trying to build.</p>
<p>A language / linter / syntax should know exactly where your application talks to the outside world. Where there is Personally Identifiable Information flowing through your systems. Instead, all major current languages are general purpose languages, and unconstrained. Unconstrained sounds good, but actually, constraints allow you to speed up tremendously.</p>
<p>Will we ever get nice things, or are we casting the current status quo in stone. Will Rust be the best language forever?</p>
<h3>3 - Dark Code is already here</h3>
<p>Dark Code is not that binary. Code I've written 1 year ago is already a lot darker than the code I'm writing today. Enterprises are full of code written by teams that are no longer there. That code is pretty dark.</p>
<p>My startup is trying to shed a light on large software systems that are too complex to understand, unless you take a birds eye view. I'm a bit scared that it might not be relevant in the world of Dark Code, because people don't seem to care about code anymore. On the other hand, there is now more code than ever, so I'm betting that people will want to work on this higher level. Maybe we're building the IDE of the future?</p>
<p>Thanks for reading. If you liked this post, you might also enjoy my thoughts on the <a href="https://blog.waleson.com/2024/09/the-future-of-programming-systems-four/">future of programming systems</a>.</p>
<p>Discuss on <a href="https://news.ycombinator.com/item?id=47442799">HN</a>, comments on the blog are mostly spam.</p>
]]></content:encoded>
            <category>ai</category>
            <category>dark-code</category>
            <category>future-of-programming</category>
        </item>
        <item>
            <title><![CDATA[Mapping out Dark Code]]></title>
            <link>https://blog.waleson.com/2026/03/mapping-out-dark-code/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2026/03/mapping-out-dark-code/</guid>
            <pubDate>Thu, 12 Mar 2026 10:53:00 GMT</pubDate>
            <description><![CDATA[This is a 30 minute talk I gave at the very first OpenAI Codex Meetup in Amsterdam on March 5th 2026. I introduced the concept of Dark Code . It was a lot of fun and the audience seemed to like it. My CMO gave me a 7-8…]]></description>
            <content:encoded><![CDATA[<p>This is a 30 minute talk I gave at the very first OpenAI Codex Meetup in Amsterdam on March 5th 2026. I introduced the concept of <strong>Dark Code</strong>.</p>
<p>It was a lot of fun and the audience seemed to like it. My CMO gave me a 7-8 out of 10.</p>
<p>This is not an exact representation of the talk, I improvised, went off-script and forgot things. Also the organizers told me to say the f-word less (4 is bad, 1 would be OK), so in this transcript I've redacted it. At least if you say the f-word, you're quite sure it's not AI-slop ;)</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/1e446fde-screenshot-from-2026-03-19-11-55-32.png" alt="" loading="lazy"></p>
<p>Hi, I'm Jouke and today I'm going to talk to you about Mapping out Dark Code. This is the very first Codex meetup here in Amsterdam, this is the very first presentation of the evening and it's also a first time we're showing Comper to a real-world audience. Lots of firsts. We're happy to be here!</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/1f90f03d-screenshot-from-2026-03-19-11-55-43.png" alt="" loading="lazy"></p>
<p>[the sentences appeared one by one, I do that all the time to make sure people follow my train of thought, and don't try to read everything at once]</p>
<p>So a bit about me, not to brag, but so you have something to related to. &quot;Affordances&quot; is what they call this in design language.</p>
<p>I'm now 38 years old. [Someone in the audience: I can relate to that]</p>
<p>I started at Mendix as an engineer, then became CTO here in Amsterdam for a tiny startup in online eye testing. Then I left to become a Fractional CTO for 3 years. Now I'm co-founder of Comper. If you follow the sequence, I'll be doing this for just one year ;)</p>
<p>Today I want to talk about how I think the world will change when intelligence is for rent. Then talk about Dark Code, a new concept that I came up with. It logically follows from the Dark Software Factory, so it's not super inventive, but I do like it a lot. Then the assignment for today was to show you what we built with Codex, so I'll give a quick demo of our product.</p>
<p>Then the last thing why I'm here today is that I'm actually scared. The future is more unpredictable than ever. I've just started a software company. I'm very good at software, a skill that is now being commoditized. Most of my friends are in software or other white collar professions.</p>
<iframe loading="lazy" allowfullscreen="" src="https://www.blogger.com/video.g?token=AD6v5dxcM78WklZITymUgk5BZgws8nAgivoibeSXZgMBQPG3XrUPHDlxGrB0x9003ne6CaQGOhbfBxKRK5aRlM7ZJg"></iframe>
<p>Let me give you a mini 10 second demo of my product, we're mapping all software in your team and allow you to zoom in, and have smart overlays etc. It helps you to rapidly understand a complex software landscape.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/91c51d0f-screenshot-from-2026-03-19-11-56-04.png" alt="" loading="lazy"></p>
<p>We started in October and now have 7 people. Did a pre-seed in January and are hiring. And it's very cool, but it's also Scary to be responsible for a software company in these times.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/26595b25-screenshot-from-2026-03-19-12-02-00.png" alt="" loading="lazy"></p>
<p>[dramatic pause with an empty slide for effect]</p>
<p>So why am I scared? Well, let me tell you a bit of a story about something that also changed the world.</p>
<p><a href="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/798e75c3-screenshot-from-2026-03-19-12-02-14.png"></a></p>
<p><a href="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/798e75c3-screenshot-from-2026-03-19-12-02-14.png"></a><a href="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/26595b25-screenshot-from-2026-03-19-12-02-00.png"></a></p>
<p><a href="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/26595b25-screenshot-from-2026-03-19-12-02-00.png"></a><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/54940a24-screenshot-from-2026-03-19-12-02-08.png" alt="" loading="lazy"></p>
<p>Back in the day, human muscles were worth a lot. These farmers harvesting the crops did useful work. Hard work, but if you look at the guy on the right it's clearly work hard / play hard.</p>
<p>Humans were the only things that could harvest food or move things around.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/798e75c3-screenshot-from-2026-03-19-12-02-14.png" alt="" loading="lazy"></p>
<p>Then later, we got horses, oxen and mules for a lot of muscle work. They used horses to move things around because they were better than humans. This is the next level of muscle power, and the human is now the driver.  I now want to talk about this guy.</p>
<p>Imagine that you're this person. He's clearly very good at his job. The coat even matches the carriage!</p>
<p>This, dear audience, is someone who knows their shit. They know how hard the horses can be driven, knows when to grease the wheels, which roads to avoid because of traffic, all of that. There were 100's of thousands of these people in the 18th century.</p>
<p>And you know what's funny. I couldn't even tell you now what this profession is called. A horse rider? A carriage driver? Whatever. It's obsolete. But people were so proud to be doing this job.</p>
<p>Now let's talk about the horse.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/c2a0f266-screenshot-from-2026-03-19-11-57-04.png" alt="" loading="lazy"></p>
<p>First a question. How many HP does one horse have?  [many in the audience: one!]</p>
<p><a href="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/c2a0f266-screenshot-from-2026-03-19-11-57-04.png"></a><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/f6d7520d-screenshot-from-2026-03-19-11-56-56.png" alt="" loading="lazy"></p>
<p>One right?</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/e4e0f738-screenshot-from-2026-03-19-11-57-11.png" alt="" loading="lazy"></p>
<p>Well actually, no. It's 14.9. [confusion all around]</p>
<p>You see, a typical horse can output 14.9 horse power at max intensity. HOWEVER, over the average of a day, you can get one horse power from one horse. So if you said one, you are still right.</p>
<p>It's actually a genius term, horse power. When James Watt invented the steam engine, he needed to sell a bulky, ugly, expensive machine to people. How did he do that? He said: this machine replaces X horses. People loved replacing horses because they needed care, food and pooped everywhere.</p>
<p>Think about that. Watt invented a new unit of measurement, not to deeper understand physics, but to sell his product. Literally &quot;this replaces N horses&quot;. Genius.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/fcd43e6e-screenshot-from-2026-03-19-11-57-17.png" alt="" loading="lazy"></p>
<p>In our history of muscles, we started with humans, then horses (etc) and only much later, together with the steam engine we got Horse Power.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/414a9bca-screenshot-from-2026-03-19-11-57-25.png" alt="" loading="lazy"></p>
<p>Ok first of all, this is not pee. It's gasoline. We're now years further, and this is the muscle in the ultimate condensed form. Gasoline. It replaced all the horses. It's energy productized to the max.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/02d4bdfd-screenshot-from-2026-03-19-11-57-32.png" alt="" loading="lazy"></p>
<p>But if you look behind the scenes, this product is part of HUGE global infrastructure that is needed to create that simple product, like this dirty refinery.</p>
<p>Humans were proud of our muscles, and industry took it away.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/cef31ac7-screenshot-from-2026-03-19-11-57-56.png" alt="" loading="lazy"></p>
<p>We were also very proud of our intelligence. I was very proud to get a very very high report card at the end of primary school, but my parents didn't care too much. Child trauma. Anyway.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/4ef1517f-screenshot-from-2026-03-19-11-58-03.png" alt="" loading="lazy"></p>
<p>Intelligence used to be, until VERY recently, exclusively human.</p>
<p>And yes I know, LLMs are not truly intelligent.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/c273ab42-screenshot-from-2026-03-19-11-58-09.png" alt="" loading="lazy"></p>
<p>Purists will say, it's not AGI and definitely not ASI etc.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/ee6dc8c3-screenshot-from-2026-03-19-11-58-25.png" alt="" loading="lazy"></p>
<p>But, to me this is a bit like saying this back in 2007 when the iPhone came about. &quot;It can do video calling&quot;. &quot;Ok but if you look at inter-human communication this is actually only the first step, we'll get VR and brain-to-brain in the future&quot;. Sure, you may be right, but right now I have this smartphone, it's awesome and I just want to build cool things with it!</p>
<p>So while we don't have AGI, it's clear that LLMs are some type of intelligent, it's just a bit unclear how much.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/70f96b15-screenshot-from-2026-03-19-11-58-33.png" alt="" loading="lazy"></p>
<p>I truly believe that we now have intelligence, priced per token. But how much? We don't have a unit for it yet. We have IQ, and SWE Bench, but how much brain time or humans can it replace?</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/644eb346-screenshot-from-2026-03-19-11-58-39.png" alt="" loading="lazy"></p>
<p>However, I am starting to get a gut feeling for it. For some tasks in my codebase I'm like &quot;Composer can handle this fine, it's super fast, let's go&quot;. And for others I'm like: let's see if Opus 4.6 can figure it out.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/60fe386d-screenshot-from-2026-03-19-11-58-46.png" alt="" loading="lazy"></p>
<p>So what is happening in the world right now is that we're taking something that we took something that was exclusively human, into a product. (Again, not pee.)</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/73b6a02e-screenshot-from-2026-03-19-11-58-51.png" alt="" loading="lazy"></p>
<p>And let's not forget, ultimately the companies that build these gigantic data centers are just trying to push more of that product to us. Interesting side note: I'm curious if we are currently demand or supply-constrained.</p>
<p>So let's see where that brings us.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/74a2ae3e-screenshot-from-2026-03-19-11-59-02.png" alt="" loading="lazy"></p>
<p>Have you seen this graph? It was popular on Hacker News a couple of months ago. There were also about 25 million horses in the USA before the car came along. If you haven't read this blog post &quot;Horses&quot; by Andy Jones, look it up, it's really great. It's about when something is good enough to replace something else. Spoiler alert: the horses lost to the car and got slaughtered.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/8445be53-screenshot-from-2026-03-19-11-58-57.png" alt="" loading="lazy"></p>
<p>I started with &quot;I'm scared&quot;. And just like horses, there are about 40 million software developers in the world right now. Some say 20 million full time professional, and a lot do it part-time or for hobbies so 40 million seems right. So where will this &quot;let's replace human intelligence with LLMs&quot; take us? I don't think we'll get slaughtered, but something has to change.</p>
<p>Melanie, our CMO just deployed our new website. Never touched code before. I gave her Cursor and she just did it with minimal help. Is everyone now becoming a developer?</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/1f7ef495-screenshot-from-2026-03-19-11-59-12.png" alt="" loading="lazy"></p>
<p>So another post that was popular recently was this one. About five levels of Agentic development. He says 5 levels, but whether you start at 0 or 1, the count is still 6 so that is weird. It goes from Spicy Autocomplete to Junior Developer, to Developer, to Engineering Team to Dark Software Factory.</p>
<p>Just a show of hands, where are you at professionally?</p>
<p>[most said 3, some said 4, other said 5. I think I should have added &quot;At 5 you are spending 1k on tokens per day per engineer&quot;.]</p>
<p>So Dark Software Factories produce something. I'm calling it Dark Code.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/b19e22fc-screenshot-from-2026-03-19-11-59-20.png" alt="" loading="lazy"></p>
<p>The definition is that it's code that no human has written, read or reviewed. So it's dark like, there are no lights.</p>
<p>Now I have thoughts and feelings about this. I think code is not the right abstraction. In Low-Code we put abstractions in place for forms, database entities etc. It was beautiful. Our IDE could reason and check high-level concepts. Agents nowadays just focus on adding code in languages that don't have these abstractions. Good luck on creating a new elegant language now. LLMs are not trained on it and people are not going to write it themselves anymore. In a way, we are cementing our current state of software language development.</p>
<p>Also, I'm often proud of my architecture, code hierarchy or highly performant code. I CARE about some of my code. And now it's just a prompt &quot;Make it faster&quot; and no one cares? My mastery feels hurt.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/f880616a-screenshot-from-2026-03-19-11-59-25.png" alt="" loading="lazy"></p>
<p>But the good news for me, is that there already is a lot of Dark Code out there in huge organizations. Sure, someone once wrote it, but that person has long since left. So now no one knows how this works.</p>
<p>In fact, that's how my startup Comper got off the ground.</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/18b9552d-screenshot-from-2026-03-19-11-59-32.png" alt="" loading="lazy"></p>
<p>Does anyone know this game? It's called GeoGuessr and we were playing it in my office a lot when I was CTO. [a lot of people said yes!]</p>
<p>You see a place in google streetview and you have to guess where you are, it's super fun.</p>
<p>Someone from marketing saw us playing this and said &quot;why don't you create that for code?&quot;. I thought it was brilliant. You see some code that you or someone in your team once wrote and you have to guess where it is. I started building the game, and figured out how to make a map out of all the software code in the company. Then I figured out it was useful for a lot of other things.</p>
<p>Now, one thing we had was the bus factor: an area is in bad shape if all the authors have left. Because then no one knows about the code anymore. Both the game and the bus factor don't really matter anymore in a world where everything is Dark Code....</p>
<p>That was my last point. Now I'll give you a quick demo, because that was the original assignment for tonight.</p>
<p>[ here I gave a 5 minute demo of Comper, including the game ]</p>
<p><img src="https://blog.waleson.com/images/2026/03/mapping-out-dark-code/ad9c4565-screenshot-from-2026-03-19-11-59-43.png" alt="" loading="lazy"></p>
<p>Alright, thanks a lot. Questions? [there were questions but I forgot the details].</p>
]]></content:encoded>
            <category>ai</category>
            <category>dark-code</category>
            <category>future-of-programming</category>
            <category>talks</category>
        </item>
        <item>
            <title><![CDATA[Hiring, from the Other Side]]></title>
            <link>https://blog.waleson.com/2025/10/hiring-from-other-side/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2025/10/hiring-from-other-side/</guid>
            <pubDate>Thu, 09 Oct 2025 21:37:00 GMT</pubDate>
            <description><![CDATA[Here's some insights about what the hiring process can look like from within a company. The title is a play on Adele's "Hello (from the other side)". Quick context: I’ve interviewed for about three jobs in my life and…]]></description>
            <content:encoded><![CDATA[<p><em>Here's some insights about what the hiring process can look like from within a company. The title is a play on Adele's &quot;Hello (from the other side)&quot;.</em></p>
<p>Quick context: I’ve interviewed for about three jobs in my life and interviewed somewhere between 300 and 500 candidates for teams I led. So yes, my view is from the interviewer’s chair. I’m in software, which has its own hype cycles, but most of this applies broadly.</p>
<p>What you’ll get here:</p>
<ul>
<li>
<p>A short glossary so we’re speaking the same language</p>
</li>
<li>
<p>Three real hiring scenarios I’ve run</p>
</li>
<li>
<p>What each one means for you</p>
</li>
<li>
<p>A practical checklist to be the candidate they remember</p>
</li>
</ul>
<h2>How companies talk about hiring (so you can decode it)</h2>
<p>This is how we talk about the hiring process, you'll might hear these terms but not all of them might mean something to you.</p>
<ul>
<li>
<p><strong>Hiring Manager</strong>: Owns the budget and the final decision. Might not run the process day-to-day.</p>
</li>
<li>
<p><strong>Internal Recruiter</strong>: Works at the company, screens and coordinates. Often your main contact. Can be a gatekeeper without deep technical knowledge.</p>
</li>
<li>
<p><strong>External Recruiter</strong>: Paid a percentage of your salary if you get hired. Useful network, misaligned incentives. Treat claims about budget with skepticism. Make sure you check if the recruiter is external or internal.</p>
</li>
<li>
<p><strong>Referral</strong>: Someone inside vouches for you. Often faster process, better odds, sometimes bonus for the referrer.</p>
</li>
<li>
<p><strong>Interview Committee</strong>: Everyone weighing in. A founder can overrule. Decisions after each round.</p>
</li>
<li>
<p><strong>The Team</strong>: The people you’ll actually work with. Weirdly, sometimes they have little say.</p>
</li>
<li>
<p><strong>Rounds</strong>: Each live step (screens, interviews, take-homes). Usually 2–4 rounds; sometimes more.</p>
</li>
<li>
<p><strong>Take-home assignment</strong>: Later-stage homework to test motivation and skill; gives you something concrete to discuss.</p>
</li>
<li>
<p><strong>The Market</strong>: If talent is plentiful, companies get picky (and add hoops). If scarce, they cut steps and sweeten offers.</p>
</li>
<li>
<p><strong>Pipeline</strong>: How many candidates are in play. A full pipeline means more competition and faster, colder decisions.</p>
</li>
<li>
<p><strong>ATS (Applicant Tracking System)</strong>: The tool tracking every note, score, and email. A one-line “reject” from a manager becomes a canned rejection to you.</p>
</li>
<li>
<p><strong>Evaluation</strong>: Scorecards (1–5 on skills, values, etc.). Imperfect, but used to create “objective” comparisons.</p>
</li>
</ul>
<p>Alright, now that you know the terminology, let's take a look at some examples.</p>
<h2>Scenario 1: The scale-up with money to hire and a high bar</h2>
<p><strong>Context</strong>: At Mendix circa 2015, we’d just raised a Series B. We needed cloud engineers. Money wasn’t the bottleneck; talent was. We used an internal recruiter to find candidates. The team only saw profiles after recruiter screening.</p>
<h4>Process we ran:</h4>
<ul>
<li>20–30 minute screen with recruiter or hiring manager: vibe check and basics. Not deep technical discussion.</li>
<li>60-minute interview with hiring manager + team member(s): skills and experience.</li>
<li>Optional take-home (~1 day) and a 90-minute review.</li>
<li>Decision rule: If anyone serious said “I have doubts,” we didn’t proceed. Feedback to candidates was often generic.</li>
</ul>
<h4>What this means for you:</h4>
<ul>
<li>
<p>Treat the first screen as a clarity and professionalism test. Keep complex answers simple. Don’t go too technical unless invited.</p>
</li>
<li>
<p>Internal recruiters can be gatekeepers. Make your resume skimmable: crisp summary, measurable outcomes, relevant tech. Layout matters more than it should.</p>
</li>
<li>
<p>A referral here is gold. It gets you past the first filter faster.</p>
</li>
<li>
<p>Ask politely about how they decide: “What are the top 3 things you’re evaluating for this role?” and “What would cause a no-go at the end?”</p>
</li>
<li>
<p>Take-homes are work. If you do one, over-communicate assumptions, document trade-offs, and propose next steps. That artifact can carry you.</p>
</li>
<li>
<p>Expect limited feedback. Ask once if you want concrete tips to improve.</p>
</li>
</ul>
<h2>Scenario 2: The startup drowning in applicants</h2>
<p><strong>Context</strong>: At easee, we needed a QA engineer. I was CTO. QA draws lots of entry-level applicants. We didn’t want to use external recruiters (expensive, sometimes game-y), so we posted the role and got ~200 CVs fast. I screened ~40 candidates, did ~10 one-hour interviews, and brought 3 to final.</p>
<p>What I learned: it’s a ton of work. Without a system, you forget people. Generic cover letters (now often AI-written) don’t help you stand out.</p>
<h4>What this means for you:</h4>
<ul>
<li>
<p>Assume you have 10–20 seconds to earn a deeper look. Lead your CV with a 2–3 line summary that matches the job. Include one punchy proof point.</p>
</li>
<li>
<p>Show effort early. Examples:</p>
</li>
<li>
<p>A 60–90 second Loom introducing yourself and one relevant project.</p>
</li>
<li>
<p>A short test plan or bug report you’ve written (for QA), or a concise repo/mini-case for other roles.</p>
</li>
<li>
<p>Three crisp bullets: why this team, why now, and what you’ll do in the first 30 days.</p>
</li>
<li>
<p>Don’t submit AI-flavored mush. If I can’t hear your voice, I assume I’ll have to manage you closely.</p>
</li>
<li>
<p>Make it easy to remember you. Use a distinctive, relevant example and repeat it: “I cut regression escapes by 40% by reworking the smoke suite—happy to walk you through how.”</p>
</li>
<li>
<p>It’s fair to ask about the pipeline: “How many candidates are in later rounds?” This helps you calibrate your effort and timeline.</p>
</li>
</ul>
<h2>Scenario 3: The tiny, chaotic company with a founder-driven process</h2>
<p><strong>Context</strong>: ~10-person company. The CEO sources on platforms like honeypot.io, runs loose interviews focused on vision, and sometimes hires on the spot. Why it works for them: they can place you at client companies if you have the right certs—team fit and long-term role clarity are secondary.</p>
<h4>What this means for you:</h4>
<ul>
<li>
<p>Expect inconsistency and gut calls. If you need structure, tread carefully.</p>
</li>
<li>
<p>Test for chaos with specific questions:</p>
</li>
<li>
<p>Who do I report to? How often do we meet?</p>
</li>
<li>
<p>What does success look like at 30/60/90 days?</p>
</li>
<li>
<p>What’s the plan if client work slows?</p>
</li>
<li>
<p>How many people have left in the last 12 months, and why?</p>
</li>
<li>
<p>Protect yourself: clarify pay, bench policy, and benefits in writing. Consider a short paid trial or contract-to-hire if the vibes are shaky.</p>
</li>
<li>
<p>If something feels off, it probably is. You’re not crazy.</p>
</li>
</ul>
<h2>What almost no one tells you</h2>
<ul>
<li>
<p>Incentives aren’t aligned. External recruiters get paid more when your salary is higher. Hiring managers want fairness across the team and budget sanity.</p>
</li>
<li>
<p>The fastest path is to no. Efficient funnels reject early and often to save time.</p>
</li>
<li>
<p>“We picked someone with a better fit” is usually true—and also a catch-all. It can mean timing, salary, team dynamics, internal politics, or a quiet “no hire” policy.</p>
</li>
<li>
<p>Feedback quality is low by design. Too much detail invites arguments and risk.</p>
</li>
<li>
<p>Not knowing what you want is risky for managers. If you’re still exploring, say so and show how this role fits a clear hypothesis.</p>
</li>
<li>
<p>Desperation level at the company changes everything. When the market favors companies, they add hoops. When it favors candidates, they skip them.</p>
</li>
<li>
<p>Hiring is expensive, and it takes months before you're truly productive. If there are concerns you might not stay long with the company, this is a red flag for many roles. In your resume, short stints are a bad sign, so if there are good reasons, explain them. If there are promotions, they are a very good sign, so clarify those too.</p>
</li>
</ul>
<h2>Questions that change the interview</h2>
<p>Try using a few of these in your next call:</p>
<ul>
<li>
<p>What’s something in your code/process/product you’re a bit embarrassed about?</p>
</li>
<li>
<p>What are you most proud of in the company?</p>
</li>
<li>
<p>What would make you say “no” to me after this call?</p>
</li>
</ul>
<h2>How to be the candidate they remember</h2>
<ul>
<li>
<p>Lead with a story: why this company, why this role, why now. Make it feel inevitable.</p>
</li>
<li>
<p>Make the recruiter’s life easy. Send a 3–4 sentence summary they can paste into the ATS note: who you are, why you fit, one concrete result, link to proof. Gatekeepers remember people who cut their admin time.</p>
</li>
<li>
<p>Be a pleasant calendar event. On time, tech checked, concise answers (60–90 seconds), finish with “does that answer your question or should I go deeper?”</p>
</li>
<li>
<p>Calibrate confidence. Clear, calm, and prepared beats hype.</p>
</li>
<li>
<p>Confidence without bragging is contagious. It’s very easy to hire a person who believes in what they can do and really looks forward to working in this team. It feels like you won’t have to hand-hold this person while they do their jobs, which is always nice for a manager.</p>
</li>
<li>
<p>Ask real questions. Show you’re also evaluating them. People advocate for candidates who have their own standards.</p>
</li>
<li>
<p>Following up after the interview with a short message shows that you really want this position. Make it positive and not desperate.</p>
</li>
</ul>
<h2>Reality check on rejection</h2>
<ul>
<li>
<p>Rejections are often about team dynamics, timing, or budgets you can’t see. Don’t overfit your entire approach to one no.</p>
</li>
<li>
<p>Ask for feedback once. If you get a generic response, move on.</p>
</li>
<li>
<p>Keep your own pipeline full. Options create calm.</p>
</li>
</ul>
<h4>If you made it this far</h4>
<p>Thanks again for listening to the interview and reading this “other side” view. And if you’re mid-process somewhere, remember to make it easy for the right people to say yes.</p>
]]></content:encoded>
            <category>leadership</category>
            <category>teams</category>
        </item>
        <item>
            <title><![CDATA[The time I literally dropped a server]]></title>
            <link>https://blog.waleson.com/2025/08/the-time-i-literally-dropped-server/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2025/08/the-time-i-literally-dropped-server/</guid>
            <pubDate>Sun, 17 Aug 2025 21:23:00 GMT</pubDate>
            <description><![CDATA[This is a short story about one of the stupidest things I ever did in my professional life, namely, dropping a physical server from the top of the rack in a data center. Spring is my favorite season, summer is actually…]]></description>
            <content:encoded><![CDATA[<p><em>This is a short story about one of the stupidest things I ever did in my professional life, namely, dropping a physical server from the top of the rack in a data center.</em></p>
<p>Spring is my favorite season, summer is actually better, but in spring it's clear that winter is over and summer is coming up. In early 2014 I was driving from the Mendix office in Rotterdam to our co-location data center (DC for short) in Amsterdam. I felt fantastic, and for a couple of reasons: I was driving my new car, I was paid to do an important job with a $10k 1U server in the boot, and as the cherry on top, it was finally <em>spring</em> and the weather was great! There have not many times in my life I felt this wonderful. Little did I know that I was now at the top of a rollercoaster, and on the way back I would feel very different.</p>
<p>All of the physical servers at Mendix had Pokemon names. The one in my car was called Flareon. It had broken down a couple of weeks before and had gotten fixed by Hans, our lead systems engineer. We now had to put it back in the rack with its fellow Pokemon. Even though I was the team's product manager and on paper, driving to the data center and replacing a server was not really my job, I had gotten my DC access badge a couple of months before and I was happy to do it. This was my first solo mission.</p>
<figure><img src="https://blog.waleson.com/images/2025/08/the-time-i-literally-dropped-server/0d5b4104-image.jpg" alt="Flareon is the second server from the top. Note that left metal extrusion with the Intel logo is misaligned with the rest of the servers. After reading the rest you'll know why." loading="lazy"><figcaption>Flareon is the second server from the top. Note that left metal extrusion with the Intel logo is misaligned with the rest of the servers. After reading the rest you'll know why.</figcaption></figure>
<p>When I got through the security gate, the floor looked deserted. I drove the cart to Flareon's aisle and stopped at the rack. As soon as I opened it, I noticed something. The empty slot where the server was supposed to be, was placed quite high up. I could only just reach it if I stood on my toes.</p>
<p>The rack looked roughly like this (sorry for the rough sketch, it's from memory).</p>
<p><code>  | Switch 1   |   |  Server 1  |   |  Server 2  |   |  Server 3  |   |  Server 4  |   |  Server 5  |   |  Server 6  |   | Switch 2   |   |  Server 7  |   |  Server 8  |   |  Server 9  |   |  Server 10  |   |  Server 11 |   |  Server 12 |   | Netapp SAN (4U?) |  </code></p>
<p>Flareon was server 2 in this rack, and was about 2m / 7ft up. No matter, I grabbed a little step ladder and slid out the rack rails. I went down to get the server and noticed it was a bit difficult to climb the ladder with an unwieldy and heavy server that weighs about 15kg / 33lbs.</p>
<p>When the server and I reached to the top, I also noticed that the rail mount had a system with four latches that had to click all together. I knew this already, but somehow I had never been as aware as now. A further complication was that when it latches, you need to click the latch on the left or right side to release it. But you're probably holding the server with one hand in the front, and one hand in the back (so yes, not left or right). For the lower placed servers you could hold the server with one hand and a knee, and use your spare hand to release the latch but at 2m in the air, balancing on a step ladder, I had no knees or spare hands available. I suddenly realized I had come to a two-person job alone and felt very lonely.</p>
<p>I now had a choice. I could go down and give up, or see if there was someone available to help. Alternatively I could just go for it and try to do it by myself. What's the worst that could happen? My instincts have been shaped by software development, and there the worst that can happen is not very bad. You can always do git reset.</p>
<p>So after a second or two of deliberation while balancing on the step ladder with a server that was not getting any lighter, I decided to go for it. Standing on the left side, I would lift the server over the rails, and I'd try to drop the two latches on one side into place. Then I would move my hands slowly to the other side and push the other two carefully in. I never got that far. One latch clicked, and the other one missed its opening. To fix it, I’d need one hand to hold the server and another to release the latch. Impossible. Within half a second the 15kg server started falling 2 meters towards to the ground. The rails twisted and released the latch. I tried to grab the server on its way, but lost my balance and fell after it like Gandalf fell after the Balrog.</p>
<p>I don't think the data center had ever been exposed to this amount of sound during normal operations. It was deafening. Within 10 seconds I realized the floor was in fact not deserted, as two guys who had been working three aisles down came running towards me. &quot;What the f*ck happened?!&quot; they shouted. Actually the scene was quite straightforward and after a moment they had figured it out. I was so shocked I didn't even answer. They asked if I was alright and then offered help. I noticed I was not hurt myself, and the server had a dented front but otherwise looked alright.</p>
<p>With the help of the other guys we put Flareon in its rack. I wasn't sure if it was broken or not, but it couldn't stay on the ground so why not put it up there now that I had two volunteers?</p>
<p>When the server was in place, my new friends went back to their own servers and I tried to calm my nerves. I looked at my phone and saw a lot of Nagios alerts coming in. What the hell?</p>
<p>After deciphering some of them and realizing another server had suddenly gone offline, I looked up and sure enough, the ON led of the server underneath Flareon looked like it was very much OFF. In my fall I must have hit the power button... I called Hans for help, explained what had gone wrong and he calmly reassured me that we would fix this first, and figure out the rest later. What a great team mate!</p>
<p>Hans set out to restart all the applications on this server. About 50 Mendix apps were down for half an hour or so (I tried to find this incident on the status page but we only started using it a couple weeks after this incident. Coincidence?). In the meantime I hooked up Flareon's power and network and started it up. Hans did a system test and everything looked alright.</p>
<p>I locked up the rack, packed the cart in my car and started driving back. On the way home I felt incredibly stupid. I had dropped a $10k machine, caused a serious production outage and suddenly realized this could have ended much, much worse.</p>
<p>See, the server fell down and could even easily hit the Netapp with 40+ disks spinning at 10k RPM. Those things don't like vibrations. There are stories of disk arrays being destroyed by way less than what happened today. Rebooting a server is annoying, but destroying ALL primary data for hundreds of customers would have been a disaster of unimaginable consequences for the company. I got away easy, but the realization made me nauseous.</p>
<p>That spring day in 2014 is burned into my memory - a real rollercoaster. Here are three things that I've learned:</p>
<p>- Hardware is not software, there is no git reset</p>
<p>- Never do a two-person job solo</p>
<p>- Disable the power button on servers</p>
<p>The story became infamous within our team. A couple months later we went back for some maintenance and had to do a re-enactment.</p>
<figure><img src="https://blog.waleson.com/images/2025/08/the-time-i-literally-dropped-server/27305f9c-image.jpg" alt="Yours truly. Horse mask and server opened up for dramatic effect." loading="lazy"><figcaption>Yours truly. Horse mask and server opened up for dramatic effect.</figcaption></figure>
<p>Flareon went through some stress testing and then happily served our customers for years. I believe it was finally decommissioned in 2021/2022.</p>
<p>P.S. I looked up some instruction videos for the latching mechanism for the DL360p. I was probably doing it wrong as you're supposed to take the latches apart, attach half to the server, and just slide it in! So lesson number four:</p>
<p>- Read the instructions.</p>
<p><a href="https://news.ycombinator.com/item?id=44935092">Discuss on HN.</a></p>
]]></content:encoded>
            <category>infrastructure</category>
        </item>
        <item>
            <title><![CDATA[Energy Driven Development]]></title>
            <link>https://blog.waleson.com/2025/07/energy-driven-development/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2025/07/energy-driven-development/</guid>
            <pubDate>Sun, 27 Jul 2025 11:43:00 GMT</pubDate>
            <description><![CDATA[Over the years I've noticed a pattern when I'm developing software. When faced with a huge backlog of things to pick up, I tend to go with the tasks that give me the most energy. I've been calling this Energy Driven…]]></description>
            <content:encoded><![CDATA[<p>Over the years I've noticed a pattern when I'm developing software. When faced with a huge backlog of things to pick up, I tend to go with the tasks that give me the most energy. I've been calling this Energy Driven Development in my head. My team leads / PMs must have hated this back in the day, because I was often only giving a halfhearted attempt at the official sprint items, but I believe that it's often the right approach. When I wake up, and in the shower I'm already thinking about a problem and how to solve it, I should probably be working on that.</p>
<p>What personally gives me energy is if there are &quot;deep problems&quot; that require an elegant solution, so if you let me focus on those things, you get a well-organized codebase out of it, making all future development a lot easier.</p>
<p>I believe that if you have good engineers with a product mindset, they will have a well-developed intuition for what to work on. So if you're managing a backlog and are stuck in analysis paralysis, ask the engineers what gives them the most energy and let them work on that. It'll be great for motivation and ownership.</p>
]]></content:encoded>
            <category>teams</category>
            <category>programming</category>
        </item>
        <item>
            <title><![CDATA[10x teams]]></title>
            <link>https://blog.waleson.com/2025/02/10x-teams/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2025/02/10x-teams/</guid>
            <pubDate>Sun, 09 Feb 2025 11:35:00 GMT</pubDate>
            <description><![CDATA[As I've written before , I've seen teams work in incredible burst of productivity. We achieved 10x productivity for a week or so at a time. I've always been fascinated by it. Wouldn't it be great if we can get closer to…]]></description>
            <content:encoded><![CDATA[<p>As I've <a href="https://blog.waleson.com/2019/01/highly-productive-teams-and-their-speed.html">written before</a>, I've seen teams work in incredible burst of productivity. We achieved 10x productivity for a week or so at a time. I've always been fascinated by it. Wouldn't it be great if we can get closer to these peaks in our day-to-day? Having now seen dozens of teams in various situations, I think the ingredients for having the perfect storm of productivity are:</p>
<ul>
<li>Team wants to work for someone <strong>who cares</strong> about what they do.</li>
<li>There is <strong>focus and clear priority</strong> for the work to be done.</li>
<li>The team has the right <strong>resources</strong> at hand.</li>
</ul>
<p>You probably can't achieve 10x performance consistently, but most mission-driven startups get very close. If you have the feeling that something is wrong with your team's output, you can use the following flow-chart to find problems.</p>
<p><img src="https://blog.waleson.com/images/2025/02/10x-teams/96897693-screenshot-from-2025-02-09-12-31-03.png" alt="" loading="lazy"></p>
<p><img src="https://blog.waleson.com/images/2025/02/10x-teams/8a9879ea-screenshot-from-2025-02-09-12-31-09.png" alt="" loading="lazy"></p>
<p>Here's a <a href="https://drive.google.com/file/d/1K86xnTgYATXKqxT93JSX4aiEroU7XX-A/view">PDF version</a>.</p>
]]></content:encoded>
            <category>teams</category>
            <category>leadership</category>
        </item>
        <item>
            <title><![CDATA[Bakelite to the Future - 1950s rotary phone ESP32 bluetooth headset]]></title>
            <link>https://blog.waleson.com/2024/10/bakelite-to-future-1950s-rotary-phone/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/10/bakelite-to-future-1950s-rotary-phone/</guid>
            <pubDate>Tue, 29 Oct 2024 15:32:00 GMT</pubDate>
            <description><![CDATA[Edit: since creating this post I've also created a video about this project on YouTube. See it below or jump straight into all the details below. Here's a very beautiful antique rotary phone. It's a Dutch PTT phone model…]]></description>
            <content:encoded><![CDATA[<p>Edit: since creating this post I've also created a video about this project on YouTube. See it below or jump straight into all the details below.</p>
<iframe loading="lazy" allowfullscreen="" src="https://www.youtube.com/embed/l37sxSrmchw"></iframe>
<p>Here's a very beautiful antique rotary phone. It's a <a href="https://dutchtelephones.com/virtueel-museum/toestellen-1930-1960/type-1950/">Dutch PTT phone model called simply &quot;1950&quot;</a>.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/cb14a1a8-img-20241027-164039596.jpg" alt="" loading="lazy"></p>
<p>But, what's this at the back? A USB-C port?</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/1dfebe40-img-20241027-162345309.jpg" alt="" loading="lazy"></p>
<p>A peek inside shows us some weird things! How did that LiPO battery get there?</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/2d1b7b16-img-20241027-103637192.jpg" alt="" loading="lazy"></p>
<p>Ok let's quit the jokey intro, this was all me. I've had this phone for about 15 years and over the last couple of weeks I've converted into a fully functioning Bluetooth headset using an ESP32 microcontroller.</p>
<p>In this post:</p>
<p>- 1 - Features</p>
<p>- 2 - Lessons Learned</p>
<p>- 3 - <strong>Pictures and story!</strong> of how it got made <strong>(scroll down!)</strong></p>
<p>but first...</p>
<h2>Does it work?</h2>
<p>This morning was the first Google meet I've used it in:</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/87caf681-screenshot-from-2024-10-29-09-09-34.png" alt="" loading="lazy"></p>
<p>A participant reported on the sound quality</p>
<blockquote>
<p>&quot;I didn't know how much I missed the different sound quality! Beautiful. We could hear you very well, not distorted. I wish I recorded your speech ehehe&quot;</p>
</blockquote>
<p>I could hear them very well too, so I consider this a great success!</p>
<p>Get the source code on Github <a href="https://github.com/jtwaleson/bakelite-to-the-future-esp32">jtwaleson/bakelite-to-the-future-esp32</a></p>
<h2>Features</h2>
<p>If you count the hours and multiply it by my hourly rate, this is BY FAR the most expensive phone I've ever touched. But you get a lot:</p>
<ul>
<li>
<p>The <strong>buzzer</strong> works when you are dialed (but not if you are dialed on Whatsapp?)</p>
</li>
<li>
<p>The <strong>rotary dial</strong> can be used to dial complete phone numbers</p>
</li>
<li>
<p>The <strong>red button</strong> connects / disconnects my phone</p>
</li>
<li>
<p>The <strong>red button</strong> connects / disconnects my laptop</p>
</li>
<li>
<p>The <strong>two indicators</strong> indicate Bluetooth usage (bottom) and an active audio connection (top)</p>
</li>
<li>
<p>Lifting <strong>the horn</strong> or putting it down picks up / hangs up call. If no number is dialed you are connected too Google Assistant/Siri.</p>
</li>
<li>
<p>It has a 1500mAh battery lasting for about 1 day. I didn't implement any power savings. As it's mostly a gimmick and easily connected to usb-c for charging this is fine.</p>
</li>
<li>
<p>Most importantly: I can once again slam down the phone if I'm having an angry phone call. Which I never have, but still.</p>
</li>
</ul>
<h2>Lessons Learned</h2>
<p>I have some new skills under my belt:</p>
<ul>
<li>
<p>I2S protocol and its many flavors, and how finding the right components is tricky (the software only supports variant left-shifted, but this chip only support Philips! So have to order new parts, look at datasheets and wait 3 days)</p>
</li>
<li>
<p>An oscilloscope and function generator are great tools!</p>
</li>
<li>
<p>This is my first ESP-IDF project, so getting to use that stack was fun. It works quite well!</p>
</li>
<li>
<p>You are on your own. Even though the ESP32 community has tens of thousands of people, there's not many people that have tried out this part of the stacks. Poorly documented functionality and missing integrations all around. However, Espressif has great people on Github.</p>
</li>
<li>
<p>Why flyback diodes are needed and what inductive loads are. (Yes I am a beginner).</p>
</li>
<li>
<p>The difference between MOSFETs (voltage controlled) and BJTs (current controlled) and NPN (low-side switching) and PNP (high side switching).</p>
</li>
<li>
<p>How simple rotary dials are.</p>
</li>
<li>
<p>How class-d amplifiers work. I vaguely knew this, but now I learned why there's no common ground in class-d amps and that you need that if you have a headset, because the mic and speaker will share a common ground over a three-wire connection.</p>
</li>
<li>
<p>Super glue can be removed with nail polish remover.</p>
</li>
</ul>
<h2>Pictures and the story</h2>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/0691c58d-img-20241005-103447137.jpg" alt="" loading="lazy"></p>
<p>The un-opened phone and connection box. I bought it for like 10 euros in a second hand shop 15 years back. Actually it had 3.5mm audio jacks because I already tried to convert it 10 years ago into a wired headset. Now we are going to do much better!</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/364c7a27-img-20241005-103628568.jpg" alt="" loading="lazy"></p>
<p>It's pretty full on the inside! You can see my wires for tapping off the audio signal made with UTP wires. The phone originally had two lines and you could switch between red and black. I don't know what the white buttons were used for. I'm going to use them for * and # DTFM codes and some other hacky stuff.</p>
<p>The indicators on the bottom left look weird, but we'll get to that.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/3b39ad67-img-20241005-152408128.jpg" alt="" loading="lazy"></p>
<p>The speaker and microphone look like this. I suppose they can be upgraded later.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/eabac14c-img-20241005-103519643.jpg" alt="" loading="lazy"></p>
<p>It was made by a company Standard Electric in The Hague.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/b16b46c5-img-20241005-103552642.jpg" alt="" loading="lazy"></p>
<p>And of course the PTT (Dutch postal service, for Post, Telegraph and Telephone)</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/9fad7cca-img-20241005-103601327.jpg" alt="" loading="lazy"></p>
<p>Was it made in 1965? The model is called 1950.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/2973f4f9-img-20241005-104053554.jpg" alt="" loading="lazy"></p>
<p>Ok let's start dismantling it with my fantastic soldering iron that heats up in 4 seconds (pro-tip, buy this 150 euro Aixun T3A T245, it will blow your mind)</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/161e363b-img-20241005-111549404.jpg" alt="" loading="lazy"></p>
<p>Wires are loose, I'll re-use them later.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/9bbf38d1-img-20241005-111917859.jpg" alt="" loading="lazy"></p>
<p>An old Capacitor.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/5566a0e8-img-20241005-111702340.jpg" alt="" loading="lazy"></p>
<p>And this? I don't know what it does but there were two of them. If you want them feel free to reach out.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/11d1aa09-img-20241008-220418753.jpg" alt="" loading="lazy"></p>
<p>There is no sensor for the horn, but I would like one, would this work? The answer is no.</p>
<figure><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/e64c232a-img-20241005-125121701.jpg" alt="The indicators are really beautiful and mechanical. No lights! The white cross is normally hidden under the black but when powered on, turns and becomes visible. When powered off, it is pushed back by a little spring. Unfortunately they don't work so let's fix them.Taken apart and glued back. Time to re-install it. Here I didn't put the springs back yet, so the white is visible." loading="lazy"><figcaption>The indicators are really beautiful and mechanical. No lights! The white cross is normally hidden under the black but when powered on, turns and becomes visible. When powered off, it is pushed back by a little spring. Unfortunately they don't work so let's fix them.<br><br><br><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/ece054ba-img-20241005-115454597.jpg"><br>Taken apart and glued back. Time to re-install it. Here I didn't put the springs back yet, so the white is visible.<br><br></figcaption></figure>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/e9266ab8-img-20241008-201822968.jpg" alt="" loading="lazy"></p>
<p>Hooking them up to power and controlling them with some simple BJTs. They also make a nice clicky sound when switching on/off.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/91c90df6-img-20241008-200500562.jpg" alt="" loading="lazy"></p>
<p>It works. The controlling wire (yellow) takes 0 mA. This is safe to use by the GPIO on the ESP32.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/4c6ec1c3-img-20241009-092053367.jpg" alt="" loading="lazy"></p>
<p>Let's try a lot of switching with a signal generator. It works, but what are these huge voltage spikes I seen on my scope?</p>
<figure><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/f390cf45-img-20241009-092709357.jpg" alt="Ah, inductive loads. Learned something new. Let's add some flyback diodes." loading="lazy"><figcaption>Ah, inductive loads. Learned something new. Let's add some flyback diodes.<br><br></figcaption></figure>
<figure><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/f6b1e33f-img-20241009-092634092.jpg" alt="And no more voltage spikes." loading="lazy"><figcaption>And no more voltage spikes.</figcaption></figure>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/67fa13e1-img-20241005-153431708.jpg" alt="" loading="lazy"></p>
<p>Hmm, I could fit a usb-c port in there.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/631398d9-img-20241008-220409076.jpg" alt="" loading="lazy"></p>
<p>Let's prepare all the buttons etc for connecting.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/7dccd520-img-20241008-190155875.jpg" alt="" loading="lazy"></p>
<p>My family was really happy with my maniacal focus on this project for three weekends straight.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/ae943288-img-20241013-131825111.jpg" alt="" loading="lazy"></p>
<p>Ok let's start to see if we can get sound to work with this INMP441.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/31506b01-img-20241019-100031500.jpg" alt="" loading="lazy"></p>
<p>Ok the signal that the HFP_HF library sends is weird, the microphone is not compatible. I went with a <a href="https://www.audiophonics.fr/en/devices-hifi-audio-adc/stereo-adc-board-wm8782-i2s-24bit-192khz-p-14897.html">WM8782 ADC based board</a> instead. I also <a href="https://www.tinytronics.nl/en/communication-and-signals/signal-generators-and-dacs/pcm5102-dac-audio-decoder-module-v2.0-i2s">bought a DAC</a> , because the first I2S board I bought was a class D amp and I need a common ground one. The new DAC/ADC boards can both be figured to &quot;standard&quot; left-aligned 16 bit i2s signals and don't need any further amplification.</p>
<p>The microphone in the headset was an electret and with +5V and a 4.7k Ohm resistor, hooked up via a tiny capacitor to the ADC, produced good results.</p>
<p>I spent like two full days trying and learning about the microphone and I2S and could tear my hair out at some points. Without on oscilloscope to see what's actually going this would have been impossible.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/4b6b8488-img-20241005-134624492.jpg" alt="" loading="lazy"></p>
<p>On the other hand, the rotary dial was super simple. One power (or ground) and the other one line gives a long pulse (while not at rest position) and the other pulses for each digit. The code to interpret this was like 10 lines.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/85d87f29-img-20241026-154400767.jpg" alt="" loading="lazy"></p>
<p>Starting the put everything together. I used an ESP32 T-Koala dev-board. The newer ESP32 chips (like C3/C6 etc) don't have classic bluetooth so you'll need to pick one that has both BLE and classic BT.</p>
<p>On the top you see the buzzer which needed 7 volts, so underneath there's a voltage booster from 5 to 7 volts configured with a switching NPN MOSFET.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/920994bb-img-20241027-193512562.jpg" alt="" loading="lazy"></p>
<p>Top view with the battery added.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/263d719e-img-20241009-121501758.jpg" alt="" loading="lazy"></p>
<p>The bar in the middle has a spring underneath it which presses the horn up. I isolated the spring and the bar with some plastic and added solder. If the spring (hooked to a GPIO) is up it touches the bar (hooked to ground). Now it acts like a switch! It bounces around (literally) so I need to add some debounce in the software.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/ef26221c-img-20241027-160526857.jpg" alt="" loading="lazy"></p>
<p>I modified the original back plate to add a USB-C and on/off switch. I had to cut off a bit of the steel with my Dremel, but it fit seamlessly into the original case. No bakelite was hurt during the making of this phone.</p>
<p>In this picture you see a simple &quot;power-only&quot; usb port, but I switched it for a fully functioning one, so now I can update the ESP32 firmware without opening the phone again.</p>
<p><img src="https://blog.waleson.com/images/2024/10/bakelite-to-future-1950s-rotary-phone/d66b3dfb-img-20241027-164108724.jpg" alt="" loading="lazy"></p>
<p>The completed thing. It's wireless and has about 1 day of standby time. This one sparks joy :)</p>
<h2>Links</h2>
<p>- HFP_HF example code <a href="https://github.com/espressif/esp-idf/blob/master/examples/bluetooth/bluedroid/classic_bt/hfp_hf/README.md">https://github.com/espressif/esp-idf/blob/master/examples/bluetooth/bluedroid/classic_bt/hfp_hf/README.md</a></p>
<p>- debug issue 1 <a href="https://github.com/espressif/esp-idf/issues/14700">https://github.com/espressif/esp-idf/issues/14700</a></p>
<p>- debug issue 2 <a href="https://github.com/espressif/esp-idf/issues/14231">https://github.com/espressif/esp-idf/issues/14231</a></p>
<p>- debug issue 3 <a href="https://github.com/espressif/esp-idf/issues/13738">https://github.com/espressif/esp-idf/issues/13738</a></p>
<p>- debug issue 4 <a href="https://www.esp32.com/viewtopic.php?t=40279">https://www.esp32.com/viewtopic.php?t=40279</a></p>
<p>- my code <a href="https://github.com/jtwaleson/bakelite-to-the-future-esp32">https://github.com/jtwaleson/bakelite-to-the-future-esp32</a></p>
<p>- Hacker News post <a href="https://news.ycombinator.com/item?id=41986452">https://news.ycombinator.com/item?id=41986452</a></p>
]]></content:encoded>
            <category>hardware</category>
        </item>
        <item>
            <title><![CDATA[The Future of Programming Systems - four thoughts]]></title>
            <link>https://blog.waleson.com/2024/09/the-future-of-programming-systems-four/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/09/the-future-of-programming-systems-four/</guid>
            <pubDate>Sun, 22 Sep 2024 16:15:00 GMT</pubDate>
            <description><![CDATA[Last week I gave a 20 minute mini talk at M&I Partners in Zeist about the Future of Programming Systems, and how the work of architects and engineering teams will grow closer together in the future. I'm completely…]]></description>
            <content:encoded><![CDATA[<p>Last week I gave a 20 minute mini talk at <a href="https://mxi.nl/">M&amp;I Partners in Zeist</a> about the Future of Programming Systems, and how the work of architects and engineering teams will grow closer together in the future. I'm completely obsessed by this topic so I'm excited! Here is that talk expressed in a blog post, slightly modified based on some feedback and new thoughts.</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/ea4dee09-screenshot-from-2024-09-22-16-19-28.png" alt="" loading="lazy"></p>
<p>Ok! Thank you for inviting me and sorry for not keeping to the original format as requested. I proposed this idea instead and it was accepted, so in part Matthijs (the organizer) is to blame and I hope it is OK with all of you as well ;)</p>
<p>When cycling here, I realized I needed to make a last minute change and added &quot;Systems&quot; to the title. I believe that limiting ourselves to the activities of the programmer is too narrow, and we should have solutions that involve everyone who is in the lifecycle of software. So it's about the <strong>System</strong> and that includes the designers, architects, users, database administrators, operators, and yes, also developers.</p>
<p>Your request was to talk about one of the classic texts of the software engineering / software architecture field. Now I didn't pick a classic, and I didn't pick a book. Instead I picked this semi-random <a href="https://divan.dev/posts/visual_programming_go/">blog post I found on Hacker News</a> a couple of years ago.</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/bad05505-screenshot-from-2024-09-22-16-22-28.png" alt="" loading="lazy"></p>
<p>Why this blog post? It's not so revolutionary or great that it is going to be the next classic work. But, it had some imagery that I just could not get out of my head. Open the link and read the first parts until the cave man animation. Here are some stills if you don't want to open the link for some reason.</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/d2032b8c-screenshot-from-2024-09-22-16-35-52.png" alt="" loading="lazy"></p>
<p>Editing code on a screen is just a window to a very large codebase!</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/ddf6e341-screenshot-from-2024-09-22-16-36-09.png" alt="" loading="lazy"></p>
<p>We are trying to form a mental image of the codebase in our head by staring at a tiny isolated part of it</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/1e07d994-screenshot-from-2024-09-22-16-35-59.png" alt="" loading="lazy"></p>
<p>It is a lot like a cave man seeing just a small part of the cave wall. With that we mean: it is primitive as hell!</p>
<p>So why did I pick it? Well, after a 7 year stint at Mendix, a Low-Code pioneer where I left in 2018 (see footnote 1), I am obsessed with better ways of programming. It was already better in many ways, but we still only scratched the surface with that product. It made me see the light, at least in my mind. This blog author seems to have some of the same ideas. Over the years I've collected bookmarks of all interesting hacker news posts about this field, and this is one of the top ones in the list. And it had nice visuals that made for a good story.</p>
<p>So let's go to the <strong>four thoughts</strong>.</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/015635f5-screenshot-from-2024-09-22-16-55-17.png" alt="" loading="lazy"></p>
<p>Today we are going to explore four ideas that lead to the way I view the future of programming systems. <strong>The middle box will remain a question mark</strong>, because it is currently a quest that I am on and I do not have the answers yet. The ideas around it will give some kind of direction for the center box though. I think you'll get it at the end.</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/edaeb127-screenshot-from-2024-09-22-16-55-32.png" alt="" loading="lazy"></p>
<p>The first idea is that the way we program now is stuck in a local maximum due to the way that software development evolved.</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/ed0f3ca6-paajgu9qwgppmgtdyxqxndleekkpqfrhc3b9xr4icjyaytilzu-w640-h360.jpg" alt="" loading="lazy"></p>
<p>The human body has an appendix. It is a useless vestigial* organ that served our predecessors and now sometimes kills us. That was the story at least, now science seems to say it is useful. But for today's story let's ignore that new science ;)  The point is that software is riddled with historic vestigial artifacts. If we would start over from scratch, we could build something much better.</p>
<p>*Vestigial: [....] having become functionless in the course of evolution. - Oxford Languages definition</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/92763299-screenshot-from-2024-09-22-17-07-39.png" alt="" loading="lazy"></p>
<p><strong>[frame 1 &amp; 2]</strong> In the fifties we programmed by connecting wires between vacuum tubes to make our circuits. Then computers became more flexible with punch cards, and then punch cards could be written with high level languages like Assembly and FORTRAN. Yes, in those days even Assembly was high level.</p>
<p><strong>[frame 3]</strong> Fast forward 40 years and we are still manipulating source code files with instructions. Or maybe not instructions if you use nice functional languages, but it's still very similar.</p>
<p><strong>[frame 4]</strong> But programs today are much more complex! Here is some code that connects to the internet and lets another program do something by sending it some data. And this is just one example of things that became more complex. In your daily lives you see how complex it can get, from self-driving cars to ChatGPT to the software that controls our international electricity grid.</p>
<p><strong>[frame 5]</strong> Nevertheless, a typical project is still structured like this. Folders with source code files, config files, makefiles, documentation etc. They looked like this 30 years ago and still do so today. The fact that &quot;you can throw anything here and call it a project&quot; is in my view a vestigial aspect of coding. It screams to me that no one is running the asylum. If I stick a random image in a random directory, very few IDEs or CI/CD systems will complain about that. Isn't that weird?</p>
<p><strong>[frame 6]</strong> Another vestigial aspect of code is the fact that we still have to bother with tabs vs spaces!</p>
<blockquote>
<p>(Audience member) - but.... that's now taken care of right? Everyone has linters and autoformatters?</p>
</blockquote>
<p>No! The fact that a linter has to fix something that shouldn't be there in the first place is horrible!</p>
<p>It is like we have built a whole bundle of tools that we've connected to each other and compensate for each others leaky abstractions to get the situation under control. To programs we've added databases, logging, monitoring, error tracing, compilers, linters, formatters, ORMs, test frameworks, mocks, LSPs, and all of these things as separate tools. And now if we want to change the way we program substantially, we have to change all of these things! It's very optimized and very powerful, but it feels like everything has been glued on.</p>
<p>The point is: we've evolved and are now at a sub-optimal point. Let's go to thought 2.</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/c567d08f-screenshot-from-2024-09-22-17-24-41.png" alt="" loading="lazy"></p>
<p>The next point is that we can understand things much faster with the right medium.</p>
<p>Understanding is crucial for being productive, and it goes for both machines and humans. If an IDE recognizes what some code or config actually does, it can apply special rules to it. For example, the Laravel framework in PHP is highly structured in what Events, Database Models, and Routes the application has. The framework treats these as High Level concepts. It's really great! But, because it's just coded as Class-based PHP, virtually every IDE still treats the code defining e.g. Events as regular PHP code. The IDE just sees the characters, and maybe some of the PHP, but knows nothing about Laravel!</p>
<p>Understanding is crucial for humans too.</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/06eb51cc-nsp6tz57msx4w3lu0w0hyn8epgcpmx5fso7z2igqd6a2z0un-m-w400-h365.jpg" alt="" loading="lazy"></p>
<p>Here is a text based description of what a combustion engine does. Thanks ChatGPT!</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/0c7b0821-1flomltwpirlwj6sbwjnkri0-iov9sydmg8ahrmfic8irwyq5m-w400-h400.jpg" alt="" loading="lazy"></p>
<p>Of course I much prefer a visual animation. Check <a href="https://ciechanow.ski/internal-combustion-engine/">this gorgeous website</a> for this and more examples. I grasp what it does much faster and much more accurately with a proper medium. That medium took way longer to generate than some text, but it pays off!</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/de2b41b4-hmebm96fnchknennggxeus2tetwszxom78mt3xwb8dmscpzrcc-w400-h267.jpg" alt="" loading="lazy"></p>
<p>Or when I'm asking for directions in a city. It's super confusing.</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/fb5ec3c2-p-kmtduvlbsiy2oirwkgslzps25ztw7fzywy6hr6bhyb9xxdsr-w320-h320.jpg" alt="" loading="lazy"></p>
<p>I much prefer a map!</p>
<p>When architects or developers are trying to make sense of a software landscape, the current mediums for that seem to be:</p>
<ul>
<li>reading powerpoint presentations by architects that are probably not at all grounded in reality</li>
<li>walking to a whiteboard with some seniors</li>
<li>reading documentation which is very likely out of date</li>
</ul>
<p>I would really like a better medium for that!</p>
<p>So the second thought is: other mediums can often explain things much, and we should try to find the right ones.</p>
<p>Let's continue to the next point.</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/e21d890a-screenshot-from-2024-09-22-17-37-39.png" alt="" loading="lazy"></p>
<p>Programming systems were originally built for the silicon. Humans were cheap and computers were expensive. Of course this has changed significantly as now the situation is completely reversed. We have powerful IDEs and there are some programming systems that really tried to do better (Mendix, etc), but nothing mainstream goes far enough.</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/61d142c9-screenshot-from-2024-09-22-17-39-35.png" alt="" loading="lazy"></p>
<p>As a piece of silicon I just need to know how I can execute a piece of code through my circuits.</p>
<p>As a programmer I have all the questions on the right. Please take a look, I'm not going to repeat them here. I think virtually <strong>no</strong> IDE helps you with these questions. Yes, some have dead code detection, but there is a lot of false negatives and if you stop using one of the entry points of the application (an API route for example) and you forget to delete it in the code, no IDE will warn you about that. Your monitoring tool knows it's not used. Why doesn't your IDE? I suspect that the fancy programming systems in Meta or Google will help you with a lot of these things, but those are not accessible to 98% of the software engineers. VS Code also highlights who wrote a piece of code and when, but if a function got moved once over the last years, good luck! Again, a sign of a bolted-on technology, which fundamentally does not capture meaning but code characters stored on disk.</p>
<p>So, the conclusion here is that programmers have a lot of questions while programming that A) need a lot of discipline to do right and B) lead to a lot of manual work. They have unmet needs.</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/609b522d-screenshot-from-2024-09-22-17-49-55.png" alt="" loading="lazy"></p>
<p>The last thought is one of hope.</p>
<p>I think software is truly truly special. It's in a category of its own if you compare it to any other industry out there. (Please note that I have not tried them all ;) )</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/ddec2d46-screenshot-from-2024-09-22-17-50-45.png" alt="" loading="lazy"></p>
<p>These are some of the reasons that make software different and that make people fall in love with software. I fell a long time ago, and I still am in love with it. The last point is made incredibly well by Fred Brooks. I managed to sneak in a reference to a proper classic text of the software engineering field after all!</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/b018f98b-2zi6b7vjld6-6pb-r-wpxct3n5glj42injjyptpingmkrfkd1j-w640-h360.jpg" alt="" loading="lazy"></p>
<p>Because software is so inherently different we have the power to re-invent our programming systems radically.</p>
<p>So please, help me out by 1) not accepting the status quo, 2) keep an eye out for the innovators and try their solutions, 3) always look for better abstractions in your code and architecture.</p>
<p><img src="https://blog.waleson.com/images/2024/09/the-future-of-programming-systems-four/609b522d-screenshot-from-2024-09-22-17-49-55.png" alt="" loading="lazy"></p>
<p>The four thoughts once again. These ideas are central to my belief that the future of software systems will be very different from where we are now. In the future, programmers, users, designers, enterprise architects, etc, will be supported by much better tooling the caters to all of their needs. And maybe most importantly we will finally get rid of tabs vs spaces ;)</p>
<p>Like I said, over the years I have curated a long list of articles that resonated with the way I was thinking. I'm now considering starting a newsletter or series of blog posts with the best links of the month with my commentary or something. Then again, maybe that's just a distraction and I should just start building my own company that actually builds the future of programming systems.</p>
<p>Thanks again for having me and letting me talk about my passion. What questions do you have?</p>
<p>[ <a href="https://news.ycombinator.com/item?id=41618070">discuss on HN</a> ]</p>
<p><strong>Footnotes:</strong></p>
<p>1. Mendix made a lot of things better through offering the right high-level concepts. I think criticasters would say that we were artificially limiting the developers. I would say that we put concepts like Forms, Database entities, Scheduled events, API endpoints etc as first class citizens in the development environment, so that <strong>it</strong> could help you. And help you in ways that make more sense than stupid-simple auto-complete. I love AI coding tools like Cursor or CoPilot, but the fact that they use text character based completion makes me mad as hell! It should be so much better!</p>
]]></content:encoded>
            <category>future-of-programming</category>
            <category>talks</category>
        </item>
        <item>
            <title><![CDATA["Security Is Our Top Priority" is BS]]></title>
            <link>https://blog.waleson.com/2024/07/security-is-our-top-priority-is-bs/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/07/security-is-our-top-priority-is-bs/</guid>
            <pubDate>Wed, 17 Jul 2024 03:29:00 GMT</pubDate>
            <description><![CDATA[A couple of years ago I was asked to give a conference talk about software security. Well, actually I wasn't really asked, my company bought a sponsorship package with a speaker slot and I replied to the internal email…]]></description>
            <content:encoded><![CDATA[<p>A couple of years ago I was asked to give a conference talk about software security. Well, actually I wasn't really asked, my company bought a sponsorship package with a speaker slot and I replied to the internal email asking for volunteers 🤣 Anyway, while preparing my talk, I realized a couple of important points about security that have not left my mind since:</p>
<ol>
<li>
<p><strong>Security is limitless</strong>. You can always spend more effort to make things more secure. The same goes for quality, safety, employee happiness, etc.</p>
</li>
<li>
<p>The needs of security are <strong>opposed</strong> to the needs of a <strong>convenient user experience</strong>. Improving one typically hurts the other.</p>
</li>
</ol>
<p>Now some organizations say &quot;Security is our #1 priority&quot;. Really? You want to make something that has no limits your number one priority? I mean security is a good thing, but this seems a bit too simple? In fact, hollow marketing claims like that can make me a bit angry. In this post I'll help you understand what to make of statements like that, and how to deal with security in real life. I'll cover:</p>
<ul>
<li>A philosophical intro</li>
<li>What does &quot;Security is our #1 priority&quot; actually mean?</li>
<li>How far should you take security?</li>
<li>What should companies say instead?</li>
</ul>
<h3>A philosophical intro to help our thinking</h3>
<p>The limitless nature of security and the balancing with user experience (UX) reminded me of something. I had to stop and think for a bit, but then I remembered. When I was about 19 or 20 I discovered GK Chesterton. I loved him as he made me see there were actually formidable Christian thinkers, and in my sort-of evangelical background there weren't many of those. Unfortunately for what 20 year old me would have liked, and despite Chesterton, my personal faith didn't survive, but I did still learn a lot from his writing that I appreciate to this day. Here's one quote that relates to this topic:</p>
<blockquote>
<p>“The modern world is not evil; in some ways the modern world is far too good. It is full of wild and wasted virtues. When a religious scheme is shattered (as Christianity was shattered at the Reformation), it is not merely the vices that are let loose. The vices are, indeed, let loose, and they wander and do damage. But the virtues are let loose also; and the virtues wander more wildly, and the virtues do more terrible damage. The modern world is full of the old Christian virtues gone mad. The virtues have gone mad because they have been isolated from each other and are wandering alone. Thus some scientists care for truth; and their truth is pitiless. Thus some humanitarians only care for pity; and their pity (I am sorry to say) is often untruthful.” - GK Chesterton, Orthodoxy, 1908</p>
</blockquote>
<p>What Chesterton is saying is that good things (virtues) can become bad if they are going too far or without including other good things. Seems like a no-brainer, and it is. But once you grasp this pattern you see it happening all the time. In this blog post it's about security freaks that forget about UX, but it's the same in our political world. Some people take DEI so far that they vilify white men. Some people take family values so far that they make others feel like misfits. Both family values and inclusivity are good things, but taken apart they can lead to extremes and can foster polarizing between the groups.</p>
<p>So what we need is a healthy mix between all the good things, without letting one become extreme and neglecting the others. This isn't easy! If you're a security extremist, and you still get hacked, at least you can say you did everything in your power to make it more secure. On the other hand, suppose you have balanced UX and Security and you took some decisions to make things less secure but for a better UX. You get hacked. How do you justify that? Saying &quot;Reality is complex and we took a decision to prioritize UX in this case&quot; is not a good soundbite. It takes guts to defend non-extremist positions, because the extremists will always have more powerful one-liners.</p>
<h3>What does &quot;Security is our #1 priority&quot; really mean?</h3>
<p>Is Security actually limitless? I think yes. In fact the safest way a bank can operate is to shut down their online presence, buy a big vault and put a small army outside the door. Even then, the amount of security checks and the size of the army can be increased infinitely. However, most people will prefer to send money using FaceId on their banking app and think that that security is good enough.</p>
<p>And do companies actually say this? Yes. Here's Microsoft &quot;<a href="https://blogs.microsoft.com/blog/2024/05/03/prioritizing-security-above-all-else/">Prioritizing Security Above All Else</a>&quot;, AWS &quot;<a href="https://docs.aws.amazon.com/whitepapers/latest/aws-overview/security-and-compliance.html">Cloud security at AWS is the highest priority</a>&quot;, Meta &quot;<a href="https://about.meta.com/actions/protecting-privacy-and-security/">Safeguarding your data is our highest priority</a>&quot; and there are many more.</p>
<p>It sounds nice, but does this in practice mean that whenever anyone has an idea to improve security, at the expense of UX, consumer prices, etc, you still implement it? Because that is what it sounds like. Of course the answer is no, so I'm not taking the statements too seriously, although I understand it gives your customers a warm fuzzy feeling. I'd prefer organizations to be honest and clear, but I can only dream about a world where this is true.</p>
<p>In reality these companies mean something like:</p>
<blockquote>
<p>&quot;We have a balanced framework of priorities, we categorize issues and give each a weighed score. We give UX issues 20%, security 25%, tech debt 10%, new features 20%, etc.. As you can see risk is the number 1 priority because 25% is higher than 20%.&quot;</p>
</blockquote>
<p>Or maybe they mean:</p>
<blockquote>
<p>&quot;Security (to a level that is the norm in our industry) is our number #1 priority. Once we have implemented enough we focus on other issues.&quot;</p>
</blockquote>
<p>If you have examples of completely honest security statements like &quot;we care about security because we need you to trust us, and therefore it's on of our top priorities&quot; I'd be very interested. Please comment!</p>
<h3>How far should you take security?</h3>
<p>When I gave the talk in 2018, where to actually draw the line was still a mystery to me. Having worked with risk frameworks like ISO 14971 and ISO 27001 since then, I now have some tools in my belt to help me deal with this.</p>
<p>It all starts with a scoring mechanism: figure out what do we have that needs to be protected, what risks are there and how likely / severe are the risks. You multiply the likeliness and severity and you get a risk number for each risk that you can map to low, medium, high.</p>
<ul>
<li>If the risk number is low you can safely accept the risk.</li>
<li>If it is medium you should consider risk controls unless justified.</li>
<li>If high you must implement risk controls.</li>
</ul>
<p>Then once the risk controls are in place, verify that they actually mitigate the risks and if the remaining risks are now low or non-existent. Then you are done.</p>
<p>Sounds simple, but where do we put the line of low, medium, high? I always put some decision makers together, give examples of things that could go wrong and ask them to come up with a scoring system. Then ask what kind of risks they are willing to accept, considering what the product or proces would like like with a risk control applied.</p>
<p>This simple scoring system has a risk matrix, a risk appetite / risk acceptance policy, and a risk inventory. It's a simple tool that can help you make decisions.</p>
<h3>What should we say instead?</h3>
<p>The entire world has been handling safety, quality and security in a grown-up manner for decades. Let's promote the companies that do that without saying it's their top priority. It's a meaningless statement.</p>
<p>In my ideal world, companies would say &quot;We maintain a state of the art security system, because without our customer's trust we would have no right to exist as a company. As such, it is one of the most important things we work on and we spend a large amount of our effort on it.&quot;. Probably doesn't sound as good to most people, but companies that say this would certainly score points with me.</p>
<p>Have good examples of honest security statements? <a href="https://news.ycombinator.com/item?id=40982307">Comment on HN</a>.</p>
]]></content:encoded>
            <category>leadership</category>
            <category>talks</category>
        </item>
        <item>
            <title><![CDATA[Always Optimize the Feedback Loop]]></title>
            <link>https://blog.waleson.com/2024/07/always-optimize-feedback-loop/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/07/always-optimize-feedback-loop/</guid>
            <pubDate>Sat, 06 Jul 2024 10:20:00 GMT</pubDate>
            <description><![CDATA[Speed, I am Speed. -- Lightning McQueen Some thoughts about the impact of feedback loops in startups and software. Parts: Intro Framework Observations Take-away Feedback delay is the time between doing something and…]]></description>
            <content:encoded><![CDATA[<p><img src="https://blog.waleson.com/images/2024/07/always-optimize-feedback-loop/24049fef-qp-tnbt1rm3iqcyy1q-ftmv9sp-r8qpwfkqrfdhdcr-ume1qs-x0ze4jhw-m.jpg" alt="" loading="lazy"></p>
<p>Speed, I am Speed. -- Lightning McQueen</p>
<p>Some thoughts about the impact of feedback loops in startups and software.</p>
<p>Parts:</p>
<ul>
<li>Intro</li>
<li>Framework</li>
<li>Observations</li>
<li>Take-away</li>
</ul>
<h2>Intro</h2>
<p><strong>Feedback delay</strong> is the time between doing something and seeing whether it was successful.</p>
<p>If the feedback is slow, we have a &quot;lagging indicator&quot;. It becomes hard to link causes and effects, and in consequence it becomes hard to optimize the performance. <strong>Fast feedback makes things easy</strong>.</p>
<p>How quickly the feedback arrives is critical. If it takes milliseconds to seconds, it works at the speed of the brain and enables a state of <em>flow</em>. If it takes decades to centuries, it needs a multi-generational organization like a government or organized religion to run it. Somewhere in the middle is the realm of persons, teams and companies.</p>
<h2>A framework of feedback time</h2>
<p>Here's a quick table of feedback times, in three groups, with each roughly an order of magnitude difference.</p>
<p><strong>C</strong> is the level where (large) organizations can run experiments without seeing the results for a long time</p>
<ul>
<li>C4 centuries</li>
<li>C3 decades</li>
<li>C2 years</li>
<li>C1 months</li>
</ul>
<p>At <strong>B</strong> a person or team can have the patience to execute this</p>
<ul>
<li>B4 weeks</li>
<li>B3 days</li>
<li>B2 hours</li>
<li>B1 minutes</li>
</ul>
<p><strong>A</strong> is the speed of the brain, the feedback arrives so fast that it feels effortless.</p>
<ul>
<li>A4 seconds</li>
<li>A3 100+ milliseconds</li>
<li>A2 10+ milliseconds</li>
<li>A1 1+ milliseconds</li>
</ul>
<h2>Observations</h2>
<p>With this mini framework in mind, here are a couple of observations.</p>
<h3>1 - Startups</h3>
<p>Startups are viable if they do something 10x better. Each of the steps is a 10x improvement, so if you manage to create a solution that brings feedback time down one step on the ladder, you have a viable startup. At least according to Peter Thiel.</p>
<h3>2 - Faster is always better</h3>
<p>Faster feedback is <strong>always</strong> better and never worse, but there are diminishing returns. E.g. when I'm typing and it takes 4 milliseconds to render to the screen, it's already imperceptible, and improving it more offers no benefit.</p>
<p>When looking to improve something, check what level it is at right now and what would count as imperceptible. The difference is the space that a new product or service could enter.</p>
<p>For example, in a regulated industry I will get feedback from an auditor at C1 (a couple of months) but my life would be so much better if they replied at A4 (seconds) and I can have a dialogue. So: there's an enormous opportunity here for a regulatory body that promises responses in B1/B2 (see e.g. <a href="https://www.scarlet.cc/">Scarlet</a>) or for a group of experts you can chat to instantly (<a href="https://openregulatory.com/community/">OpenRegulatory Slack</a>).</p>
<h3>3 - Multiple orders-of-magnitude improvements shake the world</h3>
<p>Improving by 1 step is great, but something weird happens when we take 2 steps down the ladder or more. Things that were previously impracticable or downright impossible, now become in reach.</p>
<p>When jetliners were invented, we could travel around the world in B3 (days) where in 1930 it still took my grandfather C1 (months) to go from Indonesia to The Netherlands. Jet travel has certainly changed international business, politics and tourism.</p>
<p>A recent example are LLMs. Getting a book summarized would take a human B2 (hours), and ChatGPT does it in A4 (seconds). By making it two orders of magnitude faster, we just went into flow-state territory!</p>
<p>When a new technology makes multiple orders of magnitude improvements, the world needs a couple of decades to adjust to this new reality.</p>
<h3>4 - Software vs Hardware</h3>
<p>Most software development features are in B, while hardware is in the lower Cs. They are wildly different industries because of this, with different approaches to QA.</p>
<p>Software developers that go into regulated industries see their feedback time going from B1 (when automated tests tell me there is a problem) and B3 (when customers tell me if they like something) to C2 (the regulators will review your documentation and give you feedback in about a year). Talk about culture shock!</p>
<h3>5 - Hunt for the feedback loop</h3>
<p>Whenever I am stuck on a problem, I have bad tendency to get blocked and not knowing where to start. When I notice this happening, I've learned to try to find something to do that works towards the goal and that gives me feedback the fastest.</p>
<p>In programming there are feedback loops that will always get me into a hyper productive state:</p>
<ul>
<li>
<p>Getting compiler / linter errors in my IDE. When I've refactored code or when I introduce a linter to help clean things up, I get a bunch of new errors that I just have to fix one by one.</p>
</li>
<li>
<p>I execute the program and get an error, then I fix it and go to the next error.</p>
</li>
<li>
<p>Getting CI test results (e.g. speed of the program, memory usage, code coverage). Changing something and seeing the result of my change gets me feedback pretty quickly, but CI is often in B1/B2 (minutes/hours) and unfortunately frequently gets me out of flow state.</p>
</li>
<li>
<p>Create a to-do list of things to finish, and try to get it to 0 as quickly as possible.</p>
</li>
</ul>
<p>SpaceX famously achieves rapid progress through rapid feedback cycles compared to the upfront design work at e.g. NASA.</p>
<p>Their long-term goal is to colonize Mars, which is clearly in C4 / C3 (centuries / decades). They won't know if this will work until it's done. Those kind of grandiose projects have a bad habit of not getting started. SpaceX brilliantly broke the problem up into multiple milestones, and got a feedback loop going towards milestones 1 - launching Falcon 9, 2 - making the boosters re-usable, 3 - building a bigger rocket which is entirely re-usable.</p>
<p>Their results are phenomenal.</p>
<p>This is a simple thing to apply when stuck on big projects: break it down, and get a feedback loop going that brings you towards the first milestone.</p>
<h2>Take-away</h2>
<p>Always look with a fresh view to your current way of working. How much better would things get if you got feedback sooner? That's where the opportunities are.</p>
<p>P.S. when finishing this I realized I could have written the same things about other cost factors. Time is a cost, but so are <strong>money</strong>, <strong>energy</strong> and <strong>materials</strong>. The framework could be the same and very similar observations could be made.</p>
]]></content:encoded>
            <category>startups</category>
            <category>teams</category>
        </item>
        <item>
            <title><![CDATA[[TSTIL] Mendix Cloud v4 - Part 2 - How to launch a complicated product]]></title>
            <link>https://blog.waleson.com/2024/06/2018-mendix-cloud-v4-part-2-launch/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/06/2018-mendix-cloud-v4-part-2-launch/</guid>
            <pubDate>Tue, 11 Jun 2024 07:22:00 GMT</pubDate>
            <description><![CDATA[[ This is a part of " The Software That I Love ", a series of posts about Software that I created or had a small part in ] Note : This was a multi-year mega project (at least for my standards) and ran from about 2015…]]></description>
            <content:encoded><![CDATA[<p>[ This is a part of &quot;<a href="https://blog.waleson.com/2022/10/the-software-that-i-love/">The Software That I Love</a>&quot;, a series of posts about Software that I created or had a small part in ]</p>
<h2>2018 - Mendix Cloud v4 - Part 2 - How to launch a complicated product</h2>
<p><em>Note</em>: This was a multi-year mega project (at least for my standards) and ran from about 2015 till 2018 when I left Mendix. It's still one of my proudest professional achievements. If you haven't yet, read <a href="https://blog.waleson.com/2023/07/mendix-cloud-v4-part-1-how-it-got/">part 1 about Cloud v4</a>.</p>
<p>During 2017/2018 our team was doing a lot of things at the same time:</p>
<ul>
<li>
<p>Handle the operational fires in Mendix Cloud v1/v2/v3.</p>
</li>
<li>
<p>Add new hardware with 50%-100% YoY growth, as well as handle all kinds of customer requests, like resizing VMs or adding more storage. These were all manual operations by our engineers.</p>
</li>
<li>
<p>Migrate v1/v2 apps to v3 to ensure standardization.</p>
</li>
<li>
<p>Build v4 which would make everything standard, scalable and self-service. It would be the solution to solve the chaos from v1/v2/v3.</p>
</li>
</ul>
<p><strong>Fires? What fires?</strong></p>
<p>Running a 24/7 hosting operation for customers that are not so technical is tricky. Our brand promise was to make hosting Mendix apps simple for customers, so that meant that WE had to do a lot of hard things. We had lots of crisis situations:</p>
<ul>
<li>
<p>Unexpected security vulnerabilities like <a href="https://heartbleed.com/">HeartBleed</a> that had to be patched. For HeartBleed we patched everything within about 4 hours after the announcement, thanks in a large part to Hans.</p>
</li>
<li>
<p>Physical servers breaking down, leading to about a hundred apps going offline at a time.</p>
</li>
<li>
<p>Operating system migrations (e.g. from Debian Squeeze to Wheezy), that had to be manually upgraded across thousands of VMs.</p>
</li>
<li>
<p>Programming errors leading to data corruption, on hypervisor level and also in the Mendix app that managed the entire cloud.</p>
</li>
</ul>
<p>This may sound strange, but I loved the adrenaline rush when a crisis started. I now have a sort of spider sense for it. As Product Manager I often took the lead, jumped into organizing mode and got customer service, the 2nd &amp; 3rd line engineers and management lined up. I tried to hold regular status overviews via organized excel sheets and write customer communication updates. After a couple of crisis situations we set up <a href="https://status.mendix.com">https://status.mendix.com</a> to streamline our communication, that helped a lot. I built pretty good relationships with our people in security, customer support and CloudOps. At easee, my later company, the server setup was super boring and I secretly missed the adrenaline rushes quite a bit.</p>
<p><strong>Needed features and the people that built them</strong></p>
<p>But back to v4. We had expanded our team with Emir, Hans T., Cenk, Raphael, and Diogo. Unfortunately Riccardo left for ~consultancy~ HFT pastures at some point, and Emir left too a bit later IIRC. The team started building v4 services. We could spin up empty Cloud Foundry clusters quite easily, but to get feature parity with v3 we needed:</p>
<ul>
<li>Backup services (nightly + PITR as a high-end option)</li>
<li>Service brokers for S3 and RDS</li>
<li><a href="https://blog.waleson.com/2022/10/the-reaper/">Reaper</a> / resumer for Free Apps</li>
<li>A Cloud Portal that could support both v3 and v4</li>
<li>A lot of improvements to the buildpack (client certificate support etc)</li>
<li>A logging service (+ live logging would be nice)</li>
<li>A monitoring service</li>
<li>A load balancer / SSL/TLS terminator</li>
<li>Dashboards for monitoring</li>
</ul>
<p>That's a lot of things we had to build! We also needed to up our documentation game because we were really lagging behind there. I managed to poach Kasja as a technical writer and she started creating a lot of articles, as well as bringing a lot of fun to the team. For some reason the atmosphere in our office was better on the days that she was working. It's nice to have diverse teams and not only smart introverted nerds (she is also a smart introverted nerd, but that aside).</p>
<p><strong>The plan to avoid the Second-system effect</strong></p>
<p>By this time v3 was pretty mature, and I knew enough about the software industry that I was dead-set on avoiding the <a href="https://en.wikipedia.org/wiki/Second-system_effect">Second-system effect</a>. So: no new features in v4 except for scaling horizontally, which we would get out of the box with Cloud Foundry. In fact, we would start with less features than v3, and start migrating the customers for whom the limited feature set in v4 edition was enough.</p>
<p>I figured we would launch v4 in four phases:</p>
<ol>
<li>
<p>Launch a v4 beta for Sandboxes / Free Apps without any guarantees. This way we could remove the <a href="https://blog.waleson.com/2022/10/sandboxes/">hacky Sandboxes implementation</a> in v3 and get some real world experience with running v4.</p>
</li>
<li>
<p>Launch some small non-critical apps on v4.</p>
</li>
<li>
<p>Make v4 the default for new apps (v4 unless you need VPNs, 99,99% uptime, etc.)</p>
</li>
<li>
<p>Start phasing out v3 and move everything to v4.</p>
</li>
</ol>
<p>I think I had the plan outlined in my head, but I didn't communicate this at all. People outside our team were really confused about what was happening and when, especially as all the other migrations from v2 were also still going on. It turns out Project Management is a skill, and I didn't have it yet. My approach was &quot;It's done when it's done and today we are just trying to survive&quot;. I didn't put any concrete dates on launches, because in that chaotic environment it was hard to be predictable.</p>
<p><strong>Struggles &amp; Highlights</strong></p>
<p>Nevertheless, the plan continued but some stressful situations followed. Here are some highlights:</p>
<p>1 ) <strong>Making Free Apps work with the Web Modeler.</strong> The Free Apps cluster had to be made faster and compatible with the Web Modeler. Read about that in <a href="https://blog.waleson.com/2022/10/instadeploy/">2015 - InstaDeploy</a>. Getting this more or less stable was a <strong>lot</strong> of work and had a <strong>lot</strong> of management eyes on it.</p>
<p>2 ) <strong>Sales team briefing.</strong> At some point I sat down with the Head of Marketing (Hans de V.) and we decided I should brief the sales teams on the new v4 system that was coming up. I expected some kind of dialogue, but instead sat next to Hans (who is really great at giving presentations), and was given the microphone in front of a Zoom call with 40 sales people. It was one of the absolute worst performances I've ever done. I had given plenty of mediocre presentations where I could look the audience in the face and get some kind of feedback. That interaction compensated my chaotic presentation style a bit, but here, talking into the webcam and just seeing my own slides was throwing me off. These sort of things require adaptability, preparation and experience and I had none.</p>
<p>3 ) <strong>Latency issues.</strong> Our great new &quot;High Availability&quot; feature where apps were hosted Multi-AZ turned out to be a performance disaster for some apps. We had customer apps where 1 API call would result in 1000s of sequential database requests . The latency to the database could jump between 0.1ms and 5 ms depending on the AZ, so after re-deploys the performance of apps would be dramatically different. Oops. This did not make our Customer Service and Professional Services teams happy.</p>
<ol start="4">
<li><strong>Migrating 6000 apps single-handedly in one night.</strong> After a while I wanted to migrate all Free Apps from the first and outdated Cloud Foundry cluster to a new and more professional cluster. These were about 6000 apps that needed their database, application code, dns entries and files moved to another Cloud Foundry cluster. Doing this properly with the team, checklists and reviewed migration scripts would take weeks. I decided to do it by myself in one night with a python script from my home after a long working day. Keeping track of this huge migration hit the limits of my brain and the stresslevel during the migration was insane. This turned out well but was very stupid, something could easily have gone wrong.</li>
</ol>
<p>5 ) <strong>AWS Costs.</strong> The costs at AWS were going through the roof. After a couple of weeks I discussed going for up-front investments with the Head of Finance and we had to transfer 300k EUR just for the first batch. Nowadays whenever I need anything from AWS NL I mention that I was (for a large part) responsible for getting Mendix on AWS and they are always happy to help me for landing a huge account.</p>
<ol start="6">
<li><strong>Road Trip.</strong> Mendix R&amp;D employees had a 1500 euro yearly training budget. Most people would not use it, or go to a nearby boring conference, spend 500 on the conference ticket, 500 on travel, and 500 on food &amp; hotel. I wanted something more epic. With about 8 colleagues we pooled all our resources, obtained free conference tickets (because the company sponsored Cloud Foundry) and went on an epic 10 day trip. First by plane from Amsterdam to Las Vegas, then by fancy convertible to Death Valley, Yosemite, San Francisco, the conference in San Jose, on to Los Angeles and back to Vegas. I'll write another post about this in the future, but it was one of the greatest trips of my life. We also learned a lot about Cloud Foundry.</li>
</ol>
<p><strong>Wrapping up and moving on</strong></p>
<p>In early 2018 I was getting ready to leave Mendix to be <a href="https://blog.waleson.com/2022/10/the-easee-wizard-engine/">CTO at a tiny startup called easee</a>. But by this time we had finished two important milestones, so I could leave without a sense of abandoning the project.</p>
<p>Some unsuspecting new customer was the first to go live on v4, the CSM team figured that this customer wouldn't need any of the v3 features and would not do mission critical stuff any time soon. Then more new, non-critical customers joined. Around this time we also publicly announced Cloud v4 with a blog post that is now lost.</p>
<p>About half a year after that, we decided (together with CSM, Support and Professional Services) that all new apps would go to v4 by default!</p>
<p>This completed step 3 of my 4-step plan. It had taken years of discipline of not giving out guarantees of VPNs, of not promising NL-based hosting, of promoting 12-factor architecture best practices, but now it was ready. Most apps could &quot;just&quot; be deployed to v4 and scaled horizontally, and we could scale the underlying infrastructure with just our credit card!</p>
<p><strong>The verdict</strong></p>
<p>Mendix Cloud v4 brought a lot of benefits:</p>
<ul>
<li>
<p><strong>Global deployment</strong>: we could offer locations in all AWS Regions within days, while before it would have been a huge pain to find a co-location provider, get approvals, buy hardware, fly there and set up the hardware. Let alone maintaining and growing it.</p>
</li>
<li>
<p><strong>Standardization</strong>: we offered a standard set of functionality and therefore could operate the apps very well.</p>
</li>
<li>
<p><strong>High availability</strong>: apps were running on multi-AZ 12-factor apps combined with highly available AWS RDS and AWS S3. Compare this with single-VM apps that could easily be down for an hour in case of hardware failure.</p>
</li>
<li>
<p><strong>App scalability</strong>: apps could scale horizontally and could be paired with gigantic RDS instances, something that we would have struggled with in v3 because of how tightly we packed apps onto physical machines.</p>
</li>
<li>
<p><strong>Organizational scalability</strong>: we could upgrade clusters without application disruptions (so no need to align with all customers) and could shrink and grow our cloud without doubling our engineering team. App resizes were also largely self-service, reducing the load on Customer Service.</p>
</li>
</ul>
<p>There were also some downsides:</p>
<ul>
<li>
<p><strong>Ultimately Cloud Foundry lost the OSS cloud war</strong>. In 2015/2016 we decided for Cloud Foundry. Kubernetes was in its infancy and seemed overly complex and not enterprisey enough. Despite that it turned out to be the big winner and we were stuck with Cloud Foundry. For the end user it wouldn't matter, as Cloud Foundry is just the engine underneath Cloud v4 and it can be swapped out for Kubernetes without customers noticing the difference. It was a sane choice at the time and we couldn't have predicted the future.</p>
</li>
<li>
<p><strong>It was expensive</strong>. I estimate that our infra costs were about 5 times higher on AWS than they were on own hardware because of how optimized we were running things on our own before.</p>
</li>
</ul>
<p>I think the pros <strong>far</strong> outweighed the cons.</p>
<p>To seasoned engineers this makes total sense, but just to re-iterate what worked well in the process:</p>
<ol>
<li>
<p>We kept the feature set of v4 as limited as possible to avoid the Second-system effect.</p>
</li>
<li>
<p>We prepared for the fundamental architecture changes for years. E.g. adding horizontal scalability support to the runtime, and offloading storage to S3 instead of the local file system. These had to be planned long in advance, without any direct benefit.</p>
</li>
<li>
<p>We didn't &quot;shout&quot; about v4 or commit publicly to a launch date until we were very close to ready.</p>
</li>
<li>
<p>We kept the communication lines open between all departments: R&amp;D, sales, CSM, Customer Service, CloudOps. All were actively involved in getting the first customers on v4.</p>
</li>
<li>
<p>We always launched with non-critical applications first to gain experience.</p>
</li>
</ol>
<p><strong>What happened afterwards</strong></p>
<p>After I left, the Mendix Cloud team (now called the Cloud Unit) grew to about 100 people. They continued improving the software, expanding the global footprint and offering dedicated Mendix Cloud regions for big customers. After the last apps on v3 turned off in early 2023, I believe the v4 name was officially retired, but it's still there in the documentation URL: <a href="https://docs.mendix.com/developerportal/deploy/mxcloudv4/">https://docs.mendix.com/developerportal/deploy/mxcloudv4/</a> . Note that there are 16 public regions now! In v3 we just had four regions, two of which were in The Netherlands. Also note that even 6 years after launching v4, there is still no v5, as the &quot;chaos&quot; of all the previous non-standard versions has been solved, there is no need to have multiple different versions.</p>
<p><strong>My personal take-away</strong></p>
<p>The crowning achievement for me personally were three applications that really needed the scale or fault-tolerance that Cloud v4 offered. The first was the <a href="https://www.mendix.com/videos/postnl-order-management-system-processing-1-5-million-orders-daily-with-a-mendix-app/">PostNL Order Management System</a> which had a huge landscape that needed to be deployed with horizontally scaled applications. The other was an app for the Huishoudbeurs in 2018, this was a companion app for the fair which needed to scale rapidly with thousands of concurrent users for a couple of days. The final one was a Japanese customer which needed a solution hosted very near Japan for latency reasons. Luckily AWS was there and we could spin up a new region within a couple of days. None of these could have worked on v3.</p>
<p>What makes me especially proud of the product is A) that it took years of disciplined, coordinated effort to make it possible B) that we managed to successfully host v3 and scale with the business needs year over year, while C) also spending just enough engineering capacity to build the much better solution with v4. The team was also really fantastic, I was having the time of my life with these guys.</p>
<p>We did all that with just 12 people.</p>
<p><img src="https://blog.waleson.com/images/2024/06/2018-mendix-cloud-v4-part-2-launch/14320568-cloudfoundrycorp-vertical.svg.png" alt="" loading="lazy"></p>
<p><em>I still remember this logo fondly</em></p>
<p>P.S. if you want your own &quot;Mendix Cloud&quot;-like hosting on Kubernetes ask my friend Xiwen who's building <a href="http://low-ops.com">low-ops.com</a> .</p>
<p><strong>next</strong>: <a href="https://blog.waleson.com/2022/10/the-easee-wizard-engine/">2018 - The easee Wizard Engine</a></p>
<p><strong>previous</strong>: <a href="https://blog.waleson.com/2022/10/the-reaper/">2017 - The Reaper</a></p>
]]></content:encoded>
            <category>mendix</category>
            <category>the-software-that-i-love</category>
            <category>infrastructure</category>
            <category>leadership</category>
        </item>
        <item>
            <title><![CDATA[[TSTIL] Dat Narrenschip]]></title>
            <link>https://blog.waleson.com/2024/06/dat-narrenschip/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/06/dat-narrenschip/</guid>
            <pubDate>Tue, 11 Jun 2024 06:35:00 GMT</pubDate>
            <description><![CDATA[[ This is a part of " The Software That I Love ", a series of posts about Software that I created or had a small part in ] Around 2005 I started creating a website for my friend Jaap who deals in antique maps at…]]></description>
            <content:encoded><![CDATA[<p>[ This is a part of &quot;<a href="https://blog.waleson.com/2022/10/the-software-that-i-love/">The Software That I Love</a>&quot;, a series of posts about Software that I created or had a small part in ]</p>
<h2>2005 - Dat Narrenschip</h2>
<p>Around 2005 I started creating a website for my friend Jaap who deals in antique maps at <a href="http://datnarrenschip.nl">datnarrenschip.nl</a></p>
<p>I wrote most of this here: see <a href="https://blog.waleson.com/2024/06/how-we-ended-up-owning-framing-shop/">How we ended up owning a framing shop</a></p>
<p><strong>next</strong>: <a href="https://blog.waleson.com/2022/10/porting-atari-tax-program-to-delphi/">2005 - Porting an Atari tax program to Delphi</a></p>
<p><strong>previous</strong>: <a href="https://blog.waleson.com/2024/06/2004-hp-49g/">2004 - The HP 49g</a></p>
]]></content:encoded>
            <category>the-software-that-i-love</category>
        </item>
        <item>
            <title><![CDATA[How we ended up owning a framing shop]]></title>
            <link>https://blog.waleson.com/2024/06/how-we-ended-up-owning-framing-shop/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/06/how-we-ended-up-owning-framing-shop/</guid>
            <pubDate>Mon, 03 Jun 2024 11:03:00 GMT</pubDate>
            <description><![CDATA[As of last year my wife manages her art framing shop in Soest . It's an interesting business and here's how we ended up owning it. Shortly after taking over the framing shop, the local newspaper wrote an article about…]]></description>
            <content:encoded><![CDATA[<p>As of last year my wife manages her <a href="https://www.utrechtaandemuur.nl/">art framing shop in Soest</a>. It's an interesting business and here's how we ended up owning it.</p>
<p><img src="https://blog.waleson.com/images/2024/06/how-we-ended-up-owning-framing-shop/9fc03faf-2cfff236-4533-450d-bdd0-3a34897ab3af.jpg" alt="" loading="lazy"></p>
<p><em>Shortly after taking over the framing shop, the local newspaper wrote an article about it.</em></p>
<p>When I was 16 and living in Middelburg my mom introduced me to a bit of an odd man. His kids went to the same school as my siblings and his wife told my mom they needed a new website. My mom suggested I could help because I was always on my computer and she wanted me to do something useful. Thanks mom!</p>
<p>Jaap (as he was called) smoked a lot, had long hair, and always listened to hippie music like Pink Floyd. Most importantly, he had a lot of interesting antique maps which he dealt in. His passion for the maps and the stories he told about them were incredible. His knowledge is encyclopedic, he can tell you exactly which edition of a map it is by subtle variations in the paper. I really liked him and we are friends to this day.</p>
<p>One of the maps he showed me was only one of thirteen surviving editions in the world. It is known that both Columbus and Hitler had at one point owned one. He gave it to me so I could touch it. No latex gloves or glass covers or anything. I was holding it in my bare hands and there was a chance that this map had been owned by either Hitler or Columbus, or maybe both ;). That was a mind blowing experience!</p>
<p><img src="https://blog.waleson.com/images/2024/06/how-we-ended-up-owning-framing-shop/874ed839-winkel-exterieur-2-2048x.jpg" alt="" loading="lazy"></p>
<p><em>The exterior of <a href="https://datnarrenschip.nl/">Dat Narrenschip</a> in Middelburg</em></p>
<p>So after some time I was done creating the site, we re-launched it on my php stack, and customers could view his collection. I have maintained the site ever since even though I do more &quot;important&quot; things now. Over the last 20 years it has gone through 2 more complete rewrites (php, php + jquery, now shopify). Now on Shopify it is virtually maintenance free, and if needed, other people can handle it. You can also buy maps online now, which I heartily recommend that you do. I personally have an antique map of every place I've lived in.</p>
<p>The website and hanging out with Jaap started my love for antique maps. In my view they are:</p>
<ol>
<li>Beautiful pieces of art.</li>
<li>Full of interesting history, geography, and exploration.</li>
<li>Still pretty reasonably priced, you can buy great maps for 500 - 5000 USD.</li>
</ol>
<p>When I see an antique map I think of all the people that must have collaborated to make this come true. <strong>The explorers</strong> who went all over the world, the <strong>surveyors</strong> who measured all the distances, the <strong>businessmen</strong> who brought the resources together to print hundreds of maps. The <strong>workers</strong> who carved the copper plates, the <strong>millers</strong> who made the paper in their mills, the <strong>painters</strong> who colored the printed sheets of paper. And finally of course the <strong>customers</strong> who bought the atlases, the rich merchants for their enjoyment or the seafaring navigators who needed proper maps to navigate the world.</p>
<p>Whenever I created something for the website I was paid either in cash or with an antique map. Later I had some more money and I would buy one every other year or so. The one I like most is a 1782 edition of the United States by Klockhoff. One of the very first editions recognizing the new country with their 13 states and their own flag. I think it is just absolutely beautiful, and from just 6 years after the declaration of independence. I often ask visitors to point out Washington DC (which is not on the map because it wasn't built yet!).</p>
<p><img src="https://blog.waleson.com/images/2024/06/how-we-ended-up-owning-framing-shop/82b4509c-klockhoff-1782.jpg" alt="" loading="lazy"></p>
<p><em>The very rare 1782 Klockhoff</em></p>
<p>Anyway, when I wanted to frame it, the local framing shop in Utrecht asked about 300 euros for it. I thought that was a lot of money, but still had it framed. It's on my wall today and many people who have had virtual meetings with me will have seen it behind me.</p>
<p>A couple years later I bought another map. Coming from a very DIY family, I thought I could frame it myself for way less money. How hard could it be? So I ordered some materials and tried it. It turned out that it was very difficult to get the corners in exactly 45 degrees and cutting the mats (passe-partouts) was pretty difficult too. Making sure the art work is kept safe and the materials are acid free turned out to be a requirement we didn't think about back then. Nevertheless, I managed to do it for about 80 euros and in my ignorance I was happy with the result. Then we framed some more maps together with Stephanie (my wife) and we both enjoyed doing it! Since then she has been selling antique prints and also framing a couple, but always as an extra activity besides taking care of our family, and with non-professional equipment. Which means it was slow and needing a lot of rework to get the quality right.</p>
<p><img src="https://blog.waleson.com/images/2024/06/how-we-ended-up-owning-framing-shop/dcab8cf0-02eed1ca-84ed-4a58-9f22-d53226a930af.jpg" alt="" loading="lazy"></p>
<p><em>On weekends she would sell her framed art on fairs etc.</em></p>
<p>Now that the kids are a bit older, she wanted to go full-time into selling art and framing and began looking for equipment on Marktplaats (the Dutch ebay). She looked for some professional equipment like a Morso miter chopper. To her surprise she found an entire shop for sale that also mentioned it had a Morso. That's how she found it! The owner was asking for a reasonable price for the store. We went over and it looked like this:</p>
<p><img src="https://blog.waleson.com/images/2024/06/how-we-ended-up-owning-framing-shop/4c717281-47f66680-8f04-4f20-b173-6ea99a9853b4.jpg" alt="" loading="lazy"></p>
<p><em>The workshop in Soest</em></p>
<p>Apparently the owner was 88 years old and finally retiring. We went over once, and on the way back decided that we should just do it. We had some extra money as I was freelancing that year. Buying a store wasn't really &quot;the plan&quot; as Stephanie first wanted to finish her professional framing course which would take a year, and I wanted to start my own software company, but the seller was in a hurry and chances like this don't come often. So I decided to work a bit less and Stephanie could do her course and work in the shop, while I took more care of the kids. The deal was finalized quickly.</p>
<p><img src="https://blog.waleson.com/images/2024/06/how-we-ended-up-owning-framing-shop/6583f95f-4ccd572e-392c-4b06-966a-e7e25f1e0e7f.jpg" alt="" loading="lazy"></p>
<p><em>The Art of the Deal</em></p>
<p>By now in mid 2024 she is a certified framer and runs the shop about 4 days a week. It's a lot of fun and she's really good at it. You can visit the store in Soest, look up her website <a href="https://kunstaandemuursoest.nl/">here</a>.</p>
<p>Life is a bit weird sometimes, and this string of random events is how we ended up owning a framing shop.</p>
]]></content:encoded>
            <category>personal</category>
        </item>
        <item>
            <title><![CDATA[How I learned to Innovate (a bit more) in Regulated Industries]]></title>
            <link>https://blog.waleson.com/2024/05/innovation-in-regulated-industries/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/05/innovation-in-regulated-industries/</guid>
            <pubDate>Mon, 27 May 2024 12:39:00 GMT</pubDate>
            <description><![CDATA[Here's a short story about how I learned to innovate (a bit more) in regulated industries. In the summer of 2020, Yves Prevoo had got easee in the first Techleap batch. Pretty cool! I was invited to join one of the…]]></description>
            <content:encoded><![CDATA[<p>Here's a short story about how I learned to innovate (a bit more) in regulated industries.</p>
<p><img src="https://blog.waleson.com/images/2024/05/innovation-in-regulated-industries/35db7e7b-f91w.jpg" alt="" loading="lazy"></p>
<p>In the summer of 2020, Yves Prevoo had got easee in the first Techleap batch. Pretty cool! I was invited to join one of the sessions for the CTOs in which Ali Niknam talked about his adventures. I didn't know anything about him then but I liked him right away as he also wore a black t-shirt and a Casio F91w, my standard outfit.</p>
<p>I had been struggling with innovating in a highly regulated industry. I dreaded the yearly ISO 13485 audits and always had the feeling we were doing everything wrong or breaking some law that we didn't know about yet. It didn't help we didn't have anyone with Medical Device experience and it didn't help that we had a very strict auditor. To be frank, I really had no idea how to develop anything new and get it certified without going bankrupt from the clinical trials. I didn't know what to do or how to lead my team in an inspiring way because of all the regulations.</p>
<p>Hearing Ali speaking about Bunq in such an optimistic sense was surprising. Wasn't banking also highly regulated? How did he keep the can-do attitude that I lost? So I decided to ask him about this during the Q&amp;A. As I stood up he complimented my Casio and I made some stupid joke that didn't land, but then I asked &quot;How do you keep innovation and fun alive in a highly regulated industry?&quot;</p>
<p>I think he understood the pain where my question was coming from and his answer was short and spot on. This is how I remember it:</p>
<p>&quot;Auditors are not omniscient, they are typically pretty boring and their job is to go through checklists. This is how the system works and it's a necessary evil. Now, it's your job to figure out what's on their checklists and how to give an answer to each item on the list. But don't start by focusing on doing everything right, first focus on what you want to achieve, and then figure out the checklists. After all, auditors are not leading industry advances, we need you to do that.&quot;</p>
<p>What a fantastic call to action! I felt very empowered and my whole way of thinking about auditors made a 180 in that moment.</p>
<p>In the next year I felt better, was able to file two innovative patents and ship a lot of improvements with my team. There was still quite some residual fear, and the EU really put a hard break on innovation with the &quot;no significant changes&quot; clause in the MDR.</p>
<p>But now, in every new project I take on in a regulated industry, I see my role vs the auditor's role crystal clear. My job is to innovate, their job is to make sure the necessary checklists are checked.</p>
<p>Thanks Ali!</p>
<p>(this is a cross-post from LinkedIn  <a href="https://www.linkedin.com/feed/update/urn:li:activity:7200829829997359104/">https://www.linkedin.com/feed/update/urn:li:activity:7200829829997359104/</a> )</p>
]]></content:encoded>
            <category>easee</category>
            <category>regulatory</category>
            <category>leadership</category>
            <category>startups</category>
        </item>
        <item>
            <title><![CDATA[One year of Freelancing as Fractional CTO]]></title>
            <link>https://blog.waleson.com/2024/05/one-year-of-freelancing-as-fractional/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/05/one-year-of-freelancing-as-fractional/</guid>
            <pubDate>Mon, 27 May 2024 09:20:00 GMT</pubDate>
            <description><![CDATA[I'm taking some time to recharge after struggling as a startup CTO for 5 years (with lots of ups and down). I have been working 40 hours a week in salaried positions from 2010 till 2023, and I decided to take it a bit…]]></description>
            <content:encoded><![CDATA[<p>I'm taking some time to recharge after struggling as a startup CTO for 5 years (with lots of ups and down). I have been working 40 hours a week in salaried positions from 2010 till 2023, and I decided to take it a bit easy. And with easy I don't mean to not work hard or to do simple work, but to not be 100% committed to a company for the long term.</p>
<p>In June 2023 I started freelancing as a Fractional CTO. It's for complicated projects or for companies that don't need a full-time CTO yet. It's been really great. In the past year I've:</p>
<ul>
<li>Posted my availability once in my &quot;goodbye to easee&quot; LinkedIn post.</li>
<li>Been hired by 5 companies, who all found me through my own network.</li>
<li>Networked more than ever before. I suddenly have time for random meetings throughout the day.</li>
<li>Have not started an LLC (BV), nor got insurance, nor opened a separate business bank account.</li>
<li>Have signed only 1 contract for my freelance work (but some more NDAs) and almost never committed to a certain duration of the work. The paperwork takes time (and expensive lawyer overhead) and so far I've been fine with operating on a trust basis.</li>
<li>Have had all my invoices paid (by at most a week later than the due date).</li>
<li>Earned 2.5x what I earned before, while frequently working for only 3 days a week and took more vacations.</li>
<li>Had a great vacation to Egypt with my family.</li>
<li>Bought a framing shop with my wife where she's now working full-time.</li>
<li>And maybe most importantly: I've spent much more time with my kids and their school activities than ever before. I've been on school trips, did fun things with my kids after school, was invited to teach my son's class about electricity and inventions.</li>
</ul>
<p>As for the work, it's always been interesting and most of the time fun. I've:</p>
<ul>
<li>Made plans and a technical architecture for a new product proposition, did competitive analysis and made plans to hire a team of about 40 engineers.</li>
<li>Set up an ISO 27001 ISMS in about 3 weeks, while working 2 days a week. That was a challenge but there was a lot of support from the full-time staff.</li>
<li>Interviewed about 40 people and hired a couple of them.</li>
<li>Set up k8s deployment with a helm chart and operator based on kopf, bringing my cloud skills back to excellence. (I had never touched kubernetes before).</li>
<li>Coached a software team on their way of working and individual engineers on their career tracks.</li>
<li>Helped management teams get a better grip of their technology stack.</li>
</ul>
<p>I'm doing this so I can start my own product company, and I don't find being a freelancer really fulfilling because there's no long-term perspective for me. But objectively speaking, going freelance is very very comfortable.</p>
]]></content:encoded>
            <category>leadership</category>
            <category>personal</category>
        </item>
        <item>
            <title><![CDATA[Struggle as a Service]]></title>
            <link>https://blog.waleson.com/2024/05/struggle-as-service/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/05/struggle-as-service/</guid>
            <pubDate>Wed, 15 May 2024 08:30:00 GMT</pubDate>
            <description><![CDATA[For most of us, life is the easiest and most comfortable it has ever been in all of history. We have better healthcare than royalty had 100 years ago, we fly around the world for fun and all the information and…]]></description>
            <content:encoded><![CDATA[<p>For most of us, life is the easiest and most comfortable it has ever been in all of history. We have better healthcare than royalty had 100 years ago, we fly around the world for fun and all the information and entertainment we could ever want is at our fingertips. In most civilized countries there's even decent welfare systems for the have-nots.</p>
<p>Although social media and climate change are giving us major anxiety, it sure beats hunger, plagues, world wars or nuclear gloom. Humans have evolved with constant discomfort, and now we're comfortable.</p>
<p>This is a reason for boredom and restlessness in the modern world. We want to be challenged and experience risks. Instead of life on easy mode we need struggle.</p>
<p>Perhaps this explains why seemingly normal people that have lots of other options start companies that do not make a lot of financial sense. There's a need to prove oneself, to work really hard, and to experience risks. Startups definitely let you do that. Whenever I see SaaS, I think <em>Struggle as a Service.</em></p>
]]></content:encoded>
            <category>musings</category>
        </item>
        <item>
            <title><![CDATA[The long long tail of AI applications]]></title>
            <link>https://blog.waleson.com/2024/05/the-long-long-tail-of-ai-applications/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/05/the-long-long-tail-of-ai-applications/</guid>
            <pubDate>Sun, 05 May 2024 20:26:00 GMT</pubDate>
            <description><![CDATA[My answer so far has always been: "No, bare LLMs are not going to compete with this product." I think that people are failing to understand the distinction between different classes of AI companies. I see it like this…]]></description>
            <content:encoded><![CDATA[<figure><img src="https://blog.waleson.com/images/2024/05/the-long-long-tail-of-ai-applications/067c035c-1s0u2-dfk9nbw6yenynvqmfpvh8nqjplkou-7gt6pfdhlmeennmwz8pig5uo.jpg" alt="Long tail - credit to Wikipedia" loading="lazy"><figcaption><i>Long tail - credit to Wikipedia</i></figcaption></figure>
<p>My answer so far has always been: &quot;No, bare LLMs are not going to compete with this product.&quot; I think that people are failing to understand the distinction between different classes of AI companies. I see it like this:</p>
<ul>
<li><strong>Foundational AI</strong> - creating models like GPT-4 (text), Sora (video) etc.</li>
<li><strong>Applied AI</strong> - using existing models to create smart applications.</li>
</ul>
<p>Note that there are also companies not focusing on AI - they will start lagging behind, let's forget about them ;)</p>
<p>There are <strong>orders of magnitude</strong> (!) more companies that will deal with Applied AI than Foundational AI, and they will be very busy for decades to come. Here's why:</p>
<ol>
<li>To get the most out of LLMs, you have to ask the right questions.</li>
<li>LLMs have access to very limited context by default.</li>
<li>LLMs are not AGI and agents need to programmed manually.</li>
<li>LLMs don't understand specialized problems.</li>
<li>Integrating AI in everything is a lot of work.</li>
</ol>
<h2>1 - To get the most out of LLMs, you have to ask the right questions.</h2>
<p>In &quot;The Hitchhikers Guide to the Galaxy&quot;, a bunch of philosophers decide to ask a computer the answer to &quot;The Ultimate Question of Life, the Universe, and Everything&quot;. After eons of calculations the computer famously answers &quot;42&quot; . The lesson is that we have to ask the right questions to get good answers. How does this relate to LLMs? Currently ChatGPT has an easy time looking intelligent because we are driving the conversation and we are responding pretty intelligently in the dialog with the LLM. A large part of the intelligent conversations with ChatGPT are due to the intelligence of the human.</p>
<p>When applying LLMs to a product, we have to be very clear about what answer we want answered, for all users of that product. This is difficult, and will require quite some brain power of the engineers at Applied AI companies.</p>
<h2>2 - LLMs have access to very limited context by default</h2>
<p>There's a whole world of knowledge in your head whenever you are working in a product. Assume you work in HR and are trying to process a contract change with one of your employees and you want a review of your proposal. Instead of asking the HR manager, you decide to first ask ChatGPT. When you start typing the question into ChatGPT, you will quickly notice you'll need to give it a lot of context for it to give a correct response:</p>
<p>- What was the contract before and why are we making this change?</p>
<p>- In which country are you located and are there any recent/upcoming regulations that might make this contract change problematic from a legal perspective?</p>
<p>- Is this change OK standard company HR policy or is it an exception?</p>
<p>- How will this contract change affect the performance/output/tenure of the employee?</p>
<p>- How fair is this change towards the rest of the organization? How likely is the employee to discuss this with others and what are the possible effects of this?</p>
<p>A lot of this context will be present in the heads of the HR manager you'd normally ask. If we want LLMs to give an answer without all the manual context copy/pasting, we need Agents to pass the right context to it.</p>
<h2>3 - LLMs are not AGI and agents need to programmed manually.</h2>
<p>Large Language Models are not Artificial General Intelligence. Listen to some Lex Fridman podcasts with Yann LeCun or Sam Altman if you need convincing on this one. LLMs are pretty limited in their abilities and &quot;just&quot; spit out text based on context. Within that capability they are already incredibly useful, but this is only the start. If we do some clever prompt engineering and we allow their response to issue commands, we can set up an <a href="https://www.promptingguide.ai/research/llm-agents">Agent structure</a> to perform tasks. One of the most straightforward agent structures is ChatGPT where it acts as a chat assistant to a user. Others can perform Retrieval Augmented Generation (RAG) to dynamically fetch external information into context.</p>
<p>Think of the LLM as the <strong>book-smart intern</strong> that will be handling questions, they are able to give text-book answers based on their information about the world at large. Think of the agent structure as the <strong>manager</strong> that gives instructions, checks the answers etc. To get the most out of the intern we need to give it the right instructions, gives it the right context and permissions to look up information and finally check the answers. For the foreseeable future, all this agent logic has to be programmed manually for the specific use-case you want to use it for. Read: quite a lot of work. The good news is that you can expect a TON of improvements and out of the box integration in this space over the next couple of years, but an LLM without an agent structure is not going to be very useful for most products and hard work will be required.</p>
<h2>4 - LLM's dont understand specialized problems</h2>
<p>LLMs are trained on public information on the internet, and English currently dominates there. There is a lot more non-public information and a lot more non-English content.</p>
<p>One of my friends (hi Fedor!) has a company that applies AI to speed up tender management. Their software scans the documents with details about the tender, and teams can collaborate to come back with a bid. They apply AI to find information quicker and answer questions. This is a very specialized expertise where one line on page 85 of document 14 might be critical to the viability of the bid. There's a ton of very specific terms that normal humans will have never heard of. What's more, the languages and rules vary wildly by country. We can not expect a generic LLM to give expert level answers in this context.</p>
<h2>5 - Integrating AI in everything is a lot of work.</h2>
<p>We've seen that thinking about the right questions is a lot of work. We've seen that getting the right context and prompts is a lot of work. We've seen that creating Agents is a lot of work. If we do all of these it gives us AI-power in one part of our product. So if we imagine a product that wants a simple &quot;smart input field&quot; that supports AI powered auto-complete, we have now built <strong>one</strong> &quot;smart input field&quot;. Unfortunately your product still has <strong>100</strong> dumb input fields. You'll need to think about how to make each of those smart. Right now each of those 100 fields will require manual work, as you'll have to think about what specific context the LLM should get for that place in the UI, who is able to get what context into the autocomplete functionality (e.g. an admin prompt might get different context than a guest user). Making your whole application smart will take a lot of effort.</p>
<p>Years ago only laptops had wifi. Then came the smartphones, then the TVs, then the thermostats, then the washing machines, then the doorbells, and we're not nearly near the end.</p>
<p>For AI there's a similarly long tail of applications where AI can add value, and we'll be working on incorporating them many many years. It's an incredible time to be in software engineering!</p>
<p><a href="https://news.ycombinator.com/item?id=40268011">Discuss on HN.</a></p>
]]></content:encoded>
            <category>ai</category>
            <category>startups</category>
        </item>
        <item>
            <title><![CDATA[Follow-Up]]></title>
            <link>https://blog.waleson.com/2024/04/follow-up/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/04/follow-up/</guid>
            <pubDate>Wed, 24 Apr 2024 12:21:00 GMT</pubDate>
            <description><![CDATA[Years ago I was a Product Manager of a product with lots of teething troubles. The Director of Support and I sat down from time to time to discuss the most urgent customer issues. One day he suddenly said, visibly…]]></description>
            <content:encoded><![CDATA[<p>Years ago I was a Product Manager of a product with lots of teething troubles. The Director of Support and I sat down from time to time to discuss the most urgent customer issues. One day he suddenly said, visibly annoyed, &quot;I see that you're agreeing with me and writing down things in your notebook, but are you actually going to do something with it this time?&quot; .</p>
<p>Needless to say, that hit home.</p>
<p>I've seen many LinkedIn posts that go like &quot;here are ten things that take 0 talent: following up on commitments, being on time, ... &quot; etc. . But I think that's wrong. Making sure you &quot;follow up&quot; is hard work. I'm a total pleaser by nature, I want to be agreeable and I dread confrontation. Telling someone &quot;I'm not going to do anything with this&quot; is very difficult. So my natural tendency is to say &quot;Hm yes that's a good point, it would be great if we did something with it, perhaps we can do ...&quot; and then go off and brainstorm something nice in a fantasy world. People go along with this most of the time, because it is very nice to go off into fantasy mode. It's just that at some point they have to admit reality. In this instance it took a couple of meetings to see that nothing was happening.</p>
<p>In the years afterwards, running a regulated quality management system and managing a team, I've had to unlearn my pleasing nature and get down to earth. I never want anyone to tell me again that I'm not keeping my commitments. I've noticed that I'm promising less, I say &quot;no&quot; more, and I have a simple checklist system to track all the things that I actually committed to. I smile a bit when I see other people go into fantasy mode and typically steer the conversation back to reality.</p>
<p>So thanks for the feedback Nilco, I'm definitely not perfect at following up, but a lot better than before.</p>
<p>(this is a <a href="https://www.linkedin.com/feed/update/urn:li:activity:7188619768076095488/">cross-post</a> from LinkedIn)</p>
]]></content:encoded>
            <category>mendix</category>
            <category>leadership</category>
        </item>
        <item>
            <title><![CDATA[AI programming tools should be added to the Joel Test]]></title>
            <link>https://blog.waleson.com/2024/04/ai-programming-tools-should-be-added-to/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/04/ai-programming-tools-should-be-added-to/</guid>
            <pubDate>Mon, 22 Apr 2024 08:55:00 GMT</pubDate>
            <description><![CDATA[Here's a wake-up call to all CTOs: AI programming tools are getting freaking amazing and if you don't allow your teams to use them somehow, it will bite you in the ass in a couple of years. You will be slower and you…]]></description>
            <content:encoded><![CDATA[<p>Here's a wake-up call to all CTOs: AI programming tools are getting freaking amazing and if you don't allow your teams to use them somehow, it will bite you in the ass in a couple of years. You will be slower and you will lose your best people.</p>
<p><a href="https://www.joelonsoftware.com/2000/08/09/the-joel-test-12-steps-to-better-code/">The infamous Joel Test</a> is a list from the year 2000 of 12 things all great software companies do. Since then most companies have implemented Git and CI/CD, checking of three items, so we have some space left in the 2024 update ;) I believe &quot;Do you allow your developers to use AI assisted development environments?&quot; is a necessary addition.</p>
<p>I get that you don't want your source code to end up on some OpenAI / Microsoft / Github server somewhere, sure, but find a way to use your own models or learn to live with it. Note that this is often not the same as &quot;#9 - Do you use the best tools money can buy?&quot; as blocking AI tools is about data security, not money.</p>
<p>So why do I think developers need AI programming tools? I'm a seasoned developer with about 15 years of serious programming experience. In the last 10 years I've done less of it, as I was busy with Product Manager / CTO things. I'm now starting my own company, so I'm programming a lot, and I'm exploring whether some technologies really have a 2-10x impact on my work (<a href="https://supabase.com/">supabase</a> does, shout-out to you guys!). I wanted to give AI assistants a try, so I spun up Github Copilot, <a href="http://plandex.ai">plandex.ai</a> and <a href="http://aider.chat">aider.chat</a> .</p>
<p>There's plenty of things wrong with these tools: they are often wrong, are slow and the GPT4 ones are really expensive. I have many opinions on how AI programming tools should work instead of the brute-force LLMs we use today, but I'll reserve those for another post. Because even in this early phase I can say that <strong>I will never go back to not using them</strong>. And this is just the start, it will only get better from here. Programming with Copilot is different from normal programming, a bit like singing a duet I guess. You have to adapt to having a partner, give it the right cues, move out of the way when it's their turn etc. But it takes the boring repetitive tasks out of programming, and I love it. I feel less tired, can do better work, and feel like I'm often going 2x faster.</p>
<p>So if you're an engineer, find a place that does not block you from using AI tools. Ask it during your interview process!</p>
<p>If you're a CTO: find a way to get your engineers to use them, or make one.</p>
<p>And if you're Joel Spolsky, please release an update of the Joel Test, would love to know what you think in 2024 ;)</p>
<p><a href="https://news.ycombinator.com/item?id=40112544">Discuss on HN</a></p>
]]></content:encoded>
            <category>ai</category>
            <category>leadership</category>
            <category>teams</category>
        </item>
        <item>
            <title><![CDATA[Rhyme is a parity bit]]></title>
            <link>https://blog.waleson.com/2024/04/rhyme-is-parity-bit/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/04/rhyme-is-parity-bit/</guid>
            <pubDate>Fri, 05 Apr 2024 13:29:00 GMT</pubDate>
            <description><![CDATA[Weird thought of the day. Speech patterns that rhyme are pleasing and resonate with our brain. Could there be an evolutionary benefit to it? Cultures without writing systems told stories to pass knowledge from one…]]></description>
            <content:encoded><![CDATA[<p>Weird thought of the day.</p>
<p>Speech patterns that rhyme are pleasing and resonate with our brain.</p>
<p>Could there be an evolutionary benefit to it?</p>
<p>Cultures without writing systems told stories to pass knowledge from one generation to the next.</p>
<p>As we know from the Chinese whispers game, a lot of information is lost that way.</p>
<p>During oral story telling, information is added, removed or changed, it is wildly unreliable.</p>
<p>In computer systems we add checksums or parity bits to lossy mediums to ensure reliable transmission.</p>
<p>In way, the constraint of rhyming words can be seen as a parity bit on sentences.</p>
<p>The amount of words that can be placed in a rhyming sentence is significantly lower than in an unconstrained one.</p>
<p>Perhaps humans got better at reliable data transmission when they evolved to appreciate rhyme.</p>
]]></content:encoded>
            <category>musings</category>
        </item>
        <item>
            <title><![CDATA[Podcasts]]></title>
            <link>https://blog.waleson.com/2024/03/podcasts/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/03/podcasts/</guid>
            <pubDate>Sun, 03 Mar 2024 16:54:00 GMT</pubDate>
            <description><![CDATA[The podcast hype train passed me by until late 2018. I never understood it and thought it was some Apple specific thing. Then, one evening, on a flight from Amsterdam to Berlin, I thought "I'm too tired to work or read…]]></description>
            <content:encoded><![CDATA[<p>The podcast hype train passed me by until late 2018. I never understood it and thought it was some Apple specific thing. Then, one evening, on a flight from Amsterdam to Berlin, I thought &quot;I'm too tired to work or read, but I can listen to an interesting story, let's give podcasts a try.&quot;</p>
<p>The first episode I listened to was by 99% Invisible. I believe that by the time I got back to Amsterdam a few days later I had listened to about 10 episodes. Half a year later and I had gone through 80% of all episodes. I'm a bit obsessive, and as it turns out, picky. For me to love a podcast it has to be:</p>
<ul>
<li><strong>A</strong>uthentic. I have a soft spot for the lone podcaster who loves what he/she is doing.</li>
<li><strong>I</strong>nformative. I want to learn new things, and be slightly entertained in the process.</li>
<li><strong>O</strong>rchestrated. I can't stand random people talking about something without a clear direction.</li>
</ul>
<p>Orchestrated and Authentic can sometimes conflict. It's a sign of good artistic leadership if that doesn't happen.</p>
<p>Since my first podcast in 2018, virtually no day has gone by without me listening to a podcast. Most nights I fall asleep while listening to one, which I resume the next day. Either I fall asleep and it doesn't matter, or I lie awake and learn something new. Both are good outcomes.</p>
<p>Here are the podcasts that I've listened to a LOT. Thank you to all the hosts, producers and other people involved in creating these.</p>
<p>The A / I / O are the aspects above on a 1-5 scale.</p>
<p><strong>A+ - Have to listen to any new episode:</strong></p>
<ul>
<li>The Memory Palace - A5, I5, O5</li>
<li>The Fall of Civilizations - A5, I5, O5</li>
<li>Hardcore History - A5, I5, O5</li>
<li>The Anthropocene Reviewed - A5, I4, O4</li>
</ul>
<p>If you know these podcasts you will recognize a common theme. Nostalgia. Apparently it runs deep with me.</p>
<p><strong>A - Loved most of the episodes but not all, or I'm slightly tired of it by now.</strong></p>
<ul>
<li>The Rest is History - A5, I5, O4</li>
<li>The History of Rome - A5, I4, O5</li>
<li>In Our Time - A4, I4, O5</li>
</ul>
<p>These podcasts have taught me so much about the world. I feel a bit of Renaissance man because of them.</p>
<p>The Rest is History is actually A+ to me right now, but I know I'll get a bit tired of it eventually. There's there's just so much! I've listened to about 50% of the episodes now. Around 10% of the episodes are not really interesting.</p>
<p>One of the best gifts I've ever received is a signed copy of the Rest Is History. Thank you dearest wife! Here it is:</p>
<p><img src="https://blog.waleson.com/images/2024/03/podcasts/d6bdba59-whatsapp-image-2024-02-16-at-09.17.23.jpeg" alt="" loading="lazy"></p>
<p><strong>B - Absolutely loved these in the past but lately I've stopped enjoying them as intense as before</strong></p>
<ul>
<li>99% Invisible - A3, I2, O5</li>
<li>This American Life - A4, I3, O5</li>
<li>The Lex Fridman Podcast - A5, I3-5, O3</li>
<li>Revisionist History - A3, I3, O5</li>
</ul>
<p>The style of Revisionist History get tiring a bit too quickly. 99% Invisible and TAL have become a bit too woke for my tastes.</p>
<p>The Lex Fridman podcast is sometimes great, sometimes so-so depending on the guest and the topic. Lex isn't the best interviewer (also definitely not the worst) but the insane level of the guests and high frequency of the podcast is amazing.</p>
<p><strong>C - Pretty enjoyable but too infantilized</strong></p>
<ul>
<li>Cautionary Tales - A2, I3, O3</li>
<li>50 Things That Made the Modern World - A3, I5, O5</li>
</ul>
]]></content:encoded>
            <category>personal</category>
        </item>
        <item>
            <title><![CDATA[The Fog of War, or: when being vague is useful]]></title>
            <link>https://blog.waleson.com/2024/03/the-fog-of-war-or-when-being-vague-is/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/03/the-fog-of-war-or-when-being-vague-is/</guid>
            <pubDate>Sun, 03 Mar 2024 16:36:00 GMT</pubDate>
            <description><![CDATA[I've always been a straight shooter. Perhaps it's the Dutch culture or the protestant roots, but mystery, rituals and concepts annoy me. If a company can't say clearly what their product does, I am annoyed. I don't need…]]></description>
            <content:encoded><![CDATA[<p>I've always been a straight shooter. Perhaps it's the Dutch culture or the protestant roots, but mystery, rituals and concepts annoy me. If a company can't say clearly what their product does, I am annoyed. I don't need you to tell me that my team will be 5% more effective, let me figure out myself how useful your product is on the merits of what it actually does.</p>
<p>Recently I had the pleasure to work with a CEO and got some feedback that I shared plans with our suppliers too clearly. He said he didn't like that at all and he wanted things to be as vague as possible. My gut reaction was that this was ridiculous. If they don't understand our plans and motives how can they deliver a good service? It goes against everything I've read about leading people.</p>
<p>But, when I reflected a bit, I thought of the idiom &quot;Knowledge is Power&quot;.</p>
<p>If knowledge is power, then it follows that the absence of knowledge must be weakness. By keeping the people around you weak, you keep the upper hand.</p>
<p>I don't <em>know</em> which way is right when dealing with your own team or with suppliers (although I know which I personally prefer). But: the insight has opened my mind and I now see the pattern of hiding information all the time, so I am really grateful to the CEO for teaching me this lesson. It gave me a new mental tool to understand how the world works.</p>
<ul>
<li>
<p>In strategy games, the Fog of War is crucial. You can't see what your enemies are doing and they can attack you from any corner of the map, until you put up scouts, watchtowers, etc.</p>
</li>
<li>
<p>By not sharing salary ranges, companies keep power to compensate people how they wish. If they are asking you to give your desired salary, you lose power by giving it.</p>
</li>
<li>
<p>In a consultancy company you have no one-size-fits-all product so you might want to have a vague website saying things about &quot;digital transformation&quot; etc. It sounds very important without sharing any information!</p>
</li>
<li>
<p>By not publicly committing to a roadmap, your company keeps the power to change its mind when it wants to. On the other hand, some customers might want to see whether your roadmap aligns with their needs and you might not close the deal without sharing a roadmap.</p>
</li>
<li>
<p>If you lead ambitious people and you want to keep your cozy management position, you might want to hide information from your team to prevent them from being promoted. On the other hand, <strong>if your team is stable</strong>, your team's power is the sum of the power of all the members. By giving your team the full information available, you can create amazingly powerful teams that do the right things the right way.</p>
</li>
<li>
<p>By not sharing plans with a supplier they might suspect you have other options, and they might work harder to get the contract.</p>
</li>
<li>
<p>By setting clear objectives for people and linking them to bonuses, promotions and salary increases, you give your people a LOT of power. If you can't set the right goals this will mess up your organization. By keeping these things vague you keep the power with management. This is often the right thing to do, because setting the right goals for a year up front is downright impossible.</p>
</li>
<li>
<p>In project planning, there is a 5d chess interplay of estimations, budgets, deadlines, scope. Think about the role of information the next time you are trying to figure out what is going on.</p>
</li>
</ul>
<p>In stable, high-trust, mission-oriented organizations power is not so important and knowledge is shared freely. In highly political organizations with lots of infighting, power is everything and information is guarded closely.</p>
<p>Knowledge is power.</p>
]]></content:encoded>
            <category>leadership</category>
            <category>musings</category>
        </item>
        <item>
            <title><![CDATA[The unreasonable effectiveness of i3, or: ten years of a boring desktop environment]]></title>
            <link>https://blog.waleson.com/2024/03/the-unreasonable-effectiveness-of-i3-or/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/03/the-unreasonable-effectiveness-of-i3-or/</guid>
            <pubDate>Sun, 03 Mar 2024 11:52:00 GMT</pubDate>
            <description><![CDATA[My wife uses Windows and over the years I've helped her move things to new systems. Win8, 10 and now 11. With every upgrade the UI changes. Now I can't right click the bottom right corner anymore to open Task Manager…]]></description>
            <content:encoded><![CDATA[<p>My wife uses Windows and over the years I've helped her move things to new systems. Win8, 10 and now 11. With every upgrade the UI changes. Now I can't right click the bottom right corner anymore to open Task Manager. The UI feels &quot;fresh&quot; and up-to-date I guess, but does it really matter?</p>
<p>My desktop has looked like this since 2008. I love the picture of the gearbox, it's a testament to the hidden precision engineering that goes on inside the built world all the time.</p>
<p><img src="https://blog.waleson.com/images/2024/03/the-unreasonable-effectiveness-of-i3-or/8e5942e9-screenshot-from-2024-03-03-12-40-08.png" alt="" loading="lazy"></p>
<p>I don't see much of the gearbox though, because most of the time my screens look like this:</p>
<p><img src="https://blog.waleson.com/images/2024/03/the-unreasonable-effectiveness-of-i3-or/aa16eeaa-screenshot-from-2024-03-03-12-50-19.png" alt="" loading="lazy"></p>
<p>The environment around my background picture has changed a little more, but has been stable for the last 10 years. My experience is very different to my wife's constantly changing system. In 2009 I moved from Windows to Ubuntu, then to Debian using Gnome. Then finally to i3 in 2013 after a brief affair with XMonad in 2012. So by now in early 2024, I've had the same minimal UI for more than 10 years, and if I'd only had the guts to adopt i3 earlier it could have been 15. I have made small changes but if I didn't like it I would revert back to the previous version. All the changes were conscious efforts and I had the freedom to go back when I wanted to.</p>
<p>This short post is in praise to boring stable systems, and a thank you to all the open source contributors that made my desktop environment possible.</p>
<ul>
<li>OS: Debian (since 2011)</li>
<li>Window manager: i3 (since 2013)</li>
<li>Browser: Chrome (since 2008)</li>
<li>Editor: vim (since 2011, now actually neovim but I haven't noticed a difference with vim)</li>
<li>Terminal: gnome-terminal (since 2009)</li>
<li>Shell: zsh / oh-my-zsh (since 2014)</li>
<li>Mail: Gmail (since 2005)</li>
</ul>
<p>Here are some new improvements to my home computer systems.</p>
<ul>
<li>Backup management: syncthing (since 2022)</li>
<li>Home automation and energy management: home-assistant (since 2022)</li>
<li>Microcontroller management: espthing (since 2024)</li>
<li>Shell history management: atuin (trying it out since early 2024)</li>
</ul>
<p>Being boring with tools is great. It gives you the freedom to be revolutionary in areas where you actually want to make a difference.</p>
]]></content:encoded>
            <category>personal</category>
        </item>
        <item>
            <title><![CDATA[[TSTIL] The WaterRower Camera]]></title>
            <link>https://blog.waleson.com/2024/03/the-waterrower-camera/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/03/the-waterrower-camera/</guid>
            <pubDate>Sun, 03 Mar 2024 08:28:00 GMT</pubDate>
            <description><![CDATA[[ This is a part of " The Software That I Love ", a series of posts about Software that I created or had a small part in ] I was getting more and more heavy during this period, almost reaching 90kg / 200lbs. I never…]]></description>
            <content:encoded><![CDATA[<p>[ This is a part of &quot;<a href="https://blog.waleson.com/2022/10/the-software-that-i-love/">The Software That I Love</a>&quot;, a series of posts about Software that I created or had a small part in ]</p>
<h2>2021 - The WaterRower Camera</h2>
<p>I was getting more and more heavy during this period, almost reaching 90kg / 200lbs. I never exercised but I had bought a rowing machine with a water tank because I wanted to exercise, in principle. The machine was bought in 2019 just before the pandemic and during the boring early pandemic months it was a welcome way to distract myself. For some weeks I started rowing like a maniac but of course it wasn't sustainable. The machine started collecting dust.</p>
<p>It had a small &quot;workout computer&quot; displaying the stats, but it was very dumb and not connected to anything. For some reason I thought it would be a good idea to measure my stroke speed, depth, and power more closely. The (black) elastic band is wound up around a cylinder. By painting the cylinder white and tracking with a camera how much black/white pixels are visible, it shows how far you've extracted the cord. Doing that a lot of times per second you can see stroke depth, speed, power, and recovery speed. I attempted to do this with a cheap ESP32 board with a camera but found that I could only get 1 FPS due to the way the library worked. I have since learned more about interacting with the hardware, and think that I can get it much faster because I only need to count pixels that match a certain threshold in brightness.</p>
<p>Anyway, that would take a lot of time. Instead I hooked up a raspberry pi zero 2 w and reached 60FPS with brute force. Then I built a simple web interface which I used to exercise about 5 extra times. It felt really good to exercise and have it monitored intensely at 60FPS.</p>
<p>Then my wife's cousin Johannes came by, saw me rowing and laughed his a** off. Apparently my rhythm was completely wrong and I should do short and very fast pulls, then a slow recovery. I had taught myself rowing in the wrong way and now couldn't get used to the right away anymore. I haven't rowed since, but I discovered that running is very nice. In the summer of 2022 I lost 13 kgs and got down to 77kg and feel a lot healthier.</p>
<p><strong>next</strong>: <a href="https://blog.waleson.com/2022/10/fda-crawler/">2021 - FDA Crawler</a></p>
<p><strong>previous</strong>: <a href="https://blog.waleson.com/2022/10/qms-integrity-checker/">2020 - QMS Integrity Checker</a></p>
]]></content:encoded>
            <category>the-software-that-i-love</category>
            <category>hardware</category>
        </item>
        <item>
            <title><![CDATA[Aiyima remote control codes]]></title>
            <link>https://blog.waleson.com/2024/01/aiyima-remote-control-codes/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2024/01/aiyima-remote-control-codes/</guid>
            <pubDate>Mon, 08 Jan 2024 20:33:00 GMT</pubDate>
            <description><![CDATA[I "upgraded" my old Technics SU-VX700 amplifier a couple weeks ago. After 15 years of loyal service I wanted to switch to something that had digital inputs, was more energy efficient, and had a remote control. After some…]]></description>
            <content:encoded><![CDATA[<p>I &quot;upgraded&quot; my old Technics SU-VX700 amplifier a couple weeks ago. After 15 years of loyal service I wanted to switch to something that had digital inputs, was more energy efficient, and had a remote control. After some digging I found the Aiyima D03. It checked all the boxes + was tiny, had bluetooth and was and only 150 euros. I call it &quot;upgraded&quot; because the Technics was a beast of a Class AA (whatever that means) amplifier and probably very Hifi worthy, the D03 might be considered a downgrade by audiophiles, but I'm not sophisticated enough to be able to tell the difference. Mind you, I am sophisticated enough to be very annoyed by TVs without external speakers. I think both amps sound great on my Linn Keilidh speakers.</p>
<p>The remote control was nice, but it's a separate one from my tv. Using the optical toslink output the volume buttons on my TV do nothing. Annoying for me and my family and very confusing for guests who wonder why the TV has no sound.</p>
<p>I started using some esphome microcontrollers for various home automation tasks, and I got an idea. Why don't I listen on IR to my TV remote and if the volume buttons are pressed, mimic the Aiyima remote and send the volume signals there too. I actually ordered some IR Transmitter and IR Receiver break-out boards some time ago but never used them before.</p>
<p>On a Saturday night I hooked everything up and started debugging the signals from the Philips and Aiyima remote controls. I took a quick look with my oscilloscope but then found how to come to a conclusion quicker using <a href="https://community.home-assistant.io/t/faking-an-ir-remote-control-using-esphome/369071">this blog</a>.</p>
<p>The Philips one was easy enough and showed up right away. The Aiyima uses some custom prefix and codes, or at least one not known to the esphome libraries. Many manufacturers use overlapping protocols (even though the keys might not overlap, not all the address space is used) so it recognized JVC, Pronto, Pioneer and some other codes.</p>
<p>Through trial and error I got it to work by using transmit_pioneer 0x4d80 through 0x4dff.</p>
<p>So if you ever want to accomplish the same, here are all the buttons on the remote mapped out.</p>
<p>Action - #nr - pioneer equivalent hex code</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-text"><span class="line"><span>bas- 0 0x4d80</span></span>
<span class="line"><span></span></span>
<span class="line"><span>tre- 1 0x4d81</span></span>
<span class="line"><span></span></span>
<span class="line"><span>mode 2 0x4d82</span></span>
<span class="line"><span></span></span>
<span class="line"><span>bas+ 8 0x4d88</span></span>
<span class="line"><span></span></span>
<span class="line"><span>previous 25 0x4d99</span></span>
<span class="line"><span></span></span>
<span class="line"><span>next 65 0x4dc1</span></span>
<span class="line"><span></span></span>
<span class="line"><span>tre+ 69 0x4dc5</span></span>
<span class="line"><span></span></span>
<span class="line"><span>vol+ 74 0x4dca</span></span>
<span class="line"><span></span></span>
<span class="line"><span>mute 78 0x4dce</span></span>
<span class="line"><span></span></span>
<span class="line"><span>vol- 82 0x4dd2</span></span>
<span class="line"><span></span></span>
<span class="line"><span>off 92 0x4ddc</span></span></code></pre>
<p>Having examined how similar the different protocols are, I feel like there might be a better way of doing this than emulating pioneer but I'm happy with this solution for now.</p>
<p>I was hoping that there would be secret codes on the amp that were not present on the remote control, to idempotently switch off the amp, switch to a specific input or go to a pre-set volume. That would be great for some more automation. I tried all 256 codes from 0x4d00 to 0x4dff but did not get any results. It's possible that an idempotent &quot;switch on&quot; is present but I didn't want to try all codes twice. I thought of Shenzen IO and realized that of course no one had time for this. That's what I deserve for always saying YAGNI to my engineers.</p>
<p>If you want to plug this into esphome, you can do it like so. This contains just one action but the rest can be extrapolated from the table above:</p>
<pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code class="language-text"><span class="line"><span>button:</span></span>
<span class="line"><span>- platform: template</span></span>
<span class="line"><span>    name: amp mute toggle</span></span>
<span class="line"><span>    on_press:</span></span>
<span class="line"><span>      - remote_transmitter.transmit_pioneer:</span></span>
<span class="line"><span>          rc_code_1: 0x4dce # amp mute tuggle</span></span></code></pre>
]]></content:encoded>
            <category>hardware</category>
            <category>how-to</category>
        </item>
        <item>
            <title><![CDATA[Cowboy C3 battery teardown / disassembly and rear light fix]]></title>
            <link>https://blog.waleson.com/2023/12/cowboy-c3-battery-disassembly-and-rear/</link>
            <guid isPermaLink="false">https://blog.waleson.com/2023/12/cowboy-c3-battery-disassembly-and-rear/</guid>
            <pubDate>Sun, 10 Dec 2023 15:04:00 GMT</pubDate>
            <description><![CDATA[A bit over a week ago the rear light of my Cowboy v3 / C3 broke down. Everything else was fine. Update on January 4th 2024: In the end I managed to fix the light. Check the bottom of the post for the conclusion. Thanks…]]></description>
            <content:encoded><![CDATA[<p>A bit over a week ago the rear light of my Cowboy v3 / C3 broke down. Everything else was fine.</p>
<p>Update on January 4th 2024: In the end I managed to fix the light. Check the bottom of the post for the conclusion. Thanks to reddit user <em>icannotfindagoodname</em> for the help in finding the issue!</p>
<p>After chatting with Cowboy support they offered me a new battery for 490 euros and that they would also send an offer with a discount, which appeared a couple days later. The discounted price was 367 euros. A bit much for a broken LED module! The battery was still totally fine.</p>
<p>So I'll attempt to fix it myself and I'll show you how to disassemble the battery here.</p>
<p>WARNING - DO NOT DO THIS AT HOME</p>
<p>Now I need to warn you to not try this at home. I have been working with electronics for a long long time and I know what to do and what not to do, and most importantly, know what I should not even try. Battery packs are not a joke and could lead to serious fire and health problems if not handled correctly. Real professionals will probably even look at my photos and say that I'm an idiot for doing this in my living room. I do not assume any responsibility for anything you do in your life, including opening up dangerous lithium ion battery packs.</p>
<p>WARNING - DO NOT DO THIS AT HOME</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/acdf5ea6-img-8613.jpg" alt="" loading="lazy"></p>
<p>We start with the 4 screws at the bottom.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/000ec036-img-8615.jpg" alt="" loading="lazy"></p>
<p>Great success.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/93a82d18-img-8616.jpg" alt="" loading="lazy"></p>
<p>Then remove these 4 screws here.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/0d9f3615-img-8619.jpg" alt="" loading="lazy"></p>
<p>Here it gets complicated. There's only a tiny hole through which to operate.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/eca1a17a-img-8623.jpg" alt="" loading="lazy"></p>
<p>I also pried open the top thinking that there would be easy access to the PCB connectors, but no. It's just the cylinder lock. The two compartments are totally isolated. No need to open this up unless there's any problem with your lock. Coincidentally the tiny hole that looks like a &quot;insert paperclip to reset CMOS&quot;  hole is just a water(?) drain. There's no reset button behind it.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/5caac850-img-8628.jpg" alt="" loading="lazy"></p>
<p>I also removed the long screw, which you DO NOT NEED TO DO. It's just for the structural integrity of the battery pack and is not attached to the frame.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/78d7d4ff-img-8630.jpg" alt="" loading="lazy"></p>
<p>After a lot of prying through the tiny hole I managed to disconnect the connectors and remove the outside interface. This was the hardest part of the disassembly! It's really difficult to operate through a tiny hole and still be careful because you're near the main output terminals of the battery pack.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/5f45676f-img-8632.jpg" alt="" loading="lazy"></p>
<p>Now only the light is still attached. If you push REAL HARD through the hole you can eject the rear light which is glued down. Then gently remove the connector for the light. I didn't figure out how it worked before so I  slightly damaged some plastic of the lights. All in the name of science I guess.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/5667cece-img-8631.jpg" alt="" loading="lazy"></p>
<p>Ok now how do you get the battery pack out of the enclosure? The fit is very tight and there's no surface to apply pressure to. You can't push from the inside and you definitely DO NOT want to stick metal tools anywhere near the cells. The side with the PCB has the most clearance from the edge of the frame. I figured that by slamming the frame down onto a hard edge (like the side of a workbench), the momentum of the cells will do the work and slowly slide out. And after 4 or 5 &quot;gentle&quot; slams the pack was indeed ejected.</p>
<p>Here are some shots of the pack and the PCB.</p>
<p>The white &quot;paste&quot; covers the leads that go to the batteries. I'm not replacing any cells so I'm not touching it.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/b84ecc00-img-8678.jpg" alt="" loading="lazy"></p>
<p>There's a small push button here. Not sure what it's for.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/c2ea6c53-img-8677.jpg" alt="" loading="lazy"></p>
<p>Also an on/off switch for the entire pack.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/cf49c2cb-img-8634.jpg" alt="" loading="lazy"></p>
<p>Here's the pack.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/13115797-img-8640.jpg" alt="" loading="lazy"></p>
<p>This is where it's attached to the rear lights.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/3ce494ba-img-8641.jpg" alt="" loading="lazy"></p>
<p>And here next to the enclosure.</p>
<p>Ok now we get to what we came here for...</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/1457e9cd-img-8644.jpg" alt="" loading="lazy"></p>
<p>The rear light module in all its (broken) glory.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/12dfca9d-img-8645.jpg" alt="" loading="lazy"></p>
<p>It's easily pried open.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/9db2ae9b-img-8646.jpg" alt="" loading="lazy"></p>
<p>Alright so here we just have 8 LEDs. It seems the voltage is about 1.5V tot 2V per LED.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/09247647-img-8667.jpg" alt="" loading="lazy"></p>
<p>Now I tested my LEDs and they all still work. Hm.. Could be the rear light module or the signal from from the bike's controller.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/8f1344cf-img-8682.jpg" alt="" loading="lazy"></p>
<p>The red and black terminals of the LED module are hooked directly to the main battery terminals, so there's some power modulation from 36V down to a usable level for the LEDs. The green wire is controlled from the main PCB in the frame of the bike, the connection goes through one of the small terminals of the battery connector and then directly to the led module. The signal is a 3V PWM at 1khz. So it's probably the LED module that's broken. This is actually the first time I'm seriously using my oscilloscope and it was awesome!</p>
<p>The LEDs are wired in serial. When lighting up the first or last 7 of them with 12V it works, but when connecting all 8 of the LEDs I got a short circuit, which means that there's something wrong with my LED PCB. I suspect it's the power inductor that overheated and will try to fix it.</p>
<p>Here are some close-ups of the LED PCB.</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/12b5e1d1-yf7ar2npp-ddhhrong6-n6nzwe-mcnuedvu32ylb15dghnfon746ssep8va4.jpg" alt="" loading="lazy"></p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/df421535-pict0005.jpg" alt="" loading="lazy"></p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/348a36e8-pict0006.jpg" alt="" loading="lazy"></p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/35bb729a-pict0015.jpg" alt="" loading="lazy"></p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/97600a7a-pict0017.jpg" alt="" loading="lazy"></p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/9a736c9b-pict0018.jpg" alt="" loading="lazy"></p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/be5c8f56-pict0019.jpg" alt="" loading="lazy"></p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/4d99b03c-pict0022.jpg" alt="" loading="lazy"></p>
<p>Update: after some helpful comments on the <a href="https://www.reddit.com/r/cowboybikes/comments/18f5jl9/cowboy_battery_disassembly_and_rear_light_fix/">reddit thread</a> and chatting to user <em>icannotfindagoodname</em>, I desoldered some components and tested them in isolation. The diodes and resistors all seemed fine but the fuse (F1) showed weird  behavior. As I was seeing a short if I put a voltage across all of the LEDs I traced the circuit to see where those points connect. This turned out to be the capacitor. I shorted the fuse and put in a beefier 10 uF electrolytic capacitor I had lying around, I applied 32V and a PWM signal and voila, there was light!</p>
<p>I ordered new capacitors, a resettable fuse and a new 100uH inductor (the specs of the ZXLD1362 LED driver suggested a bigger one than the 68uH that was used. The battery voltage can apparently reach 42V.)</p>
<p>Here is what I ordered at Farnell:</p>
<p><img src="https://blog.waleson.com/images/2023/12/cowboy-c3-battery-disassembly-and-rear/5d13b870-screenshot-from-2024-01-04-08-38-35.png" alt="" loading="lazy"></p>
<p>It arrived within 2 days, pretty cool. Now I put everything in place and re-assembled the battery.</p>
<p>I put in the LED module last, so that I could reach the connectors through the hole. Then I glued it shut using hot glue. Then I noticed some small cracks in the black plastic enclosure for the LED module. This probably happened because of the force I applied when removing it. As another reddit user pointed out that his battery died in the end because some water got in that way, I put super glue on the cracks to hopefully prevent the same fate. On close inspection the glue is visible and it doesn't look as clean as before, but better than the entire battery becoming e-waste.</p>
<p>After the whole ordeal the light seems a bit more dim than before, though I might just be imagining that. It could also be because of using a slightly different resistor at R4 because I damaged it while desoldering and testing. I really need to buy some SMD soldering equipment.</p>
]]></content:encoded>
            <category>cowboy</category>
            <category>hardware</category>
            <category>how-to</category>
        </item>
    </channel>
</rss>