Categories
2026
Two bugs that taught me the rules
Some bugs are interesting because they’re subtle. These two were interesting because they were the exact opposite… in each case the tool had a hard rule I simply didn’t know about, and its error message couldn’t be …

Reviewed, then applied
The genuinely dangerous moment in infrastructure-as-code isn’t the apply. It’s the gap between the plan a human read and approved, and the change that actually runs a moment later. If those two are different computations …

One graph, not micro-stacks
Once an infrastructure repo has a few concerns in it (account hardening, the security baseline, the signing stack still to come) there’s a steady pressure to split them into separate stacks with separate state, and …

CI you include, not copy
Every infrastructure repo runs the same CI: lint the OpenTofu, scan it, validate it, plan, apply. The first repo, you write that .gitlab-ci.yml by hand. The second, you copy it. By the third, you’ve got three copies of …

One image for the whole toolchain
Every CI gate job across the infrastructure repos reaches for the same pile of tools: OpenTofu, tflint, trivy, checkov, gitleaks, terraform-docs, the AWS CLI. Installing that pile per job is both slow and quietly …

A 403 you can't fix in IAM
The OIDC post explained the handshake that lets a GitLab pipeline deploy to AWS with no stored key. This is the story of the first time I got it wrong, and spent an afternoon fixing the wrong thing. The error was a flat …

Pure-Rust Git, no git binary
go-tool-base’s VCS support has two halves that get confused for one. One half talks to forge APIs (GitHub, GitLab) for releases and pull requests. The other talks to the .git directory on disk: clone, history, diff, …

Routing security findings without the noise
Turning on GuardDuty and Security Hub gives you threat detection. It also gives you a firehose. And an alert system that dutifully forwards everything in that firehose isn’t monitoring, it’s a very efficient way of …

Why go-tool-base left GitHub for GitLab
A botched version bump made me stop and actually look at where go-tool-base lived, and I didn’t much like what I saw. GitHub had spent months quietly falling over, and when Mitchell Hashimoto (GitHub user #1299, no less) …

Why I hand-rolled every module
There are well-known community module libraries for AWS: Cloud Posse, the terraform-aws-modules collection, plenty more. Both terraform-aws-bootstrap and terraform-aws-security-baseline use almost none of them. Every …

Hardening the account that will hold the keys
Bootstrapping the account got it ready: somewhere to store state, an identity to deploy as, enough for the next tofu apply to run. Ready is not the same as safe. An account with no audit trail, nothing watching it, and …

No access keys in CI
A long-lived AWS access key, sitting in a CI system, is just about the single credential I’d most like to be rid of. It’s powerful, it never expires unless someone remembers to rotate it (nobody remembers to rotate it), …

The chicken-and-egg of remote state
Here’s a puzzle that every infrastructure-as-code setup hits exactly once, right at the very beginning, and then never again. An OpenTofu stack stores its state in a backend. The bootstrap stack I wrote about last time …

Secrets that scrub themselves from RAM
A while ago I worked out where a CLI should keep your API key: env var, OS keychain, or, grudgingly, a literal in the config file. That answers where the secret lives. It says nothing about what happens to it once it’s …

Supporting a provider, or actually using it
If your CLI tool talks to an AI model, you don’t want to hard-wire one vendor. So you reach for a single client interface over several providers, which is the right call. The trap is the next step: build that interface …

A state bucket that defends itself
OpenTofu’s remote state file is, quietly, the most sensitive thing in an infrastructure repo. It’s a plain JSON document listing every resource you manage, every ID, and, depending on your providers, the odd secret in …

The bootstrap that does almost nothing
A brand-new AWS account is a slightly nerve-wracking thing. It can do almost anything, it’s hardened against almost nothing, and the list of stuff you ought to set up before you trust it with anything real is long. The …

Errors without an error handler
In the porting post I said go-tool-base’s error handler was one of the bits that didn’t survive the move to Rust, and promised to come back to it. Here’s the come-back. The short version is that Rust hands you, for free, …

Two kinds of feature flag
go-tool-base has feature flags: switches that decide which built-in commands are live in a given run. rust-tool-base has those too. But it also has a second, completely separate kind of flag, and the difference between …

A framework that contains no unsafe
“It’s written in Rust” gets thrown around as if it were a memory-safety guarantee. It mostly isn’t. Rust is memory-safe by default, which is a wonderful thing, but the unsafe keyword exists precisely so any crate, any …

Reloading config without a restart
A config file changes. Someone edits a setting, rotates a credential, flips a feature flag. How does the running process find out? For most processes the answer is blunt: it doesn’t, until you restart it. For a …

Waivers with an expiry date
A vulnerability scanner gives you a yes or a no. Is there a known advisory on a path you actually use? Yes, or no. That’s genuinely useful, and you should run one. But it’s a snapshot, taken on the day you ask, and …

A signing key needs somewhere to live
I left a door open a couple of posts ago, and it’s been quietly bothering me ever since. When I wrote about verifying your own downloads, I was honest that a checksum sitting next to the binary only catches accidents. …

A builder that won't compile if you forget a field
go-tool-base configures things with functional options, and if you forget a required one, the best case is a runtime failure and the worst case is an empty value sailing silently into everything downstream. Most builder …

Verifying your own downloads: how I solved it for self-updating CLI tools
Way back in the introduction I promised I’d come back to the self-update integrity checks. Here we are. And the honest starting point is a slightly uncomfortable admission: for a good long while, go-tool-base’s update …

Registering commands without life before main
I ended the last post promising to show how a Rust command registers itself when the language flatly refuses to run any of your code before main(). This is that post, and it’s a lovely example of reaching the same …

What survives a port, and what doesn't
Rebuilding go-tool-base in Rust turned out to be the most honest design review I’ve ever sat through, and I didn’t have to do anything except keep going. Porting a framework into a language with completely different …

The blank import that keeps a dependency out of your binary
go-tool-base can stash your credentials in the OS keychain, which most people building on it are perfectly happy about. But some of them ship into regulated and air-gapped environments where the binary isn’t permitted to …

rust-tool-base: the same idea, in a language that argues back
I built go-tool-base because I was sick of rebuilding the same CLI scaffolding every time I started a new Go tool. You’d think that would have taught me a lesson about doing things more than once. Apparently not, because …

Where should a CLI keep your API keys?
Your CLI tool needs the user’s API key. It has to come from somewhere, and it has to survive between runs, so the obvious move is to ask once and write it into the config file. One tidy api_key: line. Job done. It works …

I had the framework audited: every finding was the same shape
When a real security audit lands back in your inbox, the temptation is to read it as a shopping list of unrelated mistakes. Fix one, fix the next, tick them off, move on. I did exactly that the first time. The second …

The test-mocking pattern that races
I’m going to tell you about a bug go-tool-base shipped, because it’s one of those bugs that’s so reasonable-looking you’ll find it in textbooks, conference talks, and an awful lot of otherwise excellent Go code. We had …

Testing code that calls an LLM: yes, you actually can
“You can’t test code that calls an AI.” I’ve heard it said with great confidence, and it’s half right, which is the most dangerous kind of right. You genuinely can’t assert on what a non-deterministic model says. But the …

The AI provider that isn't an API
go-tool-base’s chat package puts five AI providers behind one interface. Four of them are exactly what you’d guess: HTTP calls to OpenAI, Claude, Gemini, and anything OpenAI-compatible. The fifth one isn’t an API at all. …

AI conversations you can resume
An AI conversation is, fundamentally, its own history. The model’s next answer depends on everything said so far. And a CLI tool, by its very nature, forgets everything the moment it exits. Put those two facts together …

An AI agent that has to make the build pass
Most AI code generation works on a charming little principle I’ll call generate-and-hope. The model writes the code, the model stops at the closing brace, and whether the thing actually compiles is left as an exercise …

Stop regex-ing the LLM's prose
Ask an LLM a question and it hands you back prose. Lovely to read, miserable to program against. You wanted the one number buried in the middle of it, and now you’re writing a regular expression to fish a word out of …

Telemetry that asks first
Usage telemetry is genuinely useful. Knowing which commands people actually run, where the errors cluster, whether anyone ever touched the feature you spent a fortnight on… that’s the stuff that makes you a better …

Nobody reads the manual
Let me describe the actual lifecycle of a user meeting your CLI tool, because it’s a bit humbling. They run it. It doesn’t quite do what they expected. They run it again with --help. They get a wall of monospaced flag …

Letting the AI call your Go functions
An AI that can only produce text can describe your system. An AI that can call your Go functions can actually operate it. That gap, between describing and doing, is the difference between a chatbot and something …

BDD where it earns its place, and nowhere else
I have a slightly complicated relationship with BDD. I’ve watched it turn a tangled test suite into something the whole team could read and reason about, and I’ve watched it turn a perfectly good unit test into a …

An AI interface that fits on one screen
The moment you decide a CLI tool should talk to an LLM, there’s a strong gravitational pull towards reaching for LangChain, or one of its many relatives. It’s the obvious move. It’s also, for most CLI work, a bit like …

Half your users don't have eyes
Run a command in your favourite CLI tool and look at what comes back. Colour. Neatly aligned columns. A friendly little summary sentence. Lovely… if you happen to be a human with eyes. But a good half of any tool’s users …

Middleware for CLI commands, not just web servers
Every CLI tool past a certain size grows a category of logic that doesn’t really belong to any one command, and yet has to happen for loads of them. Timing. An auth check. Panic recovery, so a crash becomes a clean error …

Lifecycle management for when your CLI grows up into a service
There’s a moment in the life of a lot of CLI tools where they stop being a CLI tool. Nobody quite decides it. It just happens. Someone needs the thing to also expose a little HTTP endpoint, or poll a queue, or run a …

A logging interface that doesn't leak its backend
The same tool, in two different lives, wants two completely different kinds of log. On my laptop I want logs I can actually read: colour, alignment, friendly timestamps. The very same tool running as a daemon in a …

Errors that tell the user what to do next
Here’s an error message I’ve been on the receiving end of more times than I’d care to count: error: failed to read config file True. Also completely useless! I now know something is broken and I haven’t the faintest idea …

Props: the container that does the heavy lifting
I name-dropped Props back in the introduction and then rather glossed over it, which was a bit unfair of me, because it’s the single most important design decision in the whole framework. So let’s give it the attention …

Many embedded filesystems, one merged view
Go’s embed package is one of those features that makes you slightly giddy the first time you use it. One //go:embed directive and your default config, your templates, your docs are all baked into the binary. The tool …

Scaffolding that respects your edits
When I introduced go-tool-base I made a passing promise to come back to “the generator that won’t clobber your edits”. This is me keeping it, partly because it’s the feature I’m quietly most proud of, and partly because …

Design your whole CLI in one file
Here’s a question that sounds trivial and really isn’t: where, exactly, does a CLI tool’s structure live? Not the logic of each command… the structure. Which commands exist, what they’re called, which flags they take, …

Your CLI is already an AI tool
“Make it work with AI” has become one of those requests that lands on a developer’s desk with a thud and not much further detail attached. My instinct, the first time, was to brace for a big lump of integration work… a …

go-tool-base: I got tired of reinventing the wheel
If you’ve written more than two or three command-line tools in Go, you’ll recognise the shape of the first afternoon. I certainly do! You reach for Cobra for the command tree, Viper for config, and then you start the …

2020
Migrating away from Mediawiki and how to export its data
I like Mediawiki, it is a simple tool capable of doing a lot and can be very flexible and easy to customise. However its not always the right solution! I had a situation where we needed to migrate away from using it for …
Pre-populating Neo4J using Kubernetes Init Containers and neo4j-admin import
Recently there has been an uptake in the use of Neo4j by the Data Scientists. This is a good thing! they are wanting to use the right tool for the job. However we need to run it inside our k8s cluster as a portable …

Adding Ambient Sounds to your Discord Server On LInux
I’m a Dungeon Master! I don’t mean that in the S&M sense! As in the game Dungeons & Dragons (https://dnd.wizards.com), where I run a weekly game as well as take part in a couple of campaigns as a player. It’s a lot of …

Encrypting additional drives with LUKS on Linux
Encryption is king nowadays with everyone having mobile devices. We have a significant number of people on laptops that travel around and also workstations that live in open plan offices. This means we encrypt all of our …

2019
Connecting to Ubuntu 18.04+ using RDP
We have a mix of different setups that the Software Engineer and Data Scientists use to get their work done. There are some using just Linux on laptops, Some on MacBooks and some on the various versions of Windows. For …
Technical CV writing is hard
Recruiting people is effing hard! That’s all… I’ll get back to reading through CVs now and let you get on with your day! Medicines Discovery Catapult is, at the time of writing, recruiting Software Engineers, and as …

Dell DisplayLink D6000 & Ubuntu 18.04+ Issues
I love Ubuntu… I’m pretty fond of dell kit too! So I was rather chuffed when I started working at Medicines Discovery Catapult because they let me have both. When you look at my desk it looks like it could be an advert …

Project Slayer: The Critical Path
The first of my new round of talk abstracts! In all honesty this isn’t a talk but something more that came out of a very drunken Saturday night at #phpbnl19. There were a bunch of us sat talking and somehow the topic of …

A reboot and a legacy moniker
So… my last post was a good 2 years ago now…. Hi how have you been? It’s been a very busy couple of years with a lot of stuff shifting in my personal life meaning things inevitably take a back seat. However its been long …
2016
Monty Python explains why your project failed!
As part of the attempt to develop my profile as a speaker, I’ve realised that I sometimes need to explain a few of my current talk abstracts a bit too much. This is mainly due to my lack of experience writing them and …

Using Gmail aliases with Evolution
If your anything like me you have a large number of email aliases that you use with Gmail which is great. However I use Evolution as a mail client more often than not when using Gnome3 as a desktop. It’s very easy to set …
Are you a Good Code Scout? - NomadPHP lightning talk video
https://www.youtube.com/watch?v=Tt0lnauF5lI Just before the Christmas period I was lucky enough to be able to give my “Are you a good Code Scout?” talk as a lightning talk for NomadPHP. Here is the video that was …
2015
A metaphor about PSR-7 and Middleware for non-developers
Never one to shy away from coming up with a metaphor for explaining something technical I found myself having to come up with one on the spot for PSR-7 and Middleware while at the recent PHPNW15 Conference. Normally my …

Wow... What a Conference
So I attended the PHPNW15 conference this weekend and what a weekend. I’ve been an attendee of the conference for a number of years and have always enjoyed it immensely. However this year turned out to be something …
Badges & Stickers
One of the most prominent things I’ve been asked about regarding my promoting being a Good Code Scout, is where can we get the badges? Following on from a number of questions and subsequent tweets about it …

What is a PHP Scout
Recently I’ve had a lot of people asking me what a PHP Scout is! I thought it would be a good opportunity to explain. To understand what a PHP Scout is it helps to know a little of the background basics of Scouting in …
The PHP Scout Membership Badge
As the PHP Boy Scout I’m having some badges made and I wanted to introduce the all new PHP Scout Membership Badge.

My first ever public appearance as PHPBoyScout
So it’s finally happened! I stood up in front of a group of developers and gave a lightning talk about how Scouting Principles should be applied to every day development. [slideshare …

Goodbye Dev in Charge
Over all the time that I’ve been a developer I’ve had people telling me that I should get in front of an audience and speak. However I’ve always suffered from a rather bad case of ‘Imposter Syndrome’ which meant my …
Free Open Source Website for Scouts
I’ve been a Scout Leader for a few years now and the District I work within have very little by way of internet presence. As a bit of a pet project I started building a simple Scout based website for them to use.

2013
Flexbox cross browser
Despite having been around for a while and having been through a couple of revisions, its support across browsers can vary greatly. From “Candidate Recommendation” on Chrome/Opera, “legacy flexbox” on Firefox and no …
Disabling Cache in Silverstripe 3.1
While working with Silverstripe we found ourselves having to run “?flush=1” a lot to clear the Cache. To switch it off, while you work, add the following to your mysite/_config.php: …
Creating Custom Routes in Silverstripe 3.1
We wanted to create a Route to our custom Products Controller in our products module for SilverStripe 3.1, such as: “http://www.examplesite.com/products/” However looking at the Controller Documentation it was not clear …
Set up SilverStripe 3.1 using only Git (No Composer)
We recently tried to use composer to set up SilverStripe 3.1, but ended up with a dependency nightmare. In order to work around this we decided to make use of Git submodules. First set up your Git repository and run: git …
Enabling MYSQL_CLIENT_INTERACTIVE with Doctrine 2 on Rackspace Cloud Database
We recently ran into problem using Doctrine 2 connecting to a Rackspace Cloud Database using the MySqli Driver. Problem: We have a long running PHP script that can sometimes run for hours at a time whilst processing …
Installing PECL extensions for Zend Server 6
Recently we have revisited using Zend Server for some of our projects and decided to give the new version 6 a chance to prove itself. Overall its a big improvement over version 5. There are still some things that are …
Better Output for MySQL Select Command Using \G
If you ever find yourself using MySQL via command line and end up with something like this: And thought there must be another way, well here it is: Use \G instead of ; at the end of your select command. For example: …
Introducing ZuQ - A Simple ZeroMQ Queuing Daemon
We recently had the need to create a queuing system to replace an implementation of RabbitMQ that was being used on a previous project. The reasoning behind this is that the requirements of the project required a very …
Glorious Gluster - How to setup GlusterFS on Rackspace Cloud and Ubuntu 12.10
A few of our projects recently called for a distributed file-system that provided high availability and redundancy. After a tip off from a fellow techie and a quick browse around the net it appeared that a solution …
Docblock, Oh Docblock, wherefore art thou Docblock (hint: Zend Optimizer Plus lost them)
tl;dr> I make a terrible assumption about Zend Optimizer+ and am corrected by Dominic in the comments; Terrible post title I know but its the best I could come up with. I’ve just come up for air after spending the …
Our Redmine install died, We all cried!
We have been using redmine for quite a long time and a few months ago attempted to upgrade from 1.3 to 2.something. Unfortunately I (quite typically) borked the installation and since then its been hobbling along after …
Quick and easy setup of and connection to NRPE on Ubuntu
About NRPE NRPE (Nagios Remote Plugin Executor) is a useful tool that allows you to execute scripts on remote servers and return the output for ingestion by some form of monitoring software. Setup We currently have our …
Debug PHP CLI on Remote Server with Xdebug and PHPStorm
This was a head scratcher when I ran into this yesterday and I thought I would share my solution to the following scenario: I need to debug PHP Command Line script, located on Remote LAMP Virtual WebServer running in …
2012
Compiling PHP 5.4 on Ubuntu 12.04
So recently I’ve been working with PHP 5.4 a LOT. Unfortunately Ubuntu (my main dev environment) is behind the times. So I’m resorting to compiling PHP manually. Not a daunting as it may first appear. The really tricky …
Compiling Apache 2.4 on Ubuntu 12.04
I’ve decided that I need to up my game when it comes to webservers. However I’m not yet ready to switch to Nginx or one of the other webservers out in the wild as I need something up and running rapidly. Granted the …
Rsync and custom SSH commands
Rsync is a great tool but can be a pain if you have to jump through hoops to connect via ssh such as connecting via a different port. A simple solution is to use the -e flag (also knows as –rsh=COMMAND). This flag allows …
Nice New Nexus7
This morning I woke up to an email telling me that my Nexus7 that I had ordered 3 weeks ago was… “out for delivery”. I couldn’t contain my excitement. I sat patiently waiting by my door. Finally 11 o’clock rolls around …

Registering custom view helpers in ZF2
If you want to register custom view helpers with a module you can do so by using the service location built into the Skeleton Application and creating a module config that looks something like. return array( …
Bootstrapping ZF2 Forms
So… With the release of beta 5 for Zend Framework 2 I thought it time for me to tidy up and fix a few modules I created back at beta 3. Now I’m a big fan of Twitter Bootstrap CSS framework as I’m sure a lot of other …
Loaded Testing
I recently had to do some load testing for a site recently that would allow me to test in excess of 100k requests in a 60 second period… So I decided to do some testing using JMeter as it seemed like a suitable tool for …