<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Jordan Terry</title>
    <description>Software engineer, pilot, long-distance cyclist.</description>
    <link>https://jordanterry.co.uk/</link>
    <atom:link href="https://jordanterry.co.uk/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Tue, 16 Jun 2026 03:11:17 +0000</pubDate>
    <lastBuildDate>Tue, 16 Jun 2026 03:11:17 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator><item>
        <title>Juste Cinq Minutes</title>
        <description>&lt;p&gt;Maintenant, je travaille (pas super dur) pour obtenir une qualification B2 en français. Dans ma vie, c’est un but important : avec un B2, dans deux ans je peux obtenir une nationalité française, grâce à ma femme!&lt;/p&gt;

&lt;p&gt;J’ai un problème : je suis paresseux, très paresseux. Je n’utilise pas mes journées pour cet objectif. Aujourd’hui je commence un nouveau project: “Francais sur le ferry”. Je vais écrire quelques phrases chaque jour pendant moins de cinq minutes et essayer d’être moins paresseux!&lt;/p&gt;

</description><description>&lt;p&gt;Maintenant, je travaille (pas super dur) pour obtenir une qualification B2 en français. Dans ma vie, c’est un but important : avec un B2, dans deux ans je peux obtenir une nationalité française, grâce à ma femme!&lt;/p&gt;
</description><pubDate>Tue, 09 Jun 2026 00:00:00 +0000</pubDate>
        <link>https://jordanterry.co.uk/juste-cinq-minutes</link>
        <guid isPermaLink="true">https://jordanterry.co.uk/juste-cinq-minutes</guid><category>French</category></item><item>
        <title>Hot TAIke Two: TDD is back, baby</title>
        <description>&lt;p&gt;Test Driven Development gets a bad rap. I don’t understand why; you derive your tests from your functional requirements, and thanks to Continuous Integration those tests then run on every push, merge, and release of your code. Sounds great to me.&lt;/p&gt;

&lt;p&gt;I once worked at a really, really, really boring software development job. It was so boring that I started reading the books I thought were boring at university. Except this time I had to buy them, rather than take them out of the library. Namely &lt;a href=&quot;https://www.goodreads.com/book/show/44919.Working_Effectively_with_Legacy_Code&quot;&gt;Working Effectively with Legacy Code&lt;/a&gt;, &lt;a href=&quot;https://www.goodreads.com/book/show/6408726&quot;&gt;Test-Driven Development&lt;/a&gt;, and &lt;a href=&quot;https://www.goodreads.com/book/show/67833.Extreme_Programming_Explained&quot;&gt;Extreme Programming Explained&lt;/a&gt;. They left an impression on me. That boring job became a lot less boring once I started applying what I’d read. These books gave me a framework for thinking about, and addressing Software Engineering.&lt;/p&gt;

&lt;p&gt;These books are a well of great quotes that I come back to regularly.&lt;/p&gt;

&lt;p&gt;On the definition of Legacy Code:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;To me, legacy code is simply code without tests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And a couple of bangers from Kent Beck:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I’m not a great programmer; I’m just a good programmer with great habits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Test-driven development is a way of managing fear during programming.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Over time I was lucky enough to have these mantras drilled into me by working with some great mentors.&lt;/p&gt;

&lt;p&gt;But I often despair that the fine art of validating your work before you even write it is dying off. So imagine my delight when I read that TDD is in the &lt;a href=&quot;https://www.anthropic.com/engineering/claude-code-best-practices&quot;&gt;Claude Code best practices&lt;/a&gt; documentation.&lt;/p&gt;

&lt;p&gt;The concept of Continuous Delivery was created in order to continuously validate your code. Without tests, we lose confidence in the code we write. This is even more crucial in a world of Claude Code, where we should by default distrust the code that is written.&lt;/p&gt;

&lt;p&gt;So what if we turn the current way of working on its head? If our intention is to deliver projects for our users, can we turn our roles into a job of writing tests? A harness that allows us to validate the work done by our untrustworthy friend, Claude.&lt;/p&gt;

&lt;p&gt;I think test libraries are some of the most creative spaces in software development. They give us room to experiment with new patterns, explore edge cases, and stress-test our assumptions. In a future where Claude writes the production code, this becomes &lt;em&gt;our&lt;/em&gt; playground. The place where we flex our craft and define what “correct” actually means.&lt;/p&gt;
</description><description>Test Driven Development gets a bad rap. But in a world of AI coding assistants, it might be exactly what we need.
</description><pubDate>Sun, 01 Feb 2026 00:00:00 +0000</pubDate>
        <link>https://jordanterry.co.uk/tdd-is-back-baby</link>
        <guid isPermaLink="true">https://jordanterry.co.uk/tdd-is-back-baby</guid><category>Software</category></item><item>
        <title>Hot TAIke One: Software Development is going back to its roots</title>
        <description>&lt;p&gt;There is no getting around it, we can bury our heads in the sand, but LLMs like Anthropic’s Opus 4.5 or OpenAI’s Codex can do a fair chunk of the job for us Software Developers. I belive this is cause for us to change our relationships with our careers of choice.&lt;/p&gt;

&lt;p&gt;But what if we thought of this as a good thing? What if writing less code ourselves allows us to return to the roots of our profession? Two books that I read at University, and a number of times after come to mind: The Mythical Man Month and the Pragmatic Programmer.&lt;/p&gt;

&lt;p&gt;These are two books that you would be mistaken as thinking are about programming. Well, they aren’t they describe the broader topic of the Software Development Lifecycle; the process that results in an artefact that we know as code.&lt;/p&gt;

&lt;p&gt;They describe what I think is the social aspect of our jobs. The discussions with our stakeholders, the experience of producing requirements and defining those.&lt;/p&gt;

&lt;p&gt;Whilst it has been a while, I regularly marveled at the artefacts discussed in the books. “Manuals” and “Functional Requirements”. Exhaustive lists of functional requirements:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;F1. Allow user to make a search from the home screen
    &lt;ul&gt;
      &lt;li&gt;F1.1. When the user taps the search bar, it opens the search interstitial&lt;/li&gt;
      &lt;li&gt;F1.2. yawn, these were so boring, even more annoying when QA told us our implementation was wrong&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;F2. Allow users to see their past searches
    &lt;ul&gt;
      &lt;li&gt;F2.1. …&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why was this the way? Well, code was expensive! Deploying accounting software to an IBM mainframe took time. Debugging took longer. You could say that code was expensive. A mistake could be costly, measuring twice with exhaustive requirements was a good thing.&lt;/p&gt;

&lt;p&gt;This isn’t the case nowadays. Software Development has made a shift left. We have concepts such as Continuous Integration and Deployment. If you make a mistake, or miss a requirement it’s fine we can just push a new release. Code became cheaper. We don’t write the &lt;a href=&quot;https://ntrs.nasa.gov/api/citations/19710071423/downloads/19710071423.pdf&quot;&gt;great&lt;/a&gt; &lt;a href=&quot;https://seriouscomputerist.atariverse.com/media/pdf/book/C%20Programming%20Language%20-%202nd%20Edition%20(OCR).pdf&quot;&gt;technical&lt;/a&gt; &lt;a href=&quot;https://ntrs.nasa.gov/api/citations/20000120144/downloads/20000120144.pdf&quot;&gt;documents&lt;/a&gt; of &lt;a href=&quot;https://static.googleusercontent.com/media/research.google.com/en//archive/gfs-sosp2003.pdf&quot;&gt;yesteryear&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But now, code has become incredibly cheap. It is now cheaper for my employer to ask Claude Code to resolve simple bugs. Let’s take a lifecycle issue, this does not require specialist knowledge. Claude Code just Googles the issue and finds a solution to the problem.&lt;/p&gt;

&lt;p&gt;So, how do I as a Software Developer justify my and my team mates roles in two to three years?&lt;/p&gt;

&lt;p&gt;What do all of those technical documents above have in common? They all exhaustively define requirements for a system. What do LLMs need to do a good job? Exhaustive requirements. They can’t Google what we need from them to satisfy our user requirements, but we can tell them that.&lt;/p&gt;

&lt;p&gt;In cheapening the cost of code, we’ve returned to a time where code was expensive. The similarity between them is the requirement for manuals and well defined requirements.&lt;/p&gt;

&lt;p&gt;So I think it is time to blow the dust off the covers of the books of our elders in order to rediscover the art of exhaustive definitions of requirements.&lt;/p&gt;
</description><description>Code just got cheap. Really cheap. Maybe it&apos;s time to dust off those old software engineering books.
</description><pubDate>Thu, 29 Jan 2026 00:00:00 +0000</pubDate>
        <link>https://jordanterry.co.uk/software-development-back-to-its-roots</link>
        <guid isPermaLink="true">https://jordanterry.co.uk/software-development-back-to-its-roots</guid><category>Software</category></item><item>
        <title>mortgage-cli</title>
        <description>&lt;p&gt;My wife and I are looking to buy a property in France. We want to rent it out and, ideally, have it pay for itself. Simple enough, right?&lt;/p&gt;

&lt;p&gt;The problem is there’s a lot to get your head around: French mortgage rules, notary fees, property taxes, management fees, and rental yields that vary wildly by location. We found ourselves running the same calculations over and over, tweaking one variable at a time. Is this property viable at 4% interest? What if we put 20% down instead of 15%? What rent do we actually need to break even?&lt;/p&gt;

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

&lt;p&gt;We’ve been through a few iterations trying to solve this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Spreadsheets&lt;/strong&gt; - The classic. Works until you want to compare multiple scenarios or share your assumptions with someone else.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Scrapers&lt;/strong&gt; - I built tools to pull property listings automatically. Turns out data ingestion is hard and websites don’t like being scraped.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;A website&lt;/strong&gt; - More engineering than the problem warranted.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the end, I landed on a CLI tool. Claude Code made building it remarkably quick, and a CLI fits how I actually want to use this: run a quick command, see if a property makes sense, move on.&lt;/p&gt;

&lt;h2 id=&quot;what-it-does&quot;&gt;What it does&lt;/h2&gt;

&lt;p&gt;The tool has two main concepts: &lt;strong&gt;profiles&lt;/strong&gt; and &lt;strong&gt;analysis&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A profile stores your investment parameters - things you don’t want to re-enter every time:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;How much you can put down&lt;/li&gt;
  &lt;li&gt;The interest rate you’ve been quoted&lt;/li&gt;
  &lt;li&gt;Your target rental income&lt;/li&gt;
  &lt;li&gt;French-specific costs (notary fees, property taxes, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then you can analyse properties against that profile:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mortgage-cli analyze --price 150000 --rent 900
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This calculates the break-even rent - the minimum you’d need to charge to cover all expenses - and compares it to your target. The output tells you whether the investment is viable (green), marginal (yellow), or a bad idea (red).&lt;/p&gt;

&lt;p&gt;For comparing multiple scenarios at once, there’s a sensitivity matrix:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mortgage-cli matrix --price-min 100000 --price-max 200000 --rent 1000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This shows how break-even rent varies across different price points and down payment percentages. Useful for understanding how much flexibility you have.&lt;/p&gt;

&lt;h2 id=&quot;why-a-cli&quot;&gt;Why a CLI?&lt;/h2&gt;

&lt;p&gt;I keep coming back to command-line tools for personal projects. They’re quick to build, easy to script, and you can pipe the output wherever you need it. Need a CSV for a spreadsheet? &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--format csv&lt;/code&gt;. Want to save scenarios for later? Redirect to a file.&lt;/p&gt;

&lt;h2 id=&quot;early-days&quot;&gt;Early days&lt;/h2&gt;

&lt;p&gt;These early days for tools like this are exciting. Pre-Claude Code, this project would have joined the graveyard of half-finished side projects. I’d have got the core logic working, lost momentum on the CLI interface, and never come back to it.&lt;/p&gt;

&lt;p&gt;Instead, I have something I actually use. Something with practical benefit for a real decision my wife and I are making. That’s the shift I keep noticing: the gap between “I should build something for this” and “I have something that works” has shrunk dramatically.&lt;/p&gt;

&lt;p&gt;The tool is up at &lt;a href=&quot;https://jordanterry.github.io/mortgage-cli/&quot;&gt;jordanterry.github.io/mortgage-cli&lt;/a&gt; if you’re interested.&lt;/p&gt;
</description><description>A CLI tool for analysing French rental property investments.
</description><pubDate>Thu, 08 Jan 2026 00:00:00 +0000</pubDate>
        <link>https://jordanterry.co.uk/mortgage-cli</link>
        <guid isPermaLink="true">https://jordanterry.co.uk/mortgage-cli</guid><category>Software</category><category>Projects</category></item><item>
        <title>Function Colouring and Structured Concurrency in Kotlin</title>
        <description>&lt;p&gt;In Kotlin, not all functions are created equal. Some are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;suspend&lt;/code&gt;, others are not. This distinction matters more than you might think.&lt;/p&gt;

&lt;p&gt;Bob Nystrom wrote a fantastic piece called &lt;a href=&quot;https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/&quot;&gt;What Color is Your Function?&lt;/a&gt; that introduces a helpful mental model. Functions have colours: red and blue. Each colour comes with rules:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Red functions can call blue functions&lt;/li&gt;
  &lt;li&gt;Red functions can call red functions&lt;/li&gt;
  &lt;li&gt;Blue functions can call blue functions&lt;/li&gt;
  &lt;li&gt;Blue functions &lt;strong&gt;cannot&lt;/strong&gt; call red functions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Kotlin, red maps to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;suspend&lt;/code&gt; functions. Blue maps to regular blocking functions. The metaphor maps cleanly.&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Red function&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetchUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Blue function&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;processUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Kotlin bends the rules slightly with coroutine builders like &lt;a href=&quot;https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launch&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt;&lt;/a&gt;. These let blue functions &lt;em&gt;start&lt;/em&gt; red work. But there’s a cost to bending rules.&lt;/p&gt;

&lt;h3 id=&quot;the-conductor&quot;&gt;The Conductor&lt;/h3&gt;

&lt;p&gt;The “magic” behind Kotlin coroutines is the &lt;a href=&quot;https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines/-continuation/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Continuation&lt;/code&gt;&lt;/a&gt;. Think of it as a conductor for an orchestra. It’s always there, orchestrating the asynchronous work: managing suspension points, state machines, and cooperative cancellation.&lt;/p&gt;

&lt;p&gt;The Kotlin compiler adds a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Continuation&lt;/code&gt; as the final parameter to every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;suspend&lt;/code&gt; function. When one suspend function calls another, the continuation travels with it. This is how &lt;a href=&quot;https://kotlinlang.org/docs/coroutines-basics.html#structured-concurrency&quot;&gt;structured concurrency&lt;/a&gt; works - when a parent coroutine is cancelled, all children are cancelled too.&lt;/p&gt;

&lt;p&gt;Here’s the problem: if a suspend function calls a blocking function, the continuation can’t travel into that call. It pauses outside until control returns. The conductor has left the room.&lt;/p&gt;

&lt;h3 id=&quot;crossing-the-boundary&quot;&gt;Crossing the Boundary&lt;/h3&gt;

&lt;p&gt;Consider this pattern:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;collectEvents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eventFlow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;collect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;handleEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Blue function&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handleEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;coroutineScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;launch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Red work inside a blue function&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The call stack goes: red -&amp;gt; blue -&amp;gt; red. We’ve crossed the boundary twice. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launch&lt;/code&gt; call returns immediately, and the handler continues. The launched coroutine runs on its own timeline.&lt;/p&gt;

&lt;p&gt;Three things happen:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Loss of Continuation&lt;/strong&gt; - The blocking function doesn’t receive the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Continuation&lt;/code&gt; from the suspend function above it. The conductor can’t orchestrate what happens inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launch&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Non-determinism&lt;/strong&gt; - Once the handler returns, cooperative sequencing is lost. We can’t guarantee the launched coroutine finishes before the handler does.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Detached lifecycle&lt;/strong&gt; - The new coroutine executes independently, which can result in multiple overlapping jobs if the same event is triggered rapidly.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;the-search-problem&quot;&gt;The Search Problem&lt;/h3&gt;

&lt;p&gt;Imagine a search bar. A user types “gold necklace”. Every keystroke produces an event: “g”, “go”, “gol”, “gold”, “gold “, and so on. Each event might trigger a network request.&lt;/p&gt;

&lt;p&gt;Requests vary in latency. The request for “gold” might complete &lt;em&gt;after&lt;/em&gt; the request for “gold necklace”. If you update your UI with whatever response arrives, you get stale results overwriting fresh ones.&lt;/p&gt;

&lt;p&gt;The manual fix looks like this:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SearchHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;coroutineScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CoroutineScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Job&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;job&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;coroutineScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;launch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searchRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;dispatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SearchResults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This works in the narrow sense - only the latest search survives. But we’ve reinvented concurrency control, and done it worse than what Kotlin already provides.&lt;/p&gt;

&lt;h3 id=&quot;keeping-the-conductor-in-the-room&quot;&gt;Keeping the Conductor in the Room&lt;/h3&gt;

&lt;p&gt;The fix is simple in principle: stay red. If we keep every step in the suspend world, we never lose the continuation.&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SearchHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;searchRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SearchRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searchRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SearchResults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Wire this up with Kotlin &lt;a href=&quot;https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Flow&lt;/code&gt;&lt;/a&gt;’s built-in operators:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;queryFlow&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;distinctUntilChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mapLatest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;searchHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;collect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Process result&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/map-latest.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mapLatest&lt;/code&gt;&lt;/a&gt; does what our manual job tracking tried to do, but correctly. Each new emission cancels any in-flight work before starting fresh. The continuation never leaks across the boundary. Cancellation propagates from the Flow collector through every emission.&lt;/p&gt;

&lt;h3 id=&quot;the-takeaway&quot;&gt;The Takeaway&lt;/h3&gt;

&lt;p&gt;Function coloring isn’t just a theoretical concept. It has practical consequences. Every time you cross the red/blue boundary with a coroutine builder inside a blocking function, you’re dismissing the conductor.&lt;/p&gt;

&lt;p&gt;For most code, this is fine. But for high-frequency events - search typing, rapid button taps, pull-to-refresh - the consequences compound. Out-of-order results, stale state, wasted work.&lt;/p&gt;

&lt;p&gt;The solution is to respect the colours. Keep red work red. Let Flow’s operators handle the cancellation semantics. The conductor knows what they’re doing.&lt;/p&gt;
</description><description>What happens when you cross the red/blue boundary in Kotlin coroutines?
</description><pubDate>Thu, 08 Jan 2026 00:00:00 +0000</pubDate>
        <link>https://jordanterry.co.uk/function-coloring-and-the-conductor</link>
        <guid isPermaLink="true">https://jordanterry.co.uk/function-coloring-and-the-conductor</guid><category>Software</category></item><item>
        <title>Grph</title>
        <description>&lt;p&gt;I’m curious about how agentic tools can better understand a codebase if they know how to traverse a Graph.&lt;/p&gt;

&lt;p&gt;For that reason, I (read Claude Code) have put together a CLI tool to easily consume a Graph. I call it &lt;a href=&quot;https://github.com/jordanterry/grph&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grph&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;how-does-this-work&quot;&gt;How does this work?&lt;/h2&gt;

&lt;p&gt;The CLI is a thin wrapper around the Python library &lt;a href=&quot;https://networkx.org&quot;&gt;NetworkX&lt;/a&gt;. The codebase exposes a number of features.&lt;/p&gt;

&lt;p&gt;The CLI ingests a graph formatted with GEXF format. This is a universal way of defining a graph.&lt;/p&gt;

&lt;h3 id=&quot;neighbours&quot;&gt;Neighbours&lt;/h3&gt;

&lt;p&gt;Imagine you have an airline route network. You’re at Gatwick and want to know where you can fly direct:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ grph neighbors routes.gexf Gatwick

Neighbors of Gatwick (depth=1, direction=all)

┌───────────┬─────────────┐
│ ID        │ Label       │
├───────────┼─────────────┤
│ Barcelona │ Barcelona   │
│ Amsterdam │ Amsterdam   │
│ Nice      │ Nice        │
│ Berlin    │ Berlin      │
│ Lisbon    │ Lisbon      │
└───────────┴─────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What about with one layover?&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ grph neighbors routes.gexf Gatwick --depth 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;shortest-path&quot;&gt;Shortest Path&lt;/h3&gt;

&lt;p&gt;How do I get from Edinburgh to Dubrovnik with the fewest connections?&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ grph path routes.gexf Edinburgh Dubrovnik

Path found (length: 3)
Edinburgh → Gatwick → Split → Dubrovnik
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or find all the options:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ grph all-paths routes.gexf Edinburgh Dubrovnik --max-depth 4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;pagerank&quot;&gt;PageRank&lt;/h3&gt;

&lt;p&gt;Which airports are the most critical hubs in the network? PageRank reveals where traffic flows through:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ grph centrality routes.gexf --type pagerank --top 5

┌───────────┬───────────┐
│ Node      │ Score     │
├───────────┼───────────┤
│ Gatwick   │ 0.0892    │
│ Barcelona │ 0.0734    │
│ Amsterdam │ 0.0651    │
│ Milan     │ 0.0612    │
│ Paris     │ 0.0598    │
└───────────┴───────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;and-more&quot;&gt;And more&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Find isolated route clusters&lt;/strong&gt; - are there parts of the network disconnected from each other?&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ grph components routes.gexf --list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Blast radius&lt;/strong&gt; - if Gatwick closes, which destinations become unreachable from Edinburgh?&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ grph reachable routes.gexf Edinburgh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Extract a subgraph&lt;/strong&gt; - pull out just the UK airports for focused analysis:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ grph subgraph routes.gexf --nodes Gatwick,Edinburgh,Manchester,Bristol --output uk-routes.gexf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;but-why&quot;&gt;But why?&lt;/h2&gt;

&lt;p&gt;I work in a medium-sized Android codebase. It has a lot of complexity baked into it: modularisation and a complex DI graph. I want Claude Code to be able to browse and understand both static and dynamic connections.&lt;/p&gt;

&lt;p&gt;The same queries that answer “how do I fly from Edinburgh to Dubrovnik?” can answer “how does &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MainActivity&lt;/code&gt; depend on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DatabaseHelper&lt;/code&gt;?” - it’s all just graph traversal.&lt;/p&gt;
</description><description>A CLI tool for graph traversal, built to help agentic tools understand codebases.
</description><pubDate>Sun, 04 Jan 2026 00:00:00 +0000</pubDate>
        <link>https://jordanterry.co.uk/grph</link>
        <guid isPermaLink="true">https://jordanterry.co.uk/grph</guid><category>Software</category><category>Projects</category></item><item>
        <title>Query Languages: Declarative and Imperative</title>
        <description>&lt;p&gt;Chapter two of &lt;a href=&quot;https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/&quot;&gt;Designing Data-Intensive Applications&lt;/a&gt; is called “Data Models and Query Languages”. It touches on a vast array of topics: the competition between Relational and Document models in the 70’s, through to the Relational model victory through to the introduction of Not-quite SQL databases. It takes us on a tour of one-to-many and many-to-many databases and explains how these relations dictate your underlying data model choices.&lt;/p&gt;

&lt;h2 id=&quot;query-languages&quot;&gt;Query Languages&lt;/h2&gt;

&lt;p&gt;The book proposes that there are two types of query languages: &lt;strong&gt;declarative&lt;/strong&gt; and &lt;strong&gt;imperative&lt;/strong&gt;. Before reading this, I had some vague notion of this, but I can’t say I had ever deeply thought about the relationship. I’ve found the section discussing Query Languages to be simple, but to have caused a small revolution in my mental model for data models and querying them.&lt;/p&gt;

&lt;h3 id=&quot;declarative&quot;&gt;Declarative&lt;/h3&gt;

&lt;p&gt;SQL (Structured Query Language) is a declarative query language. Everyone knows and &lt;del&gt;loves&lt;/del&gt; &lt;del&gt;hates&lt;/del&gt; loves it.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; 
  &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; 
  &lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; 
  &lt;span class=&quot;n&quot;&gt;family&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;shark&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is &lt;strong&gt;declarative&lt;/strong&gt; because the query is defining what data is required, not the &lt;em&gt;how&lt;/em&gt;. The query engine is responsible for that.&lt;/p&gt;

&lt;p&gt;Contrast this to the an &lt;strong&gt;imperative&lt;/strong&gt; alternative:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sharks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Animal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;sharks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arrayListOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Animal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;animal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;animal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;family&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Sharks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sharks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;animal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sharks&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Imperative instructions have been written to define the &lt;em&gt;how&lt;/em&gt;. We are locked into this implementation as it is the code we have written.&lt;/p&gt;

&lt;h4 id=&quot;whats-the-difference&quot;&gt;What’s the difference?&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;imperative&lt;/strong&gt; code above is going to always have a time-complexity of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;O(n)&lt;/code&gt; where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n&lt;/code&gt; is the count of animals. Parallelising the code above is going to be complex, too. The &lt;strong&gt;declarative&lt;/strong&gt; code is not tied to an implementation in any way. The query engine is responsible for applying queries; it knows how to leverage the computer it is running on to parallelise and run as quickly as possible. Perhaps we can introduce a new index that allows us to search with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;O(log n)&lt;/code&gt; time complexity? And then there are joins, joins are complex too! &lt;strong&gt;Imperative&lt;/strong&gt; joins require two outbound database connections, whereas the &lt;strong&gt;declarative&lt;/strong&gt; query runs on the same machine.&lt;/p&gt;

&lt;h3 id=&quot;where-else-does-this-apply&quot;&gt;Where else does this apply?&lt;/h3&gt;

&lt;p&gt;Data models don’t only exist on databases. If you write HTML, JSON or any UI code, you are creating a data model. Often, they are defining a tree.&lt;/p&gt;

&lt;p&gt;To lift another shark-related example from the book, here is a HTML data model:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;selected&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Sharks&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- More HTML --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Whales&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- More HTML --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Cascading Style Sheets (CSS) are used to style different elements in the data model.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;li&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.selected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;blue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;CSS is another declarative query language; it doesn’t tell CSS &lt;em&gt;how&lt;/em&gt; it should traverse the Document Object Model (DOM), that is left to the browser engine.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;imperative&lt;/strong&gt; alternative is to query the DOM using JavaScript. Each instruction for the machine will be typed out and executed, perhaps inefficiently!&lt;/p&gt;

&lt;h3 id=&quot;what-about-jetpack-compose&quot;&gt;What about Jetpack Compose?&lt;/h3&gt;

&lt;p&gt;All of this talk of data models and query languages is very exciting. It got me thinking about Jetpack Compose. For Android developers, this introduced the concept of &lt;strong&gt;declarative&lt;/strong&gt; UIs. We define &lt;em&gt;what&lt;/em&gt; we want but not the &lt;em&gt;how&lt;/em&gt;. The creation of the underlying UI tree is delegated to the library:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnimalList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Column&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Row&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Sharks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Row&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Whales&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At runtime, the UI can be updated to change the background colour:&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@Composable
&lt;span class=&quot;p&quot;&gt;fun AnimalList() {
&lt;/span&gt;    Column {
        Row {
            Text(
                &quot;Sharks&quot;,
&lt;span class=&quot;gi&quot;&gt;+                modifier = Modifier
+	                .background(Colors.Blue)
&lt;/span&gt;            )
        }
        Row {
            Text(&quot;Whales&quot;)
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Compose is both the data model and a query language. By changing the user interface at runtime, it knows &lt;em&gt;how&lt;/em&gt; to efficiently find relevant parts of the data model and update them. The user doesn’t need to know about slot-tables or gap buffers. The library handles these for us.&lt;/p&gt;

&lt;h3 id=&quot;obviously-duh&quot;&gt;Obviously, duh?&lt;/h3&gt;

&lt;p&gt;Maybe this isn’t “revolutionary” to others, but it was for me. The chapter introducing the relationship between databases and the web’s data models has helped me open my eyes to further relationships.&lt;/p&gt;

&lt;p&gt;I love this book!&lt;/p&gt;

</description><description>Database Query Lanugages and Data Models, how to they relate to Jetpack Compose?
</description><pubDate>Tue, 02 Dec 2025 00:08:00 +0000</pubDate>
        <link>https://jordanterry.co.uk/query-languages-declarative-and-imperative</link>
        <guid isPermaLink="true">https://jordanterry.co.uk/query-languages-declarative-and-imperative</guid><category>Software</category></item><item>
        <title>Nerd Club</title>
        <description>&lt;p&gt;I spend a lot of time working with technology that I don’t understand. They are black boxes to me; I write code, it does what I want it to do. Great!&lt;/p&gt;

&lt;p&gt;Let’s use SQLite as an example: I can write SQL, and I understand that indexes are efficient because they are B-trees. But I certainly do not understand how the query engine converts my SQL to an algorithm, nor do I understand how a B-tree is serialised to disk. I do have a rough mental model, though. Even if I did learn how B-tree storage worked, I still wouldn’t know how memory really works on disk; a black box remains.&lt;/p&gt;

&lt;p&gt;The fun of my job is to reduce those black boxes. But I have to choose where to do that. Will learning how flash memory works in conjunction with SQLite be the best use of my time? No.&lt;/p&gt;

&lt;p&gt;The biggest black box I have right now is the backend at work! I know roughly what happens: I send a network request, it gets routed to a computer in our fleet, data is hydrated (we fan out?), it reads data from memcache, and we map data to JSON. Oh, hang on, I don’t have a clue how that works. I have a rough blurry mental model, in fact, its a blackbox. I don’t have a clue how the backend works!&lt;/p&gt;

&lt;p&gt;Working on reducing the black box of the backend and the systems that run it makes me more effective at work.&lt;/p&gt;

&lt;p&gt;Whilst pondering how on earth I was going to tackle this in short succession, two of my colleagues mentioned the book &lt;a href=&quot;www.amazon.com/Designing-Data-Intensive-Applications-Reliable-Maintainable/dp/1449373321&quot;&gt;Designing Data-Intensive Applications&lt;/a&gt; written by &lt;a href=&quot;https://martin.kleppmann.com/&quot;&gt;Martin Kleppmann&lt;/a&gt;. Someone even had it on their desk!&lt;/p&gt;

&lt;p&gt;I vaguely knew of this book, and browsing through it I realised this book contained a literal map to understanding a lot of the black box that was the « backend »!&lt;/p&gt;

&lt;p&gt;One button click on Amazon and two days of waiting later I was the proud owner of the first edition! I now carry it around the office doing what I call « Performative Software Engineering ». I find it makes me look far more intelligent than I am!&lt;/p&gt;

&lt;p&gt;A few days later, we have a small group of us assembled. We’ve read the first two chapters now and it has been exciting! Our general pattern has been to read for a week or two, and then get together in a meeting room and talk about what we’ve learned. In the space of a few hours of talking I’ve had so many questions answered, and found that I know more than I thought and can engage on topics of systems at scale!&lt;/p&gt;

&lt;h3 id=&quot;chapter-one&quot;&gt;Chapter One&lt;/h3&gt;

&lt;p&gt;Day to day, I work on Etsy search. The first paragraph of the book neatly summarises the domain we work on:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Many applications today are data-intensive, as opposed to compute-intensive. Raw CPU power is rarely a limiting factor for these applications - bigger problems are usually the amount of data, the complexity of data, and the speed at which it is changing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A big chunk of our work is actually moving data around and analysing it. One colleague has been working on analysing consecutive sales, yes, algorithms do show up in your day-to-day work (&lt;a href=&quot;https://leetcode.com/problems/longest-consecutive-sequence/description/&quot;&gt;leetcode&lt;/a&gt;)!&lt;/p&gt;

&lt;p&gt;We do a lot of work analysing data and moving it from one place to another. Imagine analysing listings on sale and finding consecutive sales! This is data-intensive work, we have never discussed if we will overload CPUs.This paragraph (yes, the first in the book!) has opened my eyes to a new approach to thinking about what the blackbox of a backend does. This doesn’t change &lt;em&gt;how&lt;/em&gt; I do my work, my job has always been about marshalling data from one place to another. But it has made it explicit.&lt;/p&gt;

&lt;p&gt;The first chapter introduces four terms:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Faults&lt;/strong&gt; - Something that can go wrong in your application. These are split neatly into Hardware, Software, and Human errors. I’m a few chapters ahead « Fault » is a consistently used word throughout the book.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reliability&lt;/strong&gt; - This is how your application handles &lt;em&gt;faults&lt;/em&gt;, regardless of type. Unreliable software impacts money, trust and reputation. Reliability isn’t only for Nuclear Power Stations.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Scalability&lt;/strong&gt; - How does your program handle growth in load? The case study is how Twitter manages accounts with varying numbers of followers Tweeting. It’s not straightforward! We also learn how to quantify Load, perfect for Observability!&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Maintainability&lt;/strong&gt; - This introduces more terms: Operability, Simplicity and Evolvability. All descriptors of how to make operations run smoothly and make it easy for new devs to onboard and improve a system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are such simple terms, but already they have made their way into our day-to-day conversations. At first, this would evoke reactions of « ha! That’s from the book ». But we’ve realised these terms bring consistency and definitions to our conversations. It actually roots our conversations in a shared vocabulary that we all understand.&lt;/p&gt;

&lt;p&gt;Not only that, but we’ve applied these to what Etsy does. For example, how does clearing up data we’ve saved to a cache for an experiment fit into Maintainability? Our documentation and processes are now improving as we know our observability must be better for the Reliability of our systems!&lt;/p&gt;

&lt;h3 id=&quot;start-a-book-club&quot;&gt;Start a book club&lt;/h3&gt;

&lt;p&gt;I’ll leave with this: start a book club. Every one of your colleagues can help you learn by providing diffierent pieces of knowledge or points of view. You will leave every book club better off for it.&lt;/p&gt;

&lt;p&gt;We’ve also named our book club Nerd Club, because frankly who else is reading O’Reilly books?&lt;/p&gt;

</description><description>Nerd Club reads designing data intensive applications. 
</description><pubDate>Fri, 28 Nov 2025 00:08:00 +0000</pubDate>
        <link>https://jordanterry.co.uk/nerd-club</link>
        <guid isPermaLink="true">https://jordanterry.co.uk/nerd-club</guid><category>Software</category></item><item>
        <title>Android NYC Meetup - Reusable Modules: It&apos;s as Easy as ABI</title>
        <description>&lt;p&gt;An opportunity to talk at a naecent meetup group presented itself! I had recently given the talke “Reusable Modules: It’s as easy as ABI” in Mexico and thought it would be great to go again. I wasn’t able to make the improvements I wished to, due to illness and other work commitments. Regardless, it was a great night!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.meetup.com/new-york-android-engineers/events/310393618/?eventOrigin=your_events&quot;&gt;Meetup&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://speakerdeck.com/jordan_terry/reusable-modules-its-as-easy-as-abi&quot;&gt;Speakerdeck link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/tWsYtJYec_I?si=6WRsWzbAiN66XHSL&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
</description><description>&lt;p&gt;An opportunity to talk at a naecent meetup group presented itself! I had recently given the talke “Reusable Modules: It’s as easy as ABI” in Mexico and thought it would be great to go again. I wasn’t able to make the improvements I wished to, due to illness and other work commitments. Regardless, it was a great night!&lt;/p&gt;
</description><pubDate>Thu, 20 Nov 2025 00:08:00 +0000</pubDate>
        <link>https://jordanterry.co.uk/dev-community-android-reusable-modules</link>
        <guid isPermaLink="true">https://jordanterry.co.uk/dev-community-android-reusable-modules</guid><category>Talks</category></item><item>
        <title>App Dev Nights Mexico - Reusable Modules: It&apos;s as easy as ABI</title>
        <description>&lt;p&gt;I’ve been sitting on some improvements to a previous talk I have given by the same name. This presentation seeked to improve upon it. I’ve added an introduction to build systems and how they work, this provides a nice introduction into the world of Application Binary Interfaces.&lt;/p&gt;

&lt;p&gt;This was a special event for me as I traveled to it with work, I was able to meet some CDMX based colleagues in person. Note to remember, Mexico City is very high!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.meetup.com/mobile-development-mexico/events/311691310/?eventOrigin=your_events&quot;&gt;Meetup&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://speakerdeck.com/jordan_terry/reusable-modules-its-as-easy-as-abi&quot;&gt;Speakerdeck link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description><description>&lt;p&gt;I’ve been sitting on some improvements to a previous talk I have given by the same name. This presentation seeked to improve upon it. I’ve added an introduction to build systems and how they work, this provides a nice introduction into the world of Application Binary Interfaces.&lt;/p&gt;
</description><pubDate>Tue, 11 Nov 2025 00:08:00 +0000</pubDate>
        <link>https://jordanterry.co.uk/app-dev-nights-mexico-city</link>
        <guid isPermaLink="true">https://jordanterry.co.uk/app-dev-nights-mexico-city</guid><category>Talks</category></item></channel>
</rss>
