<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<?xml-stylesheet href="https://www.chiark.greenend.org.uk/~ryounger/assets/feed.xsl" type="text/xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Software on Ross Younger</title>
    <link>https://www.chiark.greenend.org.uk/~ryounger/software/</link>
    <description>Recent content in Software on Ross Younger</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-UK</language>
    <lastBuildDate>Tue, 14 Jul 2026 16:24:56 +1200</lastBuildDate><atom:link href="https://www.chiark.greenend.org.uk/~ryounger/software/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Lignum</title>
      <link>https://www.chiark.greenend.org.uk/~ryounger/2026/07/lignum/</link>
      <pubDate>Tue, 14 Jul 2026 16:24:56 +1200</pubDate>
      
      <guid>https://www.chiark.greenend.org.uk/~ryounger/2026/07/lignum/</guid>
      <description>&lt;p&gt;A source code repository visualiser with a 1,000&amp;rsquo; view.&lt;/p&gt;
&lt;p&gt;I wasn&amp;rsquo;t satisfied with the state of Git repository visualisers. I found they love to show you every last commit.
When your repo is more than trivial, this is not useful for getting the big-picture.&lt;/p&gt;
&lt;p&gt;Mine is different: it focusses on the macroscopic structure.
Which are the active branches, where are the tags, where are the divergence and merge points?&lt;/p&gt;
&lt;p&gt;In my first software job out of college &amp;ndash; back when dinosaurs roamed the earth and &lt;a href=&#34;https://en.wikipedia.org/wiki/Concurrent_Versions_System&#34;&gt;CVS&lt;/a&gt; was the height of version control &amp;ndash; we often ended up drawing what we termed railway diagrams on the whiteboard to visualise the structure. This is my attempt to automate creating these.&lt;/p&gt;
&lt;h2 id=&#34;features&#34;&gt;Features&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Output in GraphViz &lt;code&gt;dot&lt;/code&gt; format&lt;/li&gt;
&lt;li&gt;Revision of interest filtering, with wildcards&lt;/li&gt;
&lt;li&gt;Options to include all local branches (default), remotes, tags and root (parentless) commits&lt;/li&gt;
&lt;li&gt;Edges are annotated with their size in commits (broadly the number on the direct line from A to B, though merge commits give different results)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;getting-started&#34;&gt;Getting started&lt;/h2&gt;
&lt;p&gt;This is a Rust command-line application.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cargo install --locked lignum&lt;/code&gt;, or get it from the &lt;a href=&#34;https://github.com/crazyscot/lignum&#34;&gt;source repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Run lignum in a directory that contains a git repository, or give it &lt;code&gt;--repo &amp;lt;path&amp;gt;&lt;/code&gt;.
If you have &lt;code&gt;xdot&lt;/code&gt; installed, the &lt;code&gt;-x&lt;/code&gt; option will pipe the output directly to xdot.&lt;/p&gt;
&lt;p&gt;Check the &lt;code&gt;--help&lt;/code&gt; message for more.&lt;/p&gt;</description>
      <enclosure url="https://www.chiark.greenend.org.uk/~ryounger/images/software/lignum.png" type="image" /></item>
    
    <item>
      <title>A web toy: Forced ranking assistant</title>
      <link>https://www.chiark.greenend.org.uk/~ryounger/2025/07/a-web-toy-forced-ranking-assistant/</link>
      <pubDate>Mon, 21 Jul 2025 18:21:31 +1300</pubDate>
      
      <guid>https://www.chiark.greenend.org.uk/~ryounger/2025/07/a-web-toy-forced-ranking-assistant/</guid>
      <description>&lt;p&gt;I wanted to arrange some items in a strongly
ranked order of preference, but found it quite hard.
So I made a little web tool that tries to help.&lt;/p&gt;
&lt;p&gt;The idea? A sort of battle royale.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Consider two of the items chosen at random.
Which do you prefer? Give it one point.&lt;/li&gt;
&lt;li&gt;Repeat for every possible pairing of the items, and add up the scores.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The tool: &lt;a href=&#34;https://crazyscot.github.io/forced-rank/&#34;&gt;https://crazyscot.github.io/forced-rank/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a Javascript single-page application written in VueJS.
&lt;a href=&#34;https://github.com/crazyscot/forced-rank&#34;&gt;Source code&lt;/a&gt;&lt;/p&gt;</description>
      <enclosure url="https://www.chiark.greenend.org.uk/~ryounger/images/forced-ranker-response-matrix.png" type="image" /></item>
    
    <item>
      <title>Getting value from CI</title>
      <link>https://www.chiark.greenend.org.uk/~ryounger/2024/12/getting-value-from-ci/</link>
      <pubDate>Sun, 01 Dec 2024 10:53:36 +1300</pubDate>
      
      <guid>https://www.chiark.greenend.org.uk/~ryounger/2024/12/getting-value-from-ci/</guid>
      <description>&lt;p&gt;Many of my peers within the software world know the value of Continuous Integration and don&amp;rsquo;t need convincing.
This article is for everybody else.&lt;/p&gt;
&lt;h3 id=&#34;introduction&#34;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;In my first job out of college we had what you&amp;rsquo;d recognise as CI, though the term wasn&amp;rsquo;t so popular then. It was powerful, very useful, but a source of Byzantine complexity.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve also worked for people who didn&amp;rsquo;t think CI was worth doing because it was too expensive to set up and maintain. This is not totally unreasonable; the real question is to figure out where the value for your project might lie.&lt;/p&gt;
&lt;p&gt;Recently, a friend wrote:&lt;/p&gt;
&lt;div class=&#34;callout quote&#34;&gt;
    &lt;div class=&#34;callout-content&#34;&gt;
      &lt;strong&gt;&lt;/strong&gt;
      &lt;p&gt;
&lt;q&gt;I don&#39;t really know very much about CI. I would be interested in knowing
more and might even use some of the quick wins (...)
I do not want to become completely reliant upon GitHub for anything.&lt;/q&gt;
&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  
  
  &lt;style&gt;
     
    .callout {
      position: relative;  
      padding: 20px;
      margin: 20px 0;
      border: 2px solid;  
      border-radius: 8px;  
      font-family: Arial, sans-serif;
      background-color: #f9f9f9;
      box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
    }

     
    .callout::before {
      content: &#34;\f00c&#34;;  
      font-family: &#34;FontAwesome&#34;;
      font-weight: normal;  
      position: absolute;
      left: -40px;  
      top: 50%;
      transform: translateY(-50%);
      font-size: 24px;  
      color: inherit;  
    }

     
    .callout.info {
      background-color: #e0f7fa;
      border-color: #00acc1;
    }

    .callout.info::before {
      content: &#34;\f05a&#34;;  
    }

     
    .callout.warning {
      background-color: #fff3e0;
      border-color: #ff6f00;
    }

    .callout.warning::before {
      content: &#34;\f071&#34;;  
    }

     
    .callout.success {
      background-color: #e8f5e9;
      border-color: #43a047;
    }

    .callout.success::before {
      content: &#34;\f058&#34;;  
    }

     
    .callout.error {
      background-color: #ffebee;
      border-color: #e53935;
    }

    .callout.error::before {
      content: &#34;\f057&#34;;  
    }

     
    .callout.quote {
      background-color: #bbb;
      border-color: #666;
    }

    .callout.quote::before {
      content: &#34;\f27a&#34;;  
    }
&lt;/style&gt;

&lt;p&gt;So let&amp;rsquo;s start with a primer.&lt;/p&gt;
&lt;h2 id=&#34;terminology-what-is-ci&#34;&gt;Terminology: What is CI?&lt;/h2&gt;
&lt;p&gt;Unfortunately the term &amp;ldquo;CI&amp;rdquo; is sometimes misused and/or confused.&lt;/p&gt;
&lt;p&gt;The short answer is that it&amp;rsquo;s &lt;strong&gt;automation that regularly (continuously) does something useful with your codebase&lt;/strong&gt;. These actions might take place on every commit, nightly, or be activated by some external trigger.&lt;/p&gt;
&lt;p&gt;CI usually refers to a &lt;em&gt;spectrum&lt;/em&gt; of practices, each step building on the last:&lt;/p&gt;

     &lt;table class=&#34;table table-striped table-bordered&#34;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;text-align:right&#34;&gt;Continuous&amp;hellip;&lt;/th&gt;
&lt;th&gt;Typical activities&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right&#34;&gt;&lt;em&gt;Build&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Builds your code, usually to the &lt;em&gt;unit&lt;/em&gt; or &lt;em&gt;module&lt;/em&gt; level. Runs unit tests.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right&#34;&gt;&lt;em&gt;Integration&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Assembles modules to a &amp;ldquo;finished application&amp;rdquo;, whatever that means. Runs integration tests.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right&#34;&gt;&lt;em&gt;Test&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;A full suite of automated tests. May include regression, performance, deployability and data migration.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right&#34;&gt;&lt;em&gt;Delivery&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;When the test suite passes, the latest version of the system is automatically released to a &lt;em&gt;staging&lt;/em&gt; environment. This might involve building packages and putting them in a download area.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:right&#34;&gt;&lt;em&gt;Deployment&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;When the automated tests pass, the software automatically goes live. Hold tight!&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;/table&gt;
&lt;p&gt;Exactly what these phases mean for your project, and how far you go with them, depends on your project.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What suits my embedded firmware probably won&amp;rsquo;t suit your cloud app or that other person&amp;rsquo;s desktop app.&lt;/li&gt;
&lt;li&gt;The lines between the phases are blurry. For example, it may or may not make sense to build and integrate everything in one go.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-why-ci&#34;&gt;❔ Why CI&lt;/h2&gt;
&lt;p&gt;If deployed appropriately, CI can save time, reduce costs and improve quality.
Even on a hobby project, there is often value in saving your time.&lt;/p&gt;
&lt;h3 id=&#34;1-automating-stuff-so-the-humans-dont-have-to&#34;&gt;1. Automating stuff, so the humans don&amp;rsquo;t have to&lt;/h3&gt;
&lt;p&gt;You &lt;em&gt;could&lt;/em&gt; use your engineers to do the repetitive drudge work of creating a release across multiple platforms.
You &lt;em&gt;could&lt;/em&gt; have them run a full barrage of tests before committing a code change&amp;hellip; but &lt;em&gt;should&lt;/em&gt; you?
Engineers are expensive and generally dislike boring stuff, so the smart business move is usually to automate away the repetitive parts and have them focus where they can deliver most value.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re not sure, consider this: how much time does your team spend per release cycle on the repetitive parts? Consider your expected frequency of release cycles, that should lead you to the answer.&lt;/p&gt;
&lt;h3 id=&#34;2-automatic-analysis-and-status-reporting&#34;&gt;2. Automatic analysis and status reporting&lt;/h3&gt;
&lt;p&gt;One place I worked had a release process which relied on an engineer reading multiple megabytes of log file to see if things had been successful. Many things could go wrong and leave the final output in a plausible but half-broken state. Worse, it wasn&amp;rsquo;t as simple as running the script in &lt;em&gt;stop-on-error&lt;/em&gt; mode, because some of the steps were prone to false alarms.&lt;/p&gt;
&lt;p&gt;You may be ahead of me here, but I didn&amp;rsquo;t think much of that setup.&lt;/p&gt;
&lt;p&gt;Compilation failed? Show me the compiler output from the file that failed.&lt;/p&gt;
&lt;p&gt;A test failed? I want to see the result of that test (expected &amp;amp; observed results).&lt;/p&gt;
&lt;p&gt;Everything passed? Great, but don&amp;rsquo;t spend megabytes to convey &lt;em&gt;one single bit&lt;/em&gt; of information.&lt;/p&gt;
&lt;p&gt;At its simplest, a small project will have a single &lt;strong&gt;main&lt;/strong&gt; branch, and the operational information you need can be boiled down to a small number of states:&lt;/p&gt;

     &lt;table class=&#34;table table-striped table-bordered no-header cols-3&#34;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src=&#34;https://www.chiark.greenend.org.uk/~ryounger/images/software/stop-light-h_red.svg&#34; alt=&#34;Red traffic light&#34;&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src=&#34;https://www.chiark.greenend.org.uk/~ryounger/images/software/stop-light-h_yellow.svg&#34; alt=&#34;Yellow traffic light&#34;&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src=&#34;https://www.chiark.greenend.org.uk/~ryounger/images/software/stop-light-h_green.svg&#34; alt=&#34;Green traffic light&#34;&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Something is broken&lt;/td&gt;
&lt;td&gt;Non-critical warning &lt;em&gt;(not all projects use this)&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Everything is working&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;/table&gt;
&lt;p&gt;In a non-remote workplace it might make sense to set up some sort of status annunciator.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Some people use coloured lava lamps or similar.&lt;/li&gt;
&lt;li&gt;At one place I worked the machinery in the factory had physical traffic light (&lt;em&gt;andon&lt;/em&gt;) lamp sets. We set  one of these up, driven by a Raspberry Pi wired in to the build server.&lt;/li&gt;
&lt;li&gt;Some projects build more elaborate virtual dashboards that suit their needs. Multiple branches, multiple build configurations, whatever makes sense.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;3-improved-quality&#34;&gt;3. Improved quality&lt;/h3&gt;
&lt;p&gt;This one might be self-evident, but I&amp;rsquo;ll spell it out anyway.&lt;/p&gt;
&lt;p&gt;A good CI system will let you incorporate tests of many different types, with variable pass/fail criteria.
Think beyond unit and integration testing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Regression (check that your bugs stay fixed)&lt;/li&gt;
&lt;li&gt;Code quality (code/test coverage analysis; static analysis; dynamic memory leak analysis; automated code style checks)&lt;/li&gt;
&lt;li&gt;Security analysis (are there any known issues in your dependencies?)&lt;/li&gt;
&lt;li&gt;License/SBOM compliance&lt;/li&gt;
&lt;li&gt;Fuzz testing (how does it handle randomised, unexpected inputs?)&lt;/li&gt;
&lt;li&gt;Performance requirements&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Early warning&amp;rdquo; performance canaries&lt;/li&gt;
&lt;li&gt;Standards compliance&lt;/li&gt;
&lt;li&gt;System data migration&lt;/li&gt;
&lt;li&gt;On-device testing (might be real, emulated or simulated hardware)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;callout info&#34;&gt;
    &lt;div class=&#34;callout-content&#34;&gt;
      &lt;strong&gt;Performance canaries&lt;/strong&gt;
      &lt;p&gt;
Particularly where physical devices are involved, you might have a performance margin built in to your hardware spec. As the project evolves, inevitably new features will erode this margin.
When you run out things are going to go wrong, so you want to take action before you get there.
&lt;/p&gt;&lt;p&gt;
An early warning canary is some sort of metric with a threshold. Examples might include free memory, CPU/MCU consumption, or task processing time.
When the threshold is passed, that&#39;s a sign that things are getting tight and it&#39;s time to take pre-emptive action. You might plan to spend some time on algorithmic optimisations, or to kick off a new hardware spin.
&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  
  
  &lt;style&gt;
     
    .callout {
      position: relative;  
      padding: 20px;
      margin: 20px 0;
      border: 2px solid;  
      border-radius: 8px;  
      font-family: Arial, sans-serif;
      background-color: #f9f9f9;
      box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
    }

     
    .callout::before {
      content: &#34;\f00c&#34;;  
      font-family: &#34;FontAwesome&#34;;
      font-weight: normal;  
      position: absolute;
      left: -40px;  
      top: 50%;
      transform: translateY(-50%);
      font-size: 24px;  
      color: inherit;  
    }

     
    .callout.info {
      background-color: #e0f7fa;
      border-color: #00acc1;
    }

    .callout.info::before {
      content: &#34;\f05a&#34;;  
    }

     
    .callout.warning {
      background-color: #fff3e0;
      border-color: #ff6f00;
    }

    .callout.warning::before {
      content: &#34;\f071&#34;;  
    }

     
    .callout.success {
      background-color: #e8f5e9;
      border-color: #43a047;
    }

    .callout.success::before {
      content: &#34;\f058&#34;;  
    }

     
    .callout.error {
      background-color: #ffebee;
      border-color: #e53935;
    }

    .callout.error::before {
      content: &#34;\f057&#34;;  
    }

     
    .callout.quote {
      background-color: #bbb;
      border-color: #666;
    }

    .callout.quote::before {
      content: &#34;\f27a&#34;;  
    }
&lt;/style&gt;

&lt;p&gt;If you can automate a really robust set of tests, you can have a lot of confidence in the state of your code at any given time. This gives incredible agility: &lt;strong&gt;you can release at any time, if the tests pass&lt;/strong&gt;. This is the key to moving quickly, and is how a number of tech companies operate.&lt;/p&gt;
&lt;p&gt;For a success story involving physical devices, check out &lt;a href=&#34;https://itrevolution.com/articles/the-amazing-devops-transformation-of-the-hp-laserjet-firmware-team-gary-gruver/&#34;&gt;the HP LaserJet team&amp;rsquo;s DevOps transformation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;4-reduced-time-to-resolve-issues&#34;&gt;4. Reduced time to resolve issues&lt;/h3&gt;
&lt;p&gt;If there&amp;rsquo;s one thing I&amp;rsquo;ve learned in the software business, it&amp;rsquo;s that it&amp;rsquo;s cheaper to find bugs closer to development - &lt;em&gt;by orders of magnitude&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In other words, &lt;em&gt;reduce the feedback cycle to reduce your costs&lt;/em&gt;.
This is where automated tests and checks have great value.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If there is something wrong in code I modified a minute ago, I&amp;rsquo;m still in the right headspace and can usually fix it pretty quickly.&lt;/li&gt;
&lt;li&gt;If it takes a few days to get a test result, I won&amp;rsquo;t remember all the detail and will have to refresh my memory.&lt;/li&gt;
&lt;li&gt;If it takes several months to hear that something&amp;rsquo;s wrong, I may be working on a totally different part of the system and it will take longer to context switch.&lt;/li&gt;
&lt;li&gt;If a bug report comes in from the field a year or two later, I might as well be starting again from scratch.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But - as ever - engineering is a trade-off. You can&amp;rsquo;t write a test to catch a bug you haven&amp;rsquo;t foreseen. It may be prohibitively expensive to test all possible combinations before release.&lt;/p&gt;
&lt;h2 id=&#34;-why-not-ci&#34;&gt;❌ Why not CI&lt;/h2&gt;
&lt;p&gt;CI is not suitable for all software projects.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re writing a scratch throw-away project that won&amp;rsquo;t live for very long, even simple CI may not be worth it.&lt;/p&gt;
&lt;p&gt;If you have a legacy codebase that was written without testing in mind, it might be prohibitively expensive to refactor to set these up. Nevertheless, in such projects there is often still some value to be found in a continuous build.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s be pragmatic.&lt;/p&gt;
&lt;h3 id=&#34;tests-arent-everything&#34;&gt;Tests aren&amp;rsquo;t everything&lt;/h3&gt;
&lt;p&gt;On the face of it, more testing means greater quality, right? Well&amp;hellip; &lt;em&gt;maybe&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;Keep the end goal in sight.
It&amp;rsquo;s up to you to decide what makes sense for your situation; I recommend taking a &lt;em&gt;whole-of-organisation view&lt;/em&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You need to balance test runtime against overall feedback cycles. If the tests take too long to run, you&amp;rsquo;re slowing people down.&lt;/li&gt;
&lt;li&gt;Some tests are expensive in terms of time or consuming resources, so you might not want to run them daily.&lt;/li&gt;
&lt;li&gt;Tests involving physical devices can be difficult to automate, and risk creating a process bottleneck. (Consider emulation and/or simulation where appropriate.)&lt;/li&gt;
&lt;li&gt;Beware of over-testing; you may not need to exhaustively check all the combinations. Statistical techniques might help you out here.&lt;/li&gt;
&lt;li&gt;Beware of making your black-box tests too strict; this can lead to brittle tests that are more hassle to maintain than they are worth.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;costs-and-maintenance&#34;&gt;Costs and maintenance&lt;/h3&gt;
&lt;p&gt;It will take time and effort to set up CI. How much time and effort, I can&amp;rsquo;t say.&lt;/p&gt;
&lt;p&gt;In times past, CI was quite the bespoke effort.&lt;/p&gt;
&lt;p&gt;These days there is good tooling support for many environments, so it is usually pretty quick to get something going. From there you can decide how far to go.&lt;/p&gt;
&lt;h3 id=&#34;it-might-be-too-big-for-your-platform&#34;&gt;It might be too big for your platform&lt;/h3&gt;
&lt;p&gt;CI platforms are designed for small, lightweight processes. Think seconds to minutes, not hours.&lt;/p&gt;
&lt;p&gt;If you need to build a large application or a full Yocto firmware image, it&amp;rsquo;s going to be tough to make that fit within the limits of a cloud-hosted CI platform. Don&amp;rsquo;t despair! There are ways out, but you need to be smart. Alternative options include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;self-hosting CI runners that are take part in a cloud source repository;&lt;/li&gt;
&lt;li&gt;self-hosting the CI environment (e.g. Gitlab, Jenkins, CircleCI), noting that most source code hosting platforms have integrations;&lt;/li&gt;
&lt;li&gt;split up the task into multiple smaller CI jobs making good use of &lt;em&gt;artefacts&lt;/em&gt; between stages;&lt;/li&gt;
&lt;li&gt;reconsidering what is truly worth automating anyway.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-steps-you-can-take&#34;&gt;👷 Steps you can take&lt;/h2&gt;
&lt;h3 id=&#34;1-build-your-units&#34;&gt;1. Build your units&lt;/h3&gt;
&lt;p&gt;In most projects you already had to set up a buildsystem. Automating this is usually pretty cheap though you will need to get the tooling right.&lt;/p&gt;
&lt;div class=&#34;callout info&#34;&gt;
    &lt;div class=&#34;callout-content&#34;&gt;
      &lt;strong&gt;Tooling on cloud platforms&lt;/strong&gt;
      &lt;p&gt;
On-cloud CI (as provided by Github, Gitlab, Bitbucket and others) is generally containerised.
What this means is that your project has to know how to install its own tooling, starting with a minimal (usually Linux) container image.
&lt;/p&gt;&lt;p&gt;
&lt;i&gt;This is really good practice!&lt;/i&gt;
Doing so means your required tools are themselves expressed in source code under revision control.
&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  
  
  &lt;style&gt;
     
    .callout {
      position: relative;  
      padding: 20px;
      margin: 20px 0;
      border: 2px solid;  
      border-radius: 8px;  
      font-family: Arial, sans-serif;
      background-color: #f9f9f9;
      box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
    }

     
    .callout::before {
      content: &#34;\f00c&#34;;  
      font-family: &#34;FontAwesome&#34;;
      font-weight: normal;  
      position: absolute;
      left: -40px;  
      top: 50%;
      transform: translateY(-50%);
      font-size: 24px;  
      color: inherit;  
    }

     
    .callout.info {
      background-color: #e0f7fa;
      border-color: #00acc1;
    }

    .callout.info::before {
      content: &#34;\f05a&#34;;  
    }

     
    .callout.warning {
      background-color: #fff3e0;
      border-color: #ff6f00;
    }

    .callout.warning::before {
      content: &#34;\f071&#34;;  
    }

     
    .callout.success {
      background-color: #e8f5e9;
      border-color: #43a047;
    }

    .callout.success::before {
      content: &#34;\f058&#34;;  
    }

     
    .callout.error {
      background-color: #ffebee;
      border-color: #e53935;
    }

    .callout.error::before {
      content: &#34;\f057&#34;;  
    }

     
    .callout.quote {
      background-color: #bbb;
      border-color: #666;
    }

    .callout.quote::before {
      content: &#34;\f27a&#34;;  
    }
&lt;/style&gt;

&lt;p&gt;Where this might get tricky is if you have multiple build configurations (platforms or builds with different features).
Don&amp;rsquo;t be surprised if automating reveals shortcomings in your setup.&lt;/p&gt;
&lt;p&gt;If you have autogenerated documentation, consider running that too. (In Rust, for example, it could be as easy as adding a &lt;code&gt;cargo doc&lt;/code&gt; step.)&lt;/p&gt;
&lt;h3 id=&#34;2-test-your-units&#34;&gt;2. Test your units&lt;/h3&gt;
&lt;p&gt;Adding unit tests to CI is usually pretty cheap though it will depend on the language and available test frameworks.&lt;/p&gt;
&lt;p&gt;If you want to include language-intrinsic checks (e.g. code style, static analysis) this is a good time to build them in. Some analyses can be quite expensive so it may not make sense to run all the checks at the same frequency.&lt;/p&gt;
&lt;h3 id=&#34;3-integrate-it&#34;&gt;3. Integrate it&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;re pulling multiple component parts (microservices, standalone executables) together to make an end result, that&amp;rsquo;s the next step. Do they play nicely? Do you want to run any isolated tests among them before you move to delivery-level tests?&lt;/p&gt;
&lt;h3 id=&#34;4-add-more-checks&#34;&gt;4. Add more checks&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;#3-improved-quality&#34;&gt;I spoke about these above&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is where things stop being cheap and you have to start thinking about building out supporting infrastructure.&lt;/p&gt;
&lt;h3 id=&#34;5-deliver-it&#34;&gt;5. Deliver it&lt;/h3&gt;
&lt;p&gt;Now we&amp;rsquo;re getting quite situation-specific. Think about what it means to deliver your project.&lt;/p&gt;
&lt;p&gt;Are you building a package for an ecosystem (Rust crate / Python pypi / npm.js / &amp;hellip;) ?
You might be able to automate the packaging steps and that might be pretty cheap.&lt;/p&gt;
&lt;p&gt;Are you building an application? Perhaps you can automate the process of building the installer / container / whatever shape it takes.
If you have multiple build configurations or platforms, it could get very tedious to build them all by hand and there is often a win for automation.&lt;/p&gt;
&lt;div class=&#34;callout warning&#34;&gt;
    &lt;div class=&#34;callout-content&#34;&gt;
      &lt;strong&gt;&lt;/strong&gt;
      &lt;p&gt;
Where there&#39;s code signing involved, you&#39;ll need to decide whether it makes sense to automate that or leave it as a manual release step. Never put private keys or other code signing secrets directly into source!
Some platforms have secrets mechanisms that may be of use, but it pays to be cautious. If your secrets leak, how will you repair the situation?
&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  
  
  &lt;style&gt;
     
    .callout {
      position: relative;  
      padding: 20px;
      margin: 20px 0;
      border: 2px solid;  
      border-radius: 8px;  
      font-family: Arial, sans-serif;
      background-color: #f9f9f9;
      box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
    }

     
    .callout::before {
      content: &#34;\f00c&#34;;  
      font-family: &#34;FontAwesome&#34;;
      font-weight: normal;  
      position: absolute;
      left: -40px;  
      top: 50%;
      transform: translateY(-50%);
      font-size: 24px;  
      color: inherit;  
    }

     
    .callout.info {
      background-color: #e0f7fa;
      border-color: #00acc1;
    }

    .callout.info::before {
      content: &#34;\f05a&#34;;  
    }

     
    .callout.warning {
      background-color: #fff3e0;
      border-color: #ff6f00;
    }

    .callout.warning::before {
      content: &#34;\f071&#34;;  
    }

     
    .callout.success {
      background-color: #e8f5e9;
      border-color: #43a047;
    }

    .callout.success::before {
      content: &#34;\f058&#34;;  
    }

     
    .callout.error {
      background-color: #ffebee;
      border-color: #e53935;
    }

    .callout.error::before {
      content: &#34;\f057&#34;;  
    }

     
    .callout.quote {
      background-color: #bbb;
      border-color: #666;
    }

    .callout.quote::before {
      content: &#34;\f27a&#34;;  
    }
&lt;/style&gt;

&lt;h2 id=&#34;closing-thoughts&#34;&gt;Closing thoughts&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Most projects will benefit from a little CI. You don&amp;rsquo;t need to have unit tests, though they are a good idea.&lt;/li&gt;
&lt;li&gt;You&amp;rsquo;re going to have to maintain your CI, so build it for maintainability like you do your software.&lt;/li&gt;
&lt;li&gt;Apply agile to your CI as you do to your deliverables. Perfect is the enemy of good enough.
Build something, get feedback, iterate!&lt;/li&gt;
&lt;li&gt;CI vendors want to lock you in to their platform. Keep your eyes open.&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t let CI become an all-consuming monster that prevents you from delivering in the first place!&lt;/li&gt;
&lt;/ul&gt;</description>
      <enclosure url="https://www.chiark.greenend.org.uk/~ryounger/images/software/code-crane.jpg" type="image" /></item>
    
    <item>
      <title>Announcing qcp</title>
      <link>https://www.chiark.greenend.org.uk/~ryounger/2024/11/announcing-qcp/</link>
      <pubDate>Sun, 03 Nov 2024 10:23:43 +1300</pubDate>
      
      <guid>https://www.chiark.greenend.org.uk/~ryounger/2024/11/announcing-qcp/</guid>
      <description>&lt;p&gt;The QUIC Copier (&lt;code&gt;qcp&lt;/code&gt;) is an experimental high-performance remote file copy utility for long-distance internet connections.&lt;/p&gt;
&lt;p&gt;Source repository: &lt;a href=&#34;https://github.com/crazyscot/qcp&#34;&gt;https://github.com/crazyscot/qcp&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;-features&#34;&gt;📋 Features&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;🔧 Drop-in replacement for &lt;code&gt;scp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;🛡️ Similar security to &lt;code&gt;scp&lt;/code&gt;, using existing, well-known mechanisms&lt;/li&gt;
&lt;li&gt;🚀 Better throughput on congested networks&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-about-qcp&#34;&gt;📖 About qcp&lt;/h2&gt;
&lt;p&gt;qcp is a &lt;em&gt;hybrid protocol&lt;/em&gt; combining ssh and &lt;a href=&#34;https://quicwg.github.io/&#34;&gt;QUIC&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We use ssh to establish a control channel to the target machine, then spin up the QUIC protocol to transfer data.&lt;/p&gt;
&lt;p&gt;This has the following useful properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;User authentication is handled entirely by ssh&lt;/li&gt;
&lt;li&gt;Data is transmitted over UDP, avoiding known issues with TCP over &amp;ldquo;long, fat pipe&amp;rdquo; connections&lt;/li&gt;
&lt;li&gt;Data in transit is protected by TLS using ephemeral keys&lt;/li&gt;
&lt;li&gt;The security mechanisms all use existing, well-known cryptographic algorithms&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For full documentation refer to &lt;a href=&#34;https://docs.rs/qcp/latest/qcp/&#34;&gt;qcp on docs.rs&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;motivation&#34;&gt;Motivation&lt;/h3&gt;
&lt;p&gt;I needed to copy multiple large (3+ GB) files from a server in Europe to my home in New Zealand.&lt;/p&gt;
&lt;p&gt;I’ve got nothing against ssh or scp. They’re brilliant. I’ve been using them since the 1990s. However they run on top of TCP, which does not perform very well when the network is congested. With a fast fibre internet connection, a long round-trip time and noticeable packet loss, I was right in the sour spot. TCP did its thing and slowed down, but when the congestion cleared it was very slow to get back up to speed.&lt;/p&gt;
&lt;p&gt;If you’ve ever been frustrated by download performance from distant websites, you might have been experiencing this same issue. Friends with satellite (pre-Starlink) internet connections seem to be particularly badly affected.&lt;/p&gt;
&lt;h2 id=&#34;-getting-qcp&#34;&gt;💻 Getting qcp&lt;/h2&gt;
&lt;p&gt;The project is a Rust binary &lt;a href=&#34;https://crates.io/crates/qcp&#34;&gt;crate&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can install it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;as a Debian package or pre-compiled binary from the &lt;a href=&#34;https://github.com/crazyscot/qcp/releases/latest&#34;&gt;latest qcp release&lt;/a&gt; page (N.B. the Linux builds are static musl binaries);&lt;/li&gt;
&lt;li&gt;with &lt;code&gt;cargo install qcp&lt;/code&gt; (you will need to have a rust toolchain &lt;del&gt;and capnpc&lt;/del&gt; installed) &lt;em&gt;(the capnp dependency was removed later)&lt;/em&gt;;&lt;/li&gt;
&lt;li&gt;by cloning and building the &lt;a href=&#34;https://github.com/crazyscot/qcp&#34;&gt;source repository&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You will need to install qcp on both machines. Please refer to the &lt;a href=&#34;https://github.com/crazyscot/qcp/blob/main/README.md&#34;&gt;README&lt;/a&gt; for more.&lt;/p&gt;
&lt;h3 id=&#34;see-also&#34;&gt;See also&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.rfc-editor.org/rfc/rfc9000.html&#34;&gt;RFC9000 &amp;ldquo;QUIC: A UDP-Based Multiplexed and Secure Transport&amp;rdquo;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <enclosure url="https://www.chiark.greenend.org.uk/~ryounger/images/software/qcp-in-progress.png" type="image" /></item>
    
    <item>
      <title>Broadcast graphics for fencing</title>
      <link>https://www.chiark.greenend.org.uk/~ryounger/2024/08/broadcast-graphics-for-fencing/</link>
      <pubDate>Thu, 15 Aug 2024 21:16:22 +1200</pubDate>
      
      <guid>https://www.chiark.greenend.org.uk/~ryounger/2024/08/broadcast-graphics-for-fencing/</guid>
      <description>&lt;p&gt;I created a TV graphics package for fencing tournaments.&lt;/p&gt;
&lt;p&gt;Earlier this year, Christchurch played host to the Commonwealth Junior &amp;amp; Cadet fencing tournament.&lt;/p&gt;
&lt;p&gt;Selected parts of the tournament were livestreamed, with a package broadcast on Sky TV (NZ).
The broadcast and finals streams had a live graphics package fed from
the scoreboard.&lt;/p&gt;
&lt;p&gt;The programmes were produced using a broadcast-spec OB truck supplied by &lt;a href=&#34;https://kiwiob.tv/&#34;&gt;Kiwi Outside Broadcast&lt;/a&gt;.
The truck graphics PC used &lt;em&gt;Captivate&lt;/em&gt; to generate its graphics, which output as key+fill SDI signals. These were fed to the vision mixer and keyed onto the picture in the usual way.&lt;/p&gt;
&lt;p&gt;The package can be seen in action on the &lt;a href=&#34;https://youtu.be/n4kEKuf7Mf4?si=Nh3CHiz_-WjDBoPB&amp;amp;t=356&#34;&gt;Commonwealth Junior &amp;amp; Cadet 2024 programmes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The details read from the scoreboard cover the &amp;ldquo;hit&amp;rdquo; lamps, scores, clock (including fractional seconds in the last 10s), period, red/yellow cards, and the priority indicator. On top of that, the package provides a place to enter the fencer names and nationalities, and set colours for them.&lt;/p&gt;
&lt;p&gt;This is all made possible by the scoreboard, a Favero FA-07, offering a data feed over an RS-422 interface.
I wrote a Python script to parse the data feed, turn it into a JSON dictionary and pass on to Captivate.&lt;/p&gt;</description>
      <enclosure url="https://www.chiark.greenend.org.uk/~ryounger/images/software/fencing-fa07.nbtlproj.jpg" type="image" /></item>
    
    <item>
      <title>Fault-finding at the ends of the earth</title>
      <link>https://www.chiark.greenend.org.uk/~ryounger/2024/04/fault-finding-at-the-ends-of-the-earth/</link>
      <pubDate>Mon, 29 Apr 2024 13:29:43 +1200</pubDate>
      
      <guid>https://www.chiark.greenend.org.uk/~ryounger/2024/04/fault-finding-at-the-ends-of-the-earth/</guid>
      <description>&lt;p&gt;This is a tale from many months ago, working on an embedded ARM target.&lt;/p&gt;
&lt;p&gt;In my private journal I wrote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Today I feel like I saddled up and rode my horse to the literal ends of the earth. I was fault-finding in the setting-up-of-the-universe that happens before your program starts up, and in the tearing-it-down-again that happens after you declare you&amp;rsquo;re finished.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you know C++, you might guess that this was a story about static object allocation and deallocation. You&amp;rsquo;d be right. So, destructors belonging to static-allocated objects. You&amp;rsquo;d never think they&amp;rsquo;d run on a bare-metal embedded target.&lt;/p&gt;
&lt;p&gt;Well, they can. If your target supports &lt;code&gt;exit()&lt;/code&gt; - e.g. if you are running with newlib - then an &lt;code&gt;atexit&lt;/code&gt; handler is set up for you, and that will be set up to run the static destructors. If your program then calls &lt;code&gt;exit()&lt;/code&gt; (as, say, your on-silicon unit tests might, at the end of a test run) then things are at risk of turning to custard.&lt;/p&gt;
&lt;p&gt;You might have enabled an interrupt for some peripheral on the silicon. In order to do anything really useful, the ISR might reference a static object. If you do this, you&amp;rsquo;d damn well better make sure the object has a static destructor that disables the interrupt, or hilarity is one day going to ensue. You know, the sort of hilarity that involves being savaged by a horde of angry rampaging badgers, or your socks catching fire.&lt;/p&gt;
&lt;p&gt;But wait, I hear you say, it called exit! The program no longer exists! Well, sure it doesn&amp;rsquo;t; but &lt;em&gt;what happens on exit&lt;/em&gt;? On this particular ARM target, running tests via a debugger as part of a CI chain, when the atexit handlers have run the process signals final completion with a semihosting call, which is a special flavour of debug breakpoint. It is&amp;hellip; &lt;em&gt;not fast&lt;/em&gt;. If your interrupt happens regularly, the goblins are going to get you before the pseudo-system-call completes. &lt;strong&gt;Your test framework will fail the test executable for hanging, despite somehow passing all of its tests.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There was an actual bug in there, and it was mine. Class X, which contained an RTOS queue and enabled an interrupt, only had a default destructor. On exit, somewhen between static destructors and completion of the semihosting exit call, the ISR fired. It duly failed to insert an item into the now-destroyed queue, so jumped to the internal panic routine. That routine contained a breakpoint and then went nowhere fast, waiting for a debugger command that was never going to arrive &amp;mdash; hence the time-out. Maybe it would have been useful to have a library option to skip the static destructors, but I probably wouldn&amp;rsquo;t have been aware of it ahead of time anyway.&lt;/p&gt;
&lt;p&gt;The static destructor ordering fiasco can also be yours for the taking, but thankfully that hadn&amp;rsquo;t bitten me. Nevertheless, it was a rough day.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Cover image: &lt;a href=&#34;https://www.freepik.com/free-vector/cyber-bug-search_81409647.htm&#34;&gt;Cyber Bug Search, by juicy_fish on Freepik&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description>
      <enclosure url="https://www.chiark.greenend.org.uk/~ryounger/images/software/bug-search-by-juicy_fish-on-freepik.jpg" type="image" /></item>
    
    <item>
      <title>Faster C&#43;&#43; code style checks: detail</title>
      <link>https://www.chiark.greenend.org.uk/~ryounger/2023/08/faster-c-code-style-checks-detail/</link>
      <pubDate>Thu, 17 Aug 2023 22:04:01 +1200</pubDate>
      
      <guid>https://www.chiark.greenend.org.uk/~ryounger/2023/08/faster-c-code-style-checks-detail/</guid>
      <description>&lt;p&gt;&lt;code&gt;xargs&lt;/code&gt;, &lt;code&gt;nproc&lt;/code&gt; and shell arrays, oh my!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is a companion article to &lt;a href=&#34;../developer-tooling-and-boiled-frogs/&#34;&gt;Developer tooling and boiled frogs&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&#34;before-47-seconds-&#34;&gt;Before: 4.7 seconds 💩&lt;/h2&gt;
&lt;p&gt;In this codebase, style checks were performed by clang-format with an in-house style file.
They ran in a pre-commit hook (as well as in CI). However, &lt;strong&gt;the pre-commit hook is critical for a good DX&lt;/strong&gt;; 5 seconds really sucks.&lt;/p&gt;
&lt;p&gt;The entry point to the check was a shell script which invoked clang-format, amongst other things. Like all such scripts, it was not truly original, being assembled from snippets and ideas found around the internet. Here&amp;rsquo;s the gist of it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;check_files&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# arguments are filenames. if any failed, returns 1; if all passed, returns 0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    err&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; file in $@; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ext&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;file##*.&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$ext&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            c|cpp|h|hpp&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ! clang-format -Werror -style&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;file -n &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$file&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; ; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    err&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;code style check failed for &lt;/span&gt;$file&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            *&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#75715e&#34;&gt;# unsupported file type; ignore&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; $err
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;check_files &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;git ls-files&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Do you see the problems?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;It&amp;rsquo;s single-threaded!&lt;/em&gt; Almost all desktop and laptop CPUs have had multiple cores for 15 or more years, so this is truly Dark Ages style processing.&lt;/li&gt;
&lt;li&gt;We&amp;rsquo;re invoking &lt;code&gt;clang-format&lt;/code&gt; far more times than we need to. Every invocation has to parse the config file, which is wasteful.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;after-021-seconds-&#34;&gt;After: 0.21 seconds 🚀&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;check_files&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# arguments are filenames. if any failed, returns 1; if all passed, returns 0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    FILES&lt;span style=&#34;color:#f92672&#34;&gt;=()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; file in $@; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ext&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;file##*.&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$ext&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        c|cpp|h|hpp&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          FILES&lt;span style=&#34;color:#f92672&#34;&gt;+=(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$file&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        *&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#75715e&#34;&gt;# unsupported file type; ignore&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    FILES_COUNT&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${#&lt;/span&gt;FILES[@]&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    CORES&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;nproc&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# CORES+3 gave best performance for me. YMMV.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    JOB_SIZE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$((&lt;/span&gt;FILES_COUNT &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;CORES+3&lt;span style=&#34;color:#66d9ef&#34;&gt;))&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;JOB_SIZE&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt; JOB_SIZE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;1; &lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;FILES[@]&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; | xargs -P &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; -n &lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;JOB_SIZE&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt; clang-format -Werror -style&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;file -n &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s a bit to unpack here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We&amp;rsquo;ve moved from &lt;code&gt;sh&lt;/code&gt; to &lt;code&gt;bash&lt;/code&gt; to take advantage of &lt;a href=&#34;https://opensource.com/article/18/5/you-dont-know-bash-intro-bash-arrays&#34;&gt;array variables&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Rather than immediately running &lt;code&gt;clang-format&lt;/code&gt; on every file that passes the filter, add them to the &lt;code&gt;FILES&lt;/code&gt; array so we can use xargs. (Yes, I know this could be better structured. This was but a single step of evolution; &lt;em&gt;first, make it work&lt;/em&gt;.)&lt;/li&gt;
&lt;li&gt;We use &lt;code&gt;xargs&lt;/code&gt; in &lt;em&gt;parallel mode&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We&amp;rsquo;ve naively assumed that checking each file takes roughly the same amount of time. This isn&amp;rsquo;t quite true (I found about a factor of 3 variation amongst them) but the performance gains were good enough that it doesn&amp;rsquo;t seem worth worrying about at the present time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The key here is combining &lt;code&gt;-n $JOB_SIZE&lt;/code&gt; with &lt;code&gt;-P 0&lt;/code&gt; (unlimited parallelisation) and doing some shell arithmetic to figure out what that job size should be.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;xargs&lt;/code&gt; with neither option runs a single &lt;code&gt;clang-format&lt;/code&gt; with all the input files in a single invocation.
&lt;ul&gt;
&lt;li&gt;This is &lt;em&gt;good&lt;/em&gt; (about a 6x speedup) but we can do better! It is still single-threaded, iterating through the files in order.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;xargs -P $(nproc)&lt;/code&gt; sounds interesting but is no better on its own. The man page says it all:
&lt;blockquote&gt;
&lt;p&gt;Use the  -n  option  or  the -L option with -P; otherwise chances are that only one exec will be done.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;ve counted the files, divided by (slightly more than) the number of processor cores available, and told xargs to parcel the files out in jobs of that size. Of course, if you&amp;rsquo;re doing this yourself, you&amp;rsquo;ll want to repeat the experiment to figure out the sweet spot for your hardware and your codebase.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;🚅 Final speedup for this activity: 22.3x 🚀&lt;/strong&gt;
(timings as measured on my laptop for that codebase on that day; your mileage may vary, may contain nuts, etc.)&lt;/p&gt;</description>
      <enclosure url="https://www.chiark.greenend.org.uk/~ryounger/images/software/code.jpg" type="image" /></item>
    
    <item>
      <title>Developer tooling and boiled frogs</title>
      <link>https://www.chiark.greenend.org.uk/~ryounger/2023/08/developer-tooling-and-boiled-frogs/</link>
      <pubDate>Thu, 17 Aug 2023 22:04:00 +1200</pubDate>
      
      <guid>https://www.chiark.greenend.org.uk/~ryounger/2023/08/developer-tooling-and-boiled-frogs/</guid>
      <description>&lt;p&gt;This is an article about software tooling and the developer experience.&lt;/p&gt;
&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;Right now I am spending most of my work time on an embedded project.
It&amp;rsquo;s C++ running on bare-metal target hardware. I&amp;rsquo;ve got all of 768K of RAM to play with, but that&amp;rsquo;s still a tad more than my first home micro had.&lt;/p&gt;
&lt;p&gt;Our tooling and infrastructure grew mostly organically.
I was the one who initialised the git repo.&lt;/p&gt;
&lt;h3 id=&#34;ci-chain-&#34;&gt;CI chain 🔗&lt;/h3&gt;
&lt;p&gt;We have a CI chain, of course. It has grown over time and does a few tricks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;build for the target hardware (various targets; RAM and flash builds)&lt;/li&gt;
&lt;li&gt;build on Linux native port and run unit tests&lt;/li&gt;
&lt;li&gt;perform various analyses (clang-tidy, sonarcloud, code coverage, valgrind)&lt;/li&gt;
&lt;li&gt;code style enforcement (clang-format) 👮&lt;/li&gt;
&lt;li&gt;prepare a signed upgrade image with the development signing key&lt;/li&gt;
&lt;li&gt;assemble a whole-of-flash image for the factory 🏭&lt;/li&gt;
&lt;li&gt;build and run some unit tests on a development board&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The dev board is on-premises but the rest happens in the cloud, taking a matter of minutes on every push.
It&amp;rsquo;s reasonably slick.&lt;/p&gt;
&lt;p&gt;You can run all the build and analysis steps locally.
We have a script that spins up a container emulating our CI provider so you don&amp;rsquo;t even need to have the dev tools installed, you only need to pull our in-house container image.&lt;/p&gt;
&lt;h2 id=&#34;how-much-effort-to-spend-on-tooling-&#34;&gt;How much effort to spend on tooling? 💸&lt;/h2&gt;
&lt;p&gt;Many a savvy manager or product owner will tell you to not spend too much time on tooling.
They have a point; time spent working on tooling and infra is not delivering features, after all.
Many managers have been bitten by infrastructure blow-outs and would rather we focus on building the stuff they want us to build.&lt;/p&gt;
&lt;p&gt;But here&amp;rsquo;s the thing.
When they say that, they don&amp;rsquo;t mean &amp;ldquo;forget about tooling and never speak of it again&amp;rdquo;.
You are allowed to bring the subject up again in future, and you are allowed to try to educate them as to the value.&lt;/p&gt;
&lt;h3 id=&#34;the-frog-boils-&#34;&gt;The frog boils 📉&lt;/h3&gt;
&lt;p&gt;We now have over 300 source files that get compiled into the image, which includes some vendor and autogenerated code.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s the rub. We didn&amp;rsquo;t start out with anywhere near that many. The CI chain used to be really fast, under one minute.
Local builds were fast, and even running the clang-tidy script wasn&amp;rsquo;t that awful (though I did build a cache for it, which helped).&lt;/p&gt;
&lt;p&gt;The size and complexity grew over time, and here we are today.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s like the old tale about boiling frogs.
&lt;em&gt;(Disclaimer: I don&amp;rsquo;t even know if this is true, and I definitely don&amp;rsquo;t suggest you try it, but it&amp;rsquo;s an interesting thought experiment.)&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What happens if you drop a frog into a pot of boiling water?&lt;/p&gt;
&lt;p&gt;The frog doesn&amp;rsquo;t like it and jumps out.&lt;/p&gt;
&lt;p&gt;But what happens if you drop a frog into a pot of cold water, then light the stove?
The frog doesn&amp;rsquo;t realise anything is wrong. It boils to death before it thinks to jump out.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Builds with a cold cache were taking a couple of minutes locally, and about 8 minutes in the cloud. (This happens depressingly often when you change a low-level header that is included all over the place&amp;hellip; there&amp;rsquo;s another observation, that maybe our architecture needs work, but that&amp;rsquo;s for another day.)&lt;/p&gt;
&lt;p&gt;A full static analysis with cold cache was taking over 5 minutes locally, and 22 in the cloud. (The difference is because our company-provided laptops have 16 cores, while our CI provider runs our container with 4 vCPU cores and tightly limited RAM.)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Goodness me, 300 compile units now?&lt;/em&gt;
&lt;em&gt;This water sure is getting warm!&lt;/em&gt; 😰&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&amp;rsquo;s a &lt;strong&gt;poor developer experience&lt;/strong&gt;. Even running the code style checker script takes five whole seconds. That&amp;rsquo;s 5 seconds in which you can get distracted, pop over to Slack, and whoops! ten minutes later, you&amp;rsquo;re trying to remember what it was you were doing.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been in this business long enough to know that keeping myself and my team focussed is vitally important to getting things done and keeping the stakeholders happy.
An efficient DX is part of that puzzle.&lt;/p&gt;
&lt;p&gt;Like the mythical frog, I don&amp;rsquo;t remember noticing these tasks growing longer.&lt;/p&gt;
&lt;h2 id=&#34;a-concrete-walk-through-&#34;&gt;A concrete walk-through 🔧&lt;/h2&gt;
&lt;p&gt;OK, so you&amp;rsquo;ve decided you need to jump out of the pan and spend some time sharpening your tools. Great.&lt;/p&gt;
&lt;p&gt;The most important advice I can offer is to &lt;em&gt;understand where the pain points are&lt;/em&gt;. 🔍&lt;/p&gt;
&lt;p&gt;If this is stuff you work with every day, you maybe already know where the pain points are but are blind to them because you haven&amp;rsquo;t noticed how poor the DX has become. Open your eyes, and you may find it is in plain sight. Don&amp;rsquo;t optimise for its own sake; &lt;em&gt;optimise where it will help&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what I noted about our tooling:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The compilation time itself is already pretty well optimised and parallelised thanks to &lt;code&gt;meson&lt;/code&gt;, &lt;code&gt;ninja&lt;/code&gt; and &lt;code&gt;ccache&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The code style checker is something we run very frequently as part of our workflow (it&amp;rsquo;s in our pre-commit script). At 5 seconds, that&amp;rsquo;s like sand in your gears.&lt;/li&gt;
&lt;li&gt;The static analyser is a different type of pain point. We run it interactively but we don&amp;rsquo;t like to because it&amp;rsquo;s a slow ol&amp;rsquo; beast. On the cloud, it is the slowest part of the build; it is probably the reason why we&amp;rsquo;re regularly incurring overage on our CI build minutes. (Now, that&amp;rsquo;s not a big cost compared to an engineer&amp;rsquo;s salary, but it&amp;rsquo;s not zero and it has been noticed, so maybe worth spending a little time on.)&lt;/li&gt;
&lt;li&gt;The unit test run on a dev board is a bottleneck, practically by definition, as it uses a self-hosted CI runner plugged into our pipeline.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;code-style-checks-parallelise&#34;&gt;Code style checks: Parallelise!&lt;/h3&gt;
&lt;p&gt;We use &lt;code&gt;clang-format&lt;/code&gt; with an in-house style file.&lt;/p&gt;
&lt;p&gt;The heart of our checker script is a shell function.&lt;/p&gt;
&lt;p&gt;The tl;dr version is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We were invoking clang-format in a tight loop, once per input file. 🤦&lt;/li&gt;
&lt;li&gt;Now, we count the files, divide by (slightly more than) the number of CPU cores available, and have xargs parcel out the work into that many chunks. 🚅&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;static-analysis-reconsider-the-load-cache-and-parallelise-where-it-will-help&#34;&gt;Static analysis: Reconsider the load, cache, and parallelise where it will help!&lt;/h3&gt;
&lt;p&gt;The learnings from this one are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do the heavy lifting in parallel (we were already)&lt;/li&gt;
&lt;li&gt;Cacheing positive analysis results helps (we were doing that too)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don&amp;rsquo;t analyse unit tests or test harness/mocks/fakes&lt;/strong&gt; unless you have a good reason&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don&amp;rsquo;t analyse target-specific files for obsolete hardware targets&lt;/strong&gt; unless you have a good reason&lt;/li&gt;
&lt;li&gt;Where you have a string-and-glue shell phase to prepare the analysis run, it can be worth parallelising that as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;results-&#34;&gt;Results 🚀&lt;/h2&gt;
&lt;p&gt;(Timed on my work laptop, which has 16 cores. It&amp;rsquo;s running Ubuntu 22.04 on WSL on Windows 11.)&lt;/p&gt;

     &lt;table class=&#34;table table-striped table-bordered&#34;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action/Phase&lt;/th&gt;
&lt;th&gt;Time before&lt;/th&gt;
&lt;th&gt;Time after&lt;/th&gt;
&lt;th&gt;Speed-up&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Code style check&lt;/td&gt;
&lt;td&gt;4.7 seconds&lt;/td&gt;
&lt;td&gt;210 ms&lt;/td&gt;
&lt;td&gt;22.3x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static analysis setup&lt;/td&gt;
&lt;td&gt;14 seconds&lt;/td&gt;
&lt;td&gt;3.5s&lt;/td&gt;
&lt;td&gt;4x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;/table&gt;
&lt;h3 id=&#34;my-next-steps&#34;&gt;My next steps&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The on-target testing phase is a bottleneck, so we&amp;rsquo;re going to add more hardware.&lt;/li&gt;
&lt;li&gt;There might be some mileage in using using &lt;a href=&#34;https://mesonbuild.com/Precompiled-headers.html&#34;&gt;pre-compiled headers&lt;/a&gt; to speed up cold-cache compilation.
&lt;ul&gt;
&lt;li&gt;This isn&amp;rsquo;t something the places I have worked have traditionally done, but I know it&amp;rsquo;s popular when you&amp;rsquo;re dealing with the Win32 C++ api.&lt;/li&gt;
&lt;li&gt;Our codebase has a &amp;ldquo;platform&amp;rdquo; layer; this smells like a good starting point for precompilation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Coverage analysis. We already do this, but there&amp;rsquo;s another pain point while &lt;code&gt;genhtml&lt;/code&gt; crunches the data and generates the HTML report. This one could be trickier; it&amp;rsquo;s a single-threaded perl script, invoked by meson.&lt;/li&gt;
&lt;li&gt;Stack usage analysis. We&amp;rsquo;ve got some code paths that can be provoked to overflow on debug builds. This is a different sort of pain point; while we&amp;rsquo;ve configured the silicon to detect and hard-fault on overflow, I&amp;rsquo;d like to set up some analysis to automatically find these situations ahead of time.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;closing-thoughts&#34;&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;How long to spend on infrastructure?&lt;/em&gt; 💸&lt;/p&gt;
&lt;p&gt;Circling back to a question I asked earlier, how long is a piece of string?&lt;/p&gt;
&lt;p&gt;It depends where you are and what your current DX is like.&lt;/p&gt;
&lt;p&gt;10-20% of your developer time seems a reasonable benchmark; it&amp;rsquo;s enough that you can get a handful of DX tickets or story points through every iteration without becoming too much of a drag on progress.
After all, if you&amp;rsquo;re working agile (and who isn&amp;rsquo;t, these days?) constant improvements are the key.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;How&amp;rsquo;s your developer experience looking?&lt;/em&gt;&lt;/p&gt;</description>
      <enclosure url="https://www.chiark.greenend.org.uk/~ryounger/images/software/code.jpg" type="image" /></item>
    
    <item>
      <title>Lightroom plugin for Tangent control surfaces</title>
      <link>https://www.chiark.greenend.org.uk/~ryounger/2019/12/lightroom-plugin-for-tangent-control-surfaces/</link>
      <pubDate>Sun, 15 Dec 2019 21:31:00 +1300</pubDate>
      
      <guid>https://www.chiark.greenend.org.uk/~ryounger/2019/12/lightroom-plugin-for-tangent-control-surfaces/</guid>
      <description>&lt;p&gt;It&amp;rsquo;s all about speed. If you have a lot of interactive colour correction
to do, it&amp;rsquo;s so much faster to use a hardware panel
than it is to use the mouse.&lt;/p&gt;
&lt;p&gt;Professional film and video colour graders use hardware control surfaces
all the time. Broadcast (video) engineers do something very similar with
their CCUs.&lt;/p&gt;
&lt;p&gt;This is a plugin for Adobe Lightroom Classic, which draws heavily on a
similar plugin called MIDI2LR but for MIDI controllers.&lt;/p&gt;
&lt;p&gt;&lt;del&gt;You can get it from &lt;a href=&#34;https://github.com/crazyscot/tangent-lightroom&#34;&gt;tangent-lightroom on
Github&lt;/a&gt;.&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update: This project is obsolete!&lt;/strong&gt; On 1 Apr 2021, Tangent released their own official plugin for Lightroom. It&amp;rsquo;s included in version 1.7.0 of the Tangent bundle, whish you can get from &lt;a href=&#34;https://www.tangentwave.co.uk/tangent-support/&#34;&gt;Tangent support&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The repository lives on, as a place for me to keep my mapping for the Wave:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/crazyscot/tangent-lightroom/tree/tangent-map&#34;&gt;tangent-map branch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://raw.githubusercontent.com/crazyscot/tangent-lightroom/tangent-map/RY-Lightroom.xml&#34;&gt;Direct download&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <enclosure url="https://www.chiark.greenend.org.uk/~ryounger/images/tangent-lightroom-panel.jpg" type="image" /></item>
    
    <item>
      <title>Broadcast graphics package</title>
      <link>https://www.chiark.greenend.org.uk/~ryounger/2018/04/broadcast-graphics-package/</link>
      <pubDate>Tue, 10 Apr 2018 10:25:03 +1200</pubDate>
      
      <guid>https://www.chiark.greenend.org.uk/~ryounger/2018/04/broadcast-graphics-package/</guid>
      <description>&lt;p&gt;This is a package for the CasparCG playout engine.&lt;/p&gt;
&lt;p&gt;It comprises score bugs, a score lower third, generic lower thirds, and
a count-up/count-down clock.&lt;/p&gt;
&lt;p&gt;The templates are written in HTML, CSS and JavaScript.&lt;/p&gt;
&lt;p&gt;The package comes with a a custom operator panel written in Python and wxWidgets.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://mediary.nz/blog/broadcast-graphics-for-casparcg/&#34;&gt;Broadcast graphics product page&lt;/a&gt;&lt;/p&gt;</description>
      <enclosure url="https://www.chiark.greenend.org.uk/~ryounger/images/software/caspar_scorebug_with_clock_00141.png" type="image" /></item>
    
    <item>
      <title>find-definition: a helper script for programmers</title>
      <link>https://www.chiark.greenend.org.uk/~ryounger/2013/03/find-definition-a-helper-script-for-programmers/</link>
      <pubDate>Fri, 01 Mar 2013 20:09:23 +1300</pubDate>
      
      <guid>https://www.chiark.greenend.org.uk/~ryounger/2013/03/find-definition-a-helper-script-for-programmers/</guid>
      <description>&lt;p&gt;Opens up a new editor directly at the definition of a type, structure, function or anything else that ctags reports.&lt;/p&gt;
&lt;p&gt;I use it with vim. With other editors you either need to have ctags support (contributions welcome!) or be prepared to use vi as an external source browser.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/crazyscot/find-definition&#34;&gt;Browse on github&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git clone https://github.com/crazyscot/find-definition.git&lt;/code&gt;&lt;/p&gt;</description>
      <enclosure url="https://www.chiark.greenend.org.uk/~ryounger/images/software/code.jpg" type="image" /></item>
    
    <item>
      <title>brot2: a fractal plotter</title>
      <link>https://www.chiark.greenend.org.uk/~ryounger/2011/12/brot2-a-fractal-plotter/</link>
      <pubDate>Thu, 15 Dec 2011 21:17:34 +1300</pubDate>
      
      <guid>https://www.chiark.greenend.org.uk/~ryounger/2011/12/brot2-a-fractal-plotter/</guid>
      <description>&lt;p&gt;Revisiting the work of a great mathematician.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Banner image: Mandelbrot centred at (0.250648691259948, 2.6343326303079e-05), maxiter=16384, horizontal axis=3.176e-06&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When Benoit Mandelbrot passed away in 2010, I was inspired to revisit his work (as, it turns out, were some of my friends). This is my second Mandelbrot plotter; the first was cryptically named &amp;lsquo;brot&amp;rsquo;, was pretty basic, and is lost to the mists of time; this one is an interactive GTK+ application.&lt;/p&gt;
&lt;figure class=&#34;floatright&#34;&gt;&lt;img src=&#34;https://www.chiark.greenend.org.uk/~ryounger/images/software/Mandelbrot@%28-0.67,%200.1%29%20max=768%20axis=%283,3%29.jpg&#34; width=&#34;300&#34; height=&#34;300&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;Mandelbrot@(-0.67, 0.1) max=768 axis=(3,3)&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&#34;see-also&#34;&gt;See also&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.chiark.greenend.org.uk/~ryounger/2011/11/fractals/&#34;&gt;Some of my favourite images created with brot2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Mandelbrot_set&#34;&gt;About the Mandelbrot set (Wikipedia)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;feature-list&#34;&gt;Feature list&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;mouse-driven navigation (click to zoom in/out and recentre);&lt;/li&gt;
&lt;li&gt;specific zooming by dragging out a rectangle;&lt;/li&gt;
&lt;li&gt;multiple extensible palettes;&lt;/li&gt;
&lt;li&gt;linear antialiasing;&lt;/li&gt;
&lt;li&gt;save image as PNG;&lt;/li&gt;
&lt;li&gt;draws a number of related fractals as well as the original Mandelbrot set and can be relatively easily extended to add more.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;getting-brot2&#34;&gt;Getting brot2&lt;/h2&gt;
&lt;p&gt;brot2 is free software: you can redistribute it and/or modify it under the terms of the &lt;a href=&#34;http://www.gnu.org/licenses/gpl.html&#34;&gt;GNU General Public License&lt;/a&gt; as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.&lt;/p&gt;
&lt;p&gt;brot2 has a &lt;a href=&#34;https://launchpad.net/brot2&#34;&gt;project page on LaunchPad&lt;/a&gt;. You can get it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;From the &lt;a href=&#34;https://launchpad.net/brot2/+download&#34;&gt;releases page&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;from its its &lt;a href=&#34;https://launchpad.net/~crazyscot/+archive/brot2&#34;&gt;release builds PPA&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;from the &lt;a href=&#34;https://launchpad.net/~crazyscot/+archive/brot2-daily&#34;&gt;daily-build PPA&lt;/a&gt;; or&lt;/li&gt;
&lt;li&gt;by cloning from the repositiory and building it yourself:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git clone git://github.com/crazyscot/brot2.git&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/crazyscot/brot2&#34;&gt;browse&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In order to build brot2, you will need to have various gtk and gtkmm developer libraries installed (amongst others). They are listed in the debian/control file (and taken care of if you use the standard package building tools).&lt;/p&gt;
&lt;figure class=&#34;floatright&#34;&gt;&lt;img src=&#34;https://www.chiark.greenend.org.uk/~ryounger/images/software/Mandelbrot@(0.250650639435726, 2.7211535074015e-05) max=24576 axis=(3.176e-06,3.176e-06).jpg&#34; width=&#34;300&#34; height=&#34;300&#34; /&gt;
&lt;figcaption&gt;&lt;h4&gt; Mandelbrot@(0.250650639435726, &lt;br/&gt;2.7211535074015e-05) max=24576 &lt;br/&gt;axis=(3.176e-06,3.176e-06) &lt;/h4&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&#34;using-brot2&#34;&gt;Using brot2&lt;/h2&gt;
&lt;p&gt;You can move around the plot using your mouse (or equivalent pointing device). The first time you run brot2, the Controls window appears; you can hide and show it via the Options menu. By default the controls are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Left-click to recentre around the spot clicked on.&lt;/li&gt;
&lt;li&gt;Drag with the right button to mark out a rectangle to zoom in to. (It may be adjusted to fit the window.)&lt;/li&gt;
&lt;li&gt;Middle-click to zoom out around the spot clicked on.&lt;/li&gt;
&lt;li&gt;Button 8 (the 4th button on my Kensington Expert Mouse) to zoom in, centred on the spot clicked on.&lt;/li&gt;
&lt;li&gt;Scroll wheel up and down zoom in and out, centring around the pointer&amp;rsquo;s current location.&lt;/li&gt;
&lt;li&gt;Keyboard &amp;lsquo;+&amp;rsquo; and &amp;lsquo;-&amp;rsquo; zoom in and out around the current centre of the plot.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is a simple single-step undo mechanism. This is designed to let you recover from the &amp;ldquo;oh dear, I clicked somewhere silly, where was I?&amp;rdquo; scenario.&lt;/p&gt;
&lt;p&gt;The current fractal position is shown in the heads-up display overlaid onto the window. You can change its size, colour, transparency and positioning via the Preferences dialog.&lt;/p&gt;
&lt;p&gt;If you want to specify your plot numerically, you can do this via the Parameters dialog.&lt;/p&gt;
&lt;p&gt;The plot and window sizes are always the same; if you resize the window, the plot is redrawn to fit.&lt;/p&gt;
&lt;p&gt;You can save the current plot as a PNG file, with the option to save at a different size. (Changing size may take a while as the image has to be re-plotted.) The HUD is not drawn onto a saved image.&lt;/p&gt;
&lt;p&gt;For better image quality, activate the antialiasing mode (Ctrl-A). This takes longer to draw as it has to plot four times as many points and average across them. (This is a pretty naive linear antialiasing; better ways remain to be found and implemented.)&lt;/p&gt;
&lt;p&gt;brot2 has the notion of a plot being &amp;ldquo;good enough&amp;rdquo; to stop computing. This is expressed as a fraction (percentage) of the pixels which should have escaped (or be provably within the set), and a proportion of pixels continuing to escape in each pass. This is a tricky compromise between drawing enough detail and not taking forever, and it doesn&amp;rsquo;t always get it right; you can tune it via the Preferences dialog. You can always stop a plot with Ctrl-. or ask it to make another pass (&amp;ldquo;More iterations&amp;rdquo;) with Ctrl-M.&lt;/p&gt;
&lt;p&gt;There are multiple colour palettes. At the moment these are hard-coded. Changing colour scheme is a quick and cheap operation; you can even cycle through them with Ctrl-3 and Ctrl-4.&lt;/p&gt;
&lt;figure class=&#34;floatright&#34;&gt;&lt;img src=&#34;https://www.chiark.greenend.org.uk/~ryounger/images/software/Mandelbrot@(-0.761228719917374, 0.0847858796416676) max=3072 axis=(4.643e-10,4.643e-10).jpg&#34; width=&#34;300&#34; height=&#34;300&#34; /&gt;
&lt;figcaption&gt;&lt;h4&gt;Mandelbrot@(-0.761228719917374, &lt;br/&gt;0.0847858796416676) max=3072 &lt;br/&gt;axis=(4.643e-10,4.643e-10)&lt;/h4&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&#34;whats-in-a-name&#34;&gt;What&amp;rsquo;s in a name?&lt;/h2&gt;
&lt;p&gt;This is my second Mandelbrotter.&lt;/p&gt;
&lt;p&gt;The first was cryptically named &lt;code&gt;brot&lt;/code&gt;; both the origins of its name, and the code, are lost in the mists of time. It was a bit pants, even by the standards of the time; it took its parameters on the command line and output as a TIFF, ASCII art or to a terminal which understood ANSI graphics codes. (I claim this as a dubiously legitimate reason for setting xterm to &amp;lsquo;unreadable&amp;rsquo; :-) )&lt;/p&gt;
&lt;p&gt;This one is a bit better featured, but to me it&amp;rsquo;s logically the heir to its now-departed ancestor, hence the name.&lt;/p&gt;</description>
      <enclosure url="https://www.chiark.greenend.org.uk/~ryounger/images/software/Mandelbrot@%280.250648691259948,%202.6343326303079e-05%29%20max=16384%20axis=%283.176e-06,2.222e-06%29-800.jpg" type="image" /></item>
    
    <item>
      <title>CurrentCost monitoring for RRDtool</title>
      <link>https://www.chiark.greenend.org.uk/~ryounger/2010/08/currentcost-monitoring-for-rrdtool/</link>
      <pubDate>Wed, 18 Aug 2010 12:11:52 +0100</pubDate>
      
      <guid>https://www.chiark.greenend.org.uk/~ryounger/2010/08/currentcost-monitoring-for-rrdtool/</guid>
      <description>&lt;p&gt;Scripts to read data from a &lt;a href=&#34;http://www.currentcost.co.uk/&#34;&gt;CurrentCost&lt;/a&gt; CC128 electricity consumption monitor and drop it into an &lt;a href=&#34;http://oss.oetiker.ch/rrdtool/&#34;&gt;RRDtool&lt;/a&gt; database.&lt;/p&gt;
&lt;p&gt;They are based on &lt;a href=&#34;http://www.jibble.org/currentcost/&#34;&gt;Paul Mutton&amp;rsquo;s CurrentCost serial port script&lt;/a&gt;. My version adds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;minicom-compatible serial port locking&lt;/li&gt;
&lt;li&gt;autodetection in case of multiple ttyUSB devices&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/crazyscot/currentcost&#34;&gt;Browse on github&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git clone https://github.com/crazyscot/currentcost.git&lt;/code&gt;&lt;/p&gt;</description>
      <enclosure url="https://www.chiark.greenend.org.uk/~ryounger/images/software/rrd-power.png" type="image" /></item>
    
    <item>
      <title>chiark-cryptdisk: scripts for encrypted backups</title>
      <link>https://www.chiark.greenend.org.uk/~ryounger/2010/08/chiark-cryptdisk-scripts-for-encrypted-backups/</link>
      <pubDate>Wed, 11 Aug 2010 11:39:19 +0000</pubDate>
      
      <guid>https://www.chiark.greenend.org.uk/~ryounger/2010/08/chiark-cryptdisk-scripts-for-encrypted-backups/</guid>
      <description>&lt;p&gt;This package is only available in source form for Debian systems.&lt;/p&gt;
&lt;p&gt;It contains machinery to build a Debian package in the usual way. Be aware that it is not really polished and will require a certain amount of effort to integrate into a backup solution.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/crazyscot/chiark-cryptdisk&#34;&gt;Browse on github&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git clone https://github.com/crazyscot/chiark-cryptdisk.git&lt;/code&gt;&lt;/p&gt;</description>
      <enclosure url="https://www.chiark.greenend.org.uk/~ryounger/images/software/code-debian.jpg" type="image" /></item>
    
    <item>
      <title>Using the ZTE MF626 on Ubuntu and T-Mobile</title>
      <link>https://www.chiark.greenend.org.uk/~ryounger/2010/02/using-the-zte-mf626-on-ubuntu-and-t-mobile/</link>
      <pubDate>Wed, 10 Feb 2010 19:00:23 +0000</pubDate>
      
      <guid>https://www.chiark.greenend.org.uk/~ryounger/2010/02/using-the-zte-mf626-on-ubuntu-and-t-mobile/</guid>
      <description>&lt;p&gt;The ZTE MF626 is a 3G modem stick resold by T-Mobile in the UK. 
&lt;p&gt;They call it the &amp;ldquo;USB stick 120&amp;rdquo;; other networks likely call it something different. Rumour had it that it ought to work under Ubuntu; Google brought me to &lt;a href=&#34;http://ubuntuforums.org/showthread.php?t=1147685&#34;&gt;&lt;a href=&#34;http://ubuntuforums.org/showthread.php?t=1147685&#34;&gt;http://ubuntuforums.org/showthread.php?t=1147685&lt;/a&gt;&lt;/a&gt; which contains some useful info for Jaunty (9.04) but turns out to be a bit out of date with regard to Karmic (9.10).&lt;/p&gt;
&lt;!--more--&gt;
&lt;h2 id=&#34;the-usb-zerocd-oddity&#34;&gt;The USB ZeroCD oddity&lt;/h2&gt;
&lt;p&gt;The MF626 is not your average USB device. It&#39;s one of a growing trend of &lt;em&gt;ZeroCD&lt;/em&gt; devices: when you plug them in, they report themselves as being of type USB mass storage (in this case, &lt;tt&gt;19d2:2000&lt;/tt&gt; - check it with &lt;tt&gt;lsusb&lt;/tt&gt;), with an autorun.inf which installs the drivers and other paraphernalia. When you have the driver, it issues a magic command to the device, which causes it to magically change its USB product ID to that of the real modem device (in this case, &lt;tt&gt;19d2:0031&lt;/tt&gt;).
&lt;h3 id=&#34;zerocd-on-ubuntu&#34;&gt;ZeroCD on Ubuntu&lt;/h3&gt;
&lt;p&gt;It used to be the case that you had to install a program called &lt;tt&gt;usb_modeswitch&lt;/tt&gt; which sent the magic command to the device. (It&#39;s in the Ubuntu archive.) Unfortunately, this didn&#39;t work reliably for me; on one machine, I eventually had it working most of the time, but there was no such luck on my laptop.
&lt;p&gt;Almost by accident, I tripped over some steps which seem to work every time:
&lt;ol&gt;
	&lt;li&gt; Insert the USB stick
	&lt;li&gt; Watch &lt;tt&gt;/var/log/messages&lt;/tt&gt; to get the device node for the SCSI CDROM it thinks you just inserted (sr1 usually). This takes a few seconds to come in as the device boots up and udev figures out what it appears to be.
	&lt;li&gt; &lt;tt&gt;sudo eject /dev/sr1&lt;/tt&gt;
	&lt;li&gt; Wait a few more seconds - watch &lt;tt&gt;messages&lt;/tt&gt; if you like - then run lsusb to confirm the device is now 19d2:0031.
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Geeky side note:&lt;/em&gt; Once you have successfully mode-switched, the device appears as three ttyUSB devices. You can fire up minicom at this point on at least two of the three - 115200 8N1 with hardware flow control works - and send it AT commands :-)
&lt;h4 id=&#34;obsolete-usb_modeswitch-notes&#34;&gt;Obsolete usb_modeswitch notes:&lt;/h4&gt;
&lt;ul&gt;
	&lt;li&gt;The package in karmic comes with &lt;tt&gt;/etc/udev/rules.d/usb_modeswitch.rules&lt;/tt&gt; which have the ZTE MF620 enabled by default. You&#39;ll have to edit it and comment out that stanza; I did try the stanza for the MF626 but couldn&#39;t get it to work at all.
	&lt;li&gt;Editing &lt;tt&gt;/etc/usb_modeswitch.conf&lt;/tt&gt; to uncomment the relevant lines for the ZTE MF626 allowed it to work from the command line (&lt;tt&gt;sudo usb_modeswitch [-W]&lt;/tt&gt;) - most of the time, on one machine, but not at all on the other. I found I did need to be patient and wait several seconds between insertion of the device and invoking the program; this might be what stops the udev rule from working.
	&lt;li&gt; &lt;a href=&#34;http://www.draisberghof.de/usb_modeswitch/&#34;&gt;USB modeswitch home page&lt;/a&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;dial-up-settings&#34;&gt;Dial-up settings&lt;/h2&gt;
&lt;p&gt;Reportedly the NetworkManager is a bit flakey with USB modems in jaunty, but I had no problem in karmic.
&lt;ol&gt;
	&lt;li&gt;Ensure &lt;tt&gt;modemmanager&lt;/tt&gt; is installed.
	&lt;li&gt;Create the file &lt;tt&gt;/usr/share/hal/fdi/information/20thirdparty/20-zte-mf626.fdi&lt;/tt&gt; as described at step 10 of &lt;a href=&#34;http://ubuntuforums.org/showthread.php?t=1147685&#34;&gt;http://ubuntuforums.org/showthread.php?t=1147685&lt;/a&gt;. 
	&lt;li&gt;(Remove and re)insert the device and &#34;eject&#34; it as described above
	&lt;li&gt;&lt;em&gt;Be sure to wait for the modem device to settle down. I found this can take up to 15 seconds from issuing the eject.&lt;/em&gt;
	&lt;li&gt;Click on the NetworkManager icon in the menubar. If the device has been recognised, you will see menu items for Mobile Broadband, and the option to create a new connection. (If not, wait a few more seconds and try again.)
	&lt;li&gt;The wizard asks you pertinent questions; thankfully, under T-Mobile there is only the one price plan and no need to enter a username or password. You should now be ready to dial up.
&lt;/ol&gt;
&lt;p&gt;Once the connection is set up, the steps boil down to:
&lt;ol&gt;&lt;li&gt;Insert stick;
	&lt;li&gt; wait a bit; 
	&lt;li&gt;&#34;eject&#34; it; 
	&lt;li&gt;wait a bit; 
	&lt;li&gt;select the connection name from the NetworkManager drop-down list;
	&lt;li&gt;wait until NetworkManager tells you you&#39;re connected, and a couple more seconds for &lt;tt&gt;resolv.conf&lt;/tt&gt; to be updated (also see below).
&lt;/ol&gt;
&lt;h3 id=&#34;alternatives-to-networkmanager&#34;&gt;Alternatives to NetworkManager&lt;/h3&gt;
&lt;p&gt;Anecdotally from the forums, wvdial is said to be more reliable than NetworkManager.  There are also some people in the Ubuntu world who refuse to use NetworkManager, preferring to do things the old way; for them, wvdial is obviously the way. (I used to be one such person. I have to say, it has been ten years since I last used wvdial, and I found it rock solid back then, so if I had had problems with NetworkManager then wvdial would be my next port of call.)
&lt;p&gt;I haven&#39;t tried it myself, but the ubuntu forums link above does mention wvdial as a viable alternative. The device appears to present itself as an AT-compatible modem; NetworkManager seems to have it dial `*99***1#` . 
&lt;h2 id=&#34;getting-online-with-t-mobile&#34;&gt;Getting online with T-Mobile&lt;/h2&gt;
&lt;h3 id=&#34;dns-servers&#34;&gt;DNS servers&lt;/h3&gt;
&lt;p&gt;I&#39;ve found that, once in a while (and in fact the very first time I connected), NetworkManager doesn&#39;t manage to set up &lt;tt&gt;/etc/resolv.conf&lt;/tt&gt; correctly when it connects. 
&lt;p&gt;At the time of writing, T-Mobile&#39;s nameservers for mobile broadband users are 149.254.192.126 and 149.254.201.12.  I also noted that 8.8.8.8 (Google&#39;s public DNS service) also worked just fine.
&lt;p&gt;If this turns out to happen frequently, NetworkManager does let you set the connection to be PPP &#34;addresses only&#34; with manually-specified nameservers. 
&lt;h3 id=&#34;escaping-the-payg-walled-garden-registering-a-payment-card&#34;&gt;Escaping the PAYG walled garden (registering a payment card)&lt;/h3&gt;
&lt;p&gt;My USB stick is on a pay-as-you-go account. This means that when you first dial up, www requests are redirected to the T-Zones site. (You can also access t-mobile.co.uk, but that seems to be about the limit.) You have to use T-Zones to select how many days usage you want to pay for; naturally, the week and month bundles work out better if you&#39;re going to use them.
&lt;p&gt;So far so good. Unfortunately, the initial credit on the account had expired so I needed to top-up. I wanted to top-up online as this was much more convenient than finding my nearest top-up retailer or ATM. To do this, you have to first register yourself with T-Mobile and register the device with your account; then you can add a payment card and make a top-up. 
&lt;p&gt;The fun came in registering the device to my online account: you enter the device phone number and it sends you an SMS with a 4-digit confirmation &#34;PIN&#34;.  Right. Yes. This is a &lt;span style=&#34;text-decoration:line-through;&#34;&gt;lighthouse&lt;/span&gt; USB stick, and I don&#39;t have SMS software installed.  (Aside: Hello? surely you can tell that I&#39;m connected from a tmo line and skip that step?)
&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Side note: A package called &lt;tt&gt;wader&lt;/tt&gt; exists and claims to be a more fully featured mobile broadband connection manager, doing SMS as well, though it conflicts with the modemmanager package; I haven&#39;t tried it.&lt;/em&gt;&lt;/blockquote&gt;
&lt;p&gt;There was nothing else for it; I reluctantly booted into Windows and let the device install its own driver. The connection manager it installs is functional, if obviously cheaply developed, and allows you to receive SMSes. Great. But the SMS containing the code was apparently lost into the ether, and the T-Mobile site wouldn&#39;t resend it ... Thankfully, I was able to work around this by removing the device from my online account and re-adding it. At last, I was able to register a payment card, put some credit on the account and actually spend my &amp;pound;2 to get online!
&lt;p&gt;Once you have selected a package and debited your PAYG account appropriately, it seems you can disconnect and reconnect as many times as you like during the connection period you&#39;ve paid for and not be troubled by the walled garden. Nevertheless you can go back to www.t-zones.co.uk at any time to see your remaining credit.
&lt;h3 id=&#34;disabling-contentlock&#34;&gt;Disabling ContentLock&lt;/h3&gt;
&lt;p&gt;By accident, I happened to trip over ContentLock. This is T-Mobile&#39;s attempt to do their bit to prevent children from accessing content deemed unsuitable, including &#34;social networking sites&#34; and sites with &#34;user-generated content&#34;, which includes Flickr, that dangerous bastion of ... er ... something. You&#39;ll know if you hit ContentLock, because you get redirected to a message which tells you that access is blocked and purports to tell you how you switch it off. Unfortunately, actually switching it off took some doing...
&lt;p&gt;&lt;em&gt;(If the next few paragraphs are tl;dr, you essentially have two options: to take proof of ID into a T-Mobile store, or to put the SIM into a regular mobile, dial 1818 and provide credit card details.)&lt;/em&gt;
&lt;ol&gt;
	&lt;li&gt;The you-are-blocked message offers to allow you to remove the lock on the spot, if you have a credit card to &#34;prove&#34; you&#39;re 18: they propose to charge and immediately refund &amp;pound;2.  Unfortunately, this didn&#39;t work; the site took my card details then claimed to be unable to determine what phone number I was accessing it from, which didn&#39;t exactly leave me brimming with confidence.
	&lt;ul&gt;
		&lt;li&gt;&lt;em&gt;(Err, hello? Have these people never heard of authorised user cards?)&lt;/em&gt;
		&lt;li&gt;&lt;em&gt;Side note: The ContentLock portion of their infrastructure is obviously a quickly-written afterthought - it&#39;s plain HTML with no CSS.&lt;/em&gt;&lt;/ul&gt;
	&lt;li&gt;Apparently - going on ContentLock&#39;s say-so - I could switch off the lock via my T-Mobile account. So I went there and it wanted me to select my &#34;phone&#34; model before it could show me the options... but USB sticks were not in the list! To summarise my googling, this doesn&#39;t seem to work for PAYG accounts. Sigh.
	&lt;li&gt;Next, I tried ringing T-Mobile customer services. (The number is 150 from T-Mobile, or 07953966150 from other networks.) The call centre operator asked the number I was calling about, then apologetically said she couldn&#39;t access PAYG accounts and put me through to the PAYG people. The PAYG &#34;people&#34; turned out to be an automated menu system. There is an option for ContentLock... but it only provides information and tells you to ring 1818.
	&lt;li&gt;I tried ringing 1818 from my mobile, but (surprise surprise) that system only works if you&#39;re calling from the line you want to en/disable it for. &lt;b&gt;How are you expected to dial 1818 from a USB stick?!&lt;/b&gt;
	&lt;li&gt;I noted at this point that I could take proof of ID to a T-Mobile store in person, but I didn&#39;t want the hassle of going into town.
	&lt;li&gt;On a whim, I removed the SIM card from the USB stick and put it into my mobile. It powered up and connected to the network happily; I dialled 1818; keyed in my credit card details and voil&amp;agrave;, after a delay, the voice told me it had removed ContentLock from the line. Sure enough, on popping the SIM back into the stick, I could access Flickr.
&lt;/ol&gt;
&lt;p&gt;Conclusion: ContentLock is a hastily-developed addition to their network, probably added to pay lip service to some regulatory requirements, to legal due diligence or to &#34;think-of-the-children&#34; pressure. It seems to go a bit far with the sites it blocks, but was at least relatively straightforward to switch off.
&lt;h2 id=&#34;wishlist&#34;&gt;Wishlist&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;Be able to receive SMSes under Linux. This is important in case you get close to your fair-use quota / data cap / whatever your telco calls it. (Sending would be nice but is optional for me as I have a regular mobile, and T-Mobile users can also send texts via their website.) There might be some mileage in gnome-phone-manager.
&lt;/ul&gt;</description>
      <enclosure url="https://www.chiark.greenend.org.uk/~ryounger/images/software/code-mf626.jpg" type="image" /></item>
    
  </channel>
</rss>