10 Jan 2026
Remote-SSH
One of the things I’ve done a bit in Visual Studio Code is using it’s ability to work on a different machine over SSH. I have a couple of LXCs on a server set up for different languages - one for C++ and another for Rust. They are things I don’t work in often, and I didn’t want to set them up on my laptop, but thought I might want them again sometime in the future.
28 July 2025
Ghostty is a terminal application that I don’t really need (it’s listed features either already exist in the MacOS terminal, or seem so esoteric or marginal that I can’t imagine any real benefit from them in my normal use), but I wanted to be one of the cool kids, so I thought I’d give it a try.
After fiddling around with the themes for a bit I renamed it to ’term-ghosty.app’ so I’d remember to use it (ie when I pop up spotlight and type ’term’ it will come up) and got on with my day. Ten minutes later I’d run into a problem.
7 July 2025
I’ve been meaning to write this for a couple of weeks, so let’s get to it - things are moving to fast to reflect too long; which is it’s own risk.
In March, I wrote about how I was using AI in coding , which was Codeium (now Windsurf) in VS Code for completions, and ChatGPT and Claude online for architecture questions and code gen that was more than half a function.
22 June 2025
Web pages are mostly just a collection of HTML, CSS, and JavaScript, so if we had some way of adding some of these into a web page, perhaps from our browser we could add new behaviour to a web page, right?
Yes; users have long used tools like Greasemonkey (or similar userscript managers) to inject scripts into pages. Better still, modern browsers expose JavaScript APIs that let us interact directly with the browser itself. Enter: browser extensions.
12 May 2025
When you’ve made a change to your web-app, do you run it then click around the new bits to check it works? Good start, but instead of doing that yourself, do it in a faster, more comprehensive and automated way with an end-to-end (E2E) testing setup using Cypress . Here’s how.
E2E
End to End testing is testing your app as a user might - by clicking links, entering data, looking at the screen and checking everything is okay, but it’s scripted like a unit test and the results are checked with assertions. Like unit testing this allows you to build up a collection of comprehensive tests that easily detect for unexpected behaviours - not just in the results of functions in your app, but in the user experience of the app.
28 Apr 2025
A Node/Express app I’m working on has been sprouting routes so much that the server.js file has swollen to 800 lines - way past my 200-250 comfort zone, so it’s time to organise the routes into their own files. That seems like a good topic for a beginner blog post, so let’s dive in.
Imagine we’ve written this little Node/Express app.
import express from "express";
import {
dbCustomersGet,
dbCustomersGetById,
dbCustomersDelete,
dbOrdersGet,
dbOrdersGetById,
dbOrdersGetByCustomerId,
dbOrdersDelete,
} from "./db.js";
const app = express();
app.set("view engine", "ejs");
const port = 3002;
app.use(express.urlencoded({ extended: true }));
app.get("/", (req, res) => {
res.redirect("/customers");
});
app.get("/customers", (req, res) => {
const customers = dbCustomersGet();
res.render("customers", { customers });
});
app.get("/customers/:id", (req, res) => {
const customer = dbCustomersGetById(req.params.id);
const orders = dbOrdersGetByCustomerId(req.params.id);
res.render("customer", { customer, orders });
});
app.get("/customers/:id/delete", (req, res) => {
dbCustomersDelete(req.params.id);
res.redirect("/customers");
});
app.get("/orders", (req, res) => {
const orders = dbOrdersGet();
res.render("orders", { orders });
});
app.get("/orders/:id", (req, res) => {
const order = dbOrdersGetById(req.params.id);
const customer = dbCustomersGetById(order.customerId);
res.render("order", { order, customer });
});
app.get("/orders/:id/delete", (req, res) => {
dbOrdersDelete(req.params.id);
res.redirect("/orders");
});
app.listen(port, () => {
console.log(`Listening on http://127.0.0.1:${port}`);
});
Although concocted, this would seem familiar to anyone who’s built a CRUD business app.
14 Apr 2025
I’ve been whipping up a little mock-database unit that has a few access functions but actually stores the data as arrays for a demo project for a post I’m writing. In the process I wrote this gem:
export function dbOrdersAdd(order) {
const orderCopy = { ...order };
// since id is a stringified number, finding the max is a bit of a mess
const maxId = orders.reduce((max, o) => Math.max(max, parseInt(o.id)), 0);
orderCopy.id = String(maxId + 1);
orders.push(orderCopy);
return { ...orderCopy };
}
In the comment I’m claiming the code is a bit of a mess (and from a readability point that’s true) but actually I love the elegance of using the reduce() method here.
31 Mar 2025
A large part of the reason for my use of Nginx Proxy manager over vanilla NGINX, is that it has built-in Let’s Encrypt certificate requesting and renewing. This works perfectly for all my public facing services, and until recently, my homelab services. Before I dive into how I’ve fixed the problem I ran into, I better explain how my homelab domain is set up, and before I do that, an over-simplified description of how the SSL system works is required
17 Mar 2025
For the longest time, I’ve been using Mocha (test runner) and Chai (assertion library) for my JS testing. They are reliable old friends.
One of the effects of the existence of Bun and Deno has been to spur Node onto adding some new features, so after appearing as an experimental feature in 18, the Node test runner dropped in Node 20.
I’m not sure if the familiar unit test layout of Mocha and Node is inherited from Jest, or comes from older testing frameworks of which JUnit and NUnit were the first ones I’d ever used. Before that I just used to write tests as lumps of assertions in regular code - which worked but wasn’t as pleasant to use as a proper unit test setup. Regardless, the system of bundling a few tests together and having them all run and spit out green ticks is not a new one.
3 Mar 2025
There’s still plenty of controversy about LLMs for coding, and not without reason. But I thought I’d run through what I’ve tried, and where I’ve landed for using AI. Also what the pitfalls are, where it’s useful and how it’s changed my practice.
Issues
Training data
The training data for large language models generally is problematic. There’s no doubt that they have been trained on copyright material. With code it’s slightly less murky since there is a high availability of good quality open source data with attached licenses to train models on. No doubt this include code written by people who don’t approve of it being used by AI, but I think the popular reading of most open source licenses is that using it for training is fine.