Journal

Short logs. Post-mortems, one-paragraph wins, ADRs and bench notes from client work and the home cluster.

Entries
62
Topics
39
Work
22
Span
2019 – 2026

62 entries

September 2026

3 entries
SEP 26
23
Personal#sec#adr#postgres#web 1 min readRead entry

Authorisation moves back into the app

23 Sep 2026

Why the rewrite trades row-level security for one data-access layer and a test suite.

In 2025 the inventory moved its authorisation into the database ([[let-rls-do-the-filtering|Let RLS do the filtering]]). The rewrite moves it back, on purpose.

2026-09-23·#authorisation-moves-back-into-the-app
SEP 26
23
Personal#adr#db#postgres#web 1 min readRead entry

Dropping Supabase, keeping Postgres

23 Sep 2026

The Next.js rewrite of the parts inventory moves to plain Postgres, Drizzle and Better Auth.

The first plan for the rewrite migrated the live Supabase database in place. Reviewing that plan ended it. New uuid ids would have broken the Flutter app in daily use, a self-hoster could never build the schema from an empty database, and the migration SQL had a recursive row-level security policy and a path from admin to owner.

2026-09-23·#dropping-supabase-keeping-postgres
SEP 26
15
Work#gcp#benchmarking#ops 1 min readRead entry

Wait on quota, don't fail the shape

15 Sep 2026

Quota as a scheduling signal rather than an error.

Porting [[fleet-that-launches-itself|the benchmark fleet]] to a third cloud, the launchers kept failing shapes on quota errors. The fix was to treat quota as a scheduling signal: wait and retry when CPUs or disk are exhausted, wait on expired tokens, keep polling when a describe call fails.

2026-09-15·#wait-on-quota

August 2026

1 entry
AUG 26
23
Work#ops#data 1 min readRead entry

The vendor feed came back empty

23 Aug 2026

A weekly refresh that trusted an upstream feed, and the abort gate it earned.

A weekly multi-cloud price refresh had run without a miss for the better part of a year. Then one upstream feed returned nothing: not an error, an empty result, and the pipeline was happy to publish a catalog with a whole cloud missing. Nothing failed, which was the problem.

2026-08-23·#vendor-feed-empty

June 2026

2 entries
JUN 26
23
Work#web#ops#node 1 min readRead entry

The chart that printed blank

23 Jun 2026

A race between chart animation and a screenshot, diagnosed from both sides in one day.

Charts loaded through the incremental path rendered blank in the print PDF. On the page side, the placeholder chart had no type and the update path only updated, so a chart whose type changed was never recreated. On the print side, the screenshot fired while the animation was still drawing.

2026-06-23·#chart-that-printed-blank
JUN 26
09
Personal#ops#k8s 1 min readRead entry

Decommissioning without deleting the docs

9 Jun 2026

A clean decommission checklist, including what lives outside the repository.

The CV builder that ran on the cluster was replaced by a static site, so its namespace, volume, backups, ingresses and certificates were removed. The documentation was not: the pages were marked decommissioned and kept, with the removal commit linked.

2026-06-09·#decommissioning-without-deleting-docs

May 2026

6 entries
MAY 26
26
Work#aws#cdk#iac#s3 1 min readRead entry

Two stacks, one bucket notification

26 May 2026

Two CDK stacks configuring one bucket overwrite each other silently.

Two stacks each declared a notification on the same bucket. Each deploy overwrote the other's configuration, and nothing failed; the loader simply stopped being told about uploads.

2026-05-26·#two-stacks-one-bucket
MAY 26
11
Personal#db#ops#postgres#supabase 1 min readRead entry

The restore that broke logins

11 May 2026

A restore without ownership, and the confusing pair of errors it produces.

A cutover restore used the no-owner option to avoid role errors. It worked, and the next start of the auth service failed on a missing schema, and logins failed on a missing table. Both existed. They belonged to the wrong role.

2026-05-11·#restore-that-broke-logins
MAY 26
07
Personal#k8s#ops#supabase 1 min readRead entry

First app on the cluster

7 May 2026

The components inventory live on k3s, with the exact list of what went wrong.

The components inventory went live on the cluster against a self-hosted Supabase. The runbook keeps the list: the registry token that had to exist before the first pull, two configuration values to change, a race between namespace creation and the first apply, the rollout pattern for an image tagged latest, and an API gateway that did not reload its routes until restarted.

2026-05-07·#first-app-on-the-cluster
MAY 26
06
Personal#db#ops#postgres 1 min readRead entry

Backups that restore nothing

6 May 2026

A backup job that exited 0 and restored zero rows, and the drill that caught it.

The nightly backup piped a full dump straight into a restore and exited 0 every night. The first real drill restored zero rows. Three causes stacked: a client-server version mismatch in the dump format, a schema shape pre-created by the chart bootstrap, and a parser error that cascaded silently.

2026-05-06·#backups-that-restore-nothing
MAY 26
06
Personal#net#k8s 1 min readRead entry

systemd-resolved stub breaks CoreDNS

6 May 2026

The second of two Hetzner and Ubuntu 24.04 traps; short and easy to miss.

Pods could resolve nothing. CoreDNS was forwarding to the node's resolver, and on Ubuntu 24.04 that file points at the systemd-resolved stub on a loopback address the pods cannot reach.

2026-05-06·#systemd-resolved-breaks-coredns
MAY 26
04
Personal#adr#db#k8s#postgres 1 min readRead entry

ADR: defer CloudNativePG

4 May 2026

Bundled Postgres per project for now, with written triggers to revisit.

An operator-managed Postgres was evaluated, an image was even built for it, and the decision was to defer: each project keeps the Postgres bundled with its Supabase release, backed up nightly.

2026-05-04·#adr-defer-cloudnativepg

April 2026

2 entries
APR 26
26
Personal#k8s#iac#obs 1 min readRead entry

Four VMs, one k3s cluster, one repo

26 Apr 2026

The start of the self-hosted platform every later entry runs on.

A k3s cluster on four Hetzner Cloud machines, one control plane and three workers, declared in a new infrastructure repository: Traefik for ingress, cert-manager for certificates, ArgoCD for GitOps, kube-prometheus-stack and Loki for observability.

2026-04-26·#four-vms-one-cluster
APR 26
26
Personal#adr#ops#k8s#obs 1 min readRead entry

Why the inventory left Coolify

26 Apr 2026

Coolify deployed the app fast; backups, logs and alerts were what it could not give me.

Coolify made the first deploy of the inventory quick: [[fifty-minutes-after-coolify|five fixes in fifty minutes]]. Running the app was another matter. Backups could not be managed the way I needed, the containers left little room for fine-tuning, there were no reports or alerts, and the logs were close to useless when something broke.

2026-04-26·#why-the-inventory-left-coolify

February 2026

2 entries
FEB 26
13
Work#aws#benchmarking#bash 1 min readRead entry

One launch script for every generation

13 Feb 2026

Replacing a per-instance script matrix with detection at boot.

The [[fleet-that-launches-itself|first fleet]] had a script per instance type. It worked until the matrix had to change, and then every change was a dozen edits. The replacement is one script that decides at boot: swap sized from the memory it finds, CPU generation detected from the processor, compiler flags chosen per generation.

2026-02-13·#one-launch-script
FEB 26
06
Work#aws#benchmarking#ops#bash 1 min readRead entry

A fleet that launches itself

6 Feb 2026

What has to be in user-data for a machine to finish a job with nobody watching.

A benchmarking campaign across hundreds of instance types cannot have a person watching each one. Each instance boots from a user-data script that installs the toolchain, runs the benchmark, uploads the results and terminates itself. The first run where every report came back clean was the milestone.

2026-02-06·#fleet-that-launches-itself

January 2026

2 entries
JAN 26
19
Work#data#ops#aws#serverless 1 min readRead entry

Which failures should block the pipeline

19 Jan 2026

How a hard validation gate turned into a warning, and why that was right.

A validation-only state machine with detailed notifications, so the checks could be run without moving data. One check, a name mismatch between two records, kept blocking runs for cases that were legitimate. For months it grew exceptions, one per case.

2026-01-19·#which-failures-block
JAN 26
08
Work#data#aws#ops 1 min readRead entry

Rollback added at 16:30, muted at 16:55

8 Jan 2026

Why a rollback that runs on every failure can be worse than none.

An Iceberg insert stage went into the analytics pipeline in the morning. By half past four it had a rollback on failure; by five to five the rollback was muted.

2026-01-08·#rollback-added-then-muted

November 2025

2 entries
NOV 25
27
Personal#ops#web#flutter#supabase 1 min readRead entry

Fifty minutes after the first Coolify deploy

27 Nov 2025

Five fixes for Flutter web behind nginx, each a reusable gotcha.

First self-hosted deploy on Coolify, then five fixes in fifty minutes: a deprecated renderer flag in the Dockerfile, stale HTML served from cache after a new build, nginx location blocks in the wrong order, row-level-security errors leaking raw into the UI.

2025-11-27·#fifty-minutes-after-coolify
NOV 25
27
Personal#sec#db#supabase#postgres 1 min readRead entry

Let RLS do the filtering

27 Nov 2025

Moving authorisation from the client into the database, and the code that disappeared.

Every query in the app filtered by the current user's id on the client. Supabase row-level security already did that on the server, so the client filters were both redundant and a false sense of safety.

2025-11-27·#let-rls-do-the-filtering

October 2025

1 entry
OCT 25
07
Work#llm#aws#serverless 1 min readRead entry

The LLM call moves behind a queue

7 Oct 2025

A model call is too slow for a request-response API behind a gateway timeout.

The first version called the model synchronously inside the API handler. It worked in testing and timed out in production, because a gateway waits thirty seconds and a model does not promise to answer in thirty seconds.

2025-10-07·#llm-call-behind-a-queue

September 2025

1 entry
SEP 25
18
Work#aws#ops#serverless 1 min readRead entry

Retiring Lambdas with a restore path

18 Sep 2025

Archive, verify, delete, keep a way back.

Retiring functions is easy; retiring them safely is a sequence. Archive the code and configuration first, verify that the archive restores, delete, and keep the restore script next to the archive.

2025-09-18·#retiring-lambdas-restore-path

July 2025

5 entries
JUL 25
31
Personal#adr#web#flutter#supabase 1 min readRead entry

Flatten the architecture, keep the app

31 Jul 2025

An honest ADR on removing abstraction from a small app.

The components inventory was rewritten from Next.js with PostgreSQL and Docker to Flutter web on Supabase, and its layered architecture collapsed into a flat tree of services, providers and pages.

2025-07-31·#flatten-the-architecture
JUL 25
28
Personal#iot#embedded#adr#supabase 1 min readRead entry

A LoRa star for garage and mailbox

28 Jul 2025

One hub on Wi-Fi, battery nodes talking to it point to point, for places Wi-Fi does not reach.

The goal: see what happens where Wi-Fi does not reach, in a garage one or two floors down, at the mailbox, on the veranda. The design is a star. One hub indoors stays on Wi-Fi; battery nodes sleep almost all the time, wake to send one frame with all their readings, listen two seconds for commands, and send an alarm at once when a door or motion pin fires.

2025-07-28·#lora-star-garage-and-mailbox
JUL 25
28
Personal#sec#iot#embedded 1 min readRead entry

Pairing a sensor with no screen

28 Jul 2025

A hub key that never leaves the chip, a household key handed out at pairing, revocation by deleting a row.

Security was designed before the firmware. The hub creates its private key on first boot, keeps it in the chip, and gets a signed certificate from a certificate request; it talks to the backend over mutual TLS. Nodes ship with nothing secret, only their unique ID.

2025-07-28·#pairing-a-sensor-with-no-screen
JUL 25
27
Personal#embedded#hw 1 min readRead entry

Porting the panel driver to an ESP32

27 Jul 2025

From Good Display's Arduino sample to a PlatformIO project, and an adapter board of my own.

Good Display's sample drove the panel from an Arduino board. Moving it to an ESP32 meant a PlatformIO project, the driver as a library, and a new pin map: busy, reset, data or command, and chip select on ESP32 pins, with SPI on its defaults.

2025-07-27·#panel-driver-to-an-esp32
JUL 25
21
Work#java#perf#ops#etl 1 min readRead entry

The SAX day

21 Jul 2025

A streaming parser rolled back and re-landed in one day.

Five iterations of a streaming parser before eleven in the morning, the parser removed, a rollback to the stable build, then a fresh series of fixes until late evening. The eighth one held.

2025-07-21·#the-sax-day

June 2025

5 entries
JUN 25
30
Work#aws#serverless#python 1 min readRead entry

A notebook becomes a Lambda

30 Jun 2025

What has to change for an analyst's notebook to run as a production handler.

The analysts had the algorithm and they lived in notebooks. Instead of translating each change into application code, a converter turns the notebook itself into the Lambda handler: first a local script, then a deployed stage, then a rewrite of the converter once the pattern proved itself.

2025-06-30·#notebook-becomes-a-lambda
JUN 25
24
Personal#supabase#web#embedded 1 min readRead entry

Appwrite in the morning, Supabase by the afternoon

24 Jun 2025

The e-ink frame renderer, built twice in one day on two backends.

The frame renderer was built twice in one day. In the morning it was an Appwrite function in Node: fill an SVG template with the word and picture, rasterise it with resvg, store the PNG. By the afternoon it was a Supabase Edge Function in Deno that also picks a random word from the database and returns the frame with the device's sleep time in a header.

2025-06-24·#appwrite-morning-supabase-afternoon
JUN 25
23
Personal#adr#embedded#iot 1 min readRead entry

Render on the server, draw on the device

23 Jun 2025

The e-ink device fetches a finished picture; the server does the layout.

In the design, the device lays nothing out. It wakes, asks the server for its next frame, and gets back a finished PNG in the panel's six colours, a pronunciation clip and how long to sleep. Themes, fonts and the word itself are the server's problem, so a new layout never needs a firmware update.

2025-06-23·#render-on-server-draw-on-device
JUN 25
18
Personal#embedded#hw#adr 1 min readRead entry

Colour e-ink decides the chip

18 Jun 2025

A six-colour panel needs PSRAM, a dual-core ESP32-S3 and patience on every refresh.

The desk device shows a word, a phrase and a picture on e-ink. A comparison of two 800 by 480 panels, 2-bit grayscale and six-colour Spectra 6, decided most of the hardware before any board was drawn.

2025-06-18·#colour-eink-decides-the-chip
JUN 25
18
Personal#ops#sec#net 1 min readRead entry

First backend on a rented VPS

18 Jun 2025

Two Hetzner servers, keys-only SSH and an admin console behind Zero Trust, ten months before the cluster.

The backend for the e-ink desk device started on two Hetzner Cloud servers: one ran Appwrite in Docker, the other served the Flutter web app from nginx, with Cloudflare in front for DNS.

2025-06-18·#first-backend-on-a-rented-vps

January 2025

1 entry
JAN 25
24
Personal#aws#amplify#ops 1 min readRead entry

Dev and prod on Amplify branches

24 Jan 2025

Environment promotion for a serverless app done by branch merges.

Two Amplify environments mapped to two branches: main deploys dev, prod deploys prod, and promotion is a merge. A short helper document lists the order of operations and the one command that must not run against the wrong environment.

2025-01-24·#dev-and-prod-on-amplify-branches

October 2024

1 entry
OCT 24
04
Work#web#flutter#java#aws#serverless 1 min readRead entry

Two languages, one JSON contract

4 Oct 2024

A drag-and-drop layout editor in Flutter web, with Java Lambdas doing the heavy work.

A Flutter web app for building report pages from blocks and widgets. The user drags blocks into a tree and sets margins, borders and colors in side panels; the app saves the layout as JSON.

2024-10-04·#two-languages-one-json-contract

September 2024

1 entry
SEP 24
07
Personal#aws#amplify#web#flutter#python#serverless 1 min readRead entry

MySpool web: Amplify, Python 3.12 Lambdas

7 Sep 2024

A second-generation rewrite with a collaborator on the backend.

The sensor app came back as a Flutter web application on Amplify, with a collaborator owning the backend. The Lambdas moved to Python 3.12 and were split by responsibility, one function per device-management concern instead of one handler for everything.

2024-09-07·#myspool-web-amplify

August 2024

1 entry
AUG 24
13
Work#java#etl 1 min readRead entry

Reverting the revert

13 Aug 2024

A new union approach, reverted, then re-applied the same evening.

A new approach to the union action went out in the afternoon, was reverted in the evening, and was re-applied ninety minutes later. The first version was right about the algorithm and wrong about one edge: columns present in one input and not the other.

2024-08-13·#reverting-the-revert

May 2024

1 entry
MAY 24
24
Work#java#jvm#perf#ops 1 min readRead entry

Climbing the heap

24 May 2024

How a workbook engine eats memory, and where the real limit was.

The memory ladder over fifteen months: more heap in March and a switch to ZGC, an out-of-memory handler in May, the heap raised again, and a year later the cap on byte arrays raised again.

2024-05-24·#climbing-the-heap

February 2024

1 entry
FEB 24
26
Work#qa#aws#ops#serverless#web#node 1 min readRead entry

Visual regression before every release

26 Feb 2024

Screenshot diffs before every release, and why partial results mattered more than a perfect diff.

Four days from an empty repository to a screenshot-based QA service: a Lambda driving a headless browser, state in DynamoDB, a two-stage error handler.

2024-02-26·#visual-regression-before-release

January 2024

1 entry
JAN 24
09
Personal#embedded#hw 1 min readRead entry

First colours on a Spectra 6 panel

9 Jan 2024

A four-inch six-colour e-paper panel on the bench, eighteen months before the device had a name.

The desk device started with a panel, not a plan: a Good Display GDEP040E01, a four-inch six-colour Spectra 6 e-paper module, driven from an Arduino board by Good Display's sample code. The first frame was a hard-coded image, 400 by 600 pixels at 4 bits each, 240,000 bytes compiled into the firmware.

2024-01-09·#first-colours-on-a-spectra-6-panel

November 2023

1 entry
NOV 23
20
Work#java#aws#fargate#etl#adr 1 min readRead entry

The Excel engine moves to Java

20 Nov 2023

From Node Lambdas to a Spring Boot container on Fargate, and why.

Two weeks after the first commit of the Java rewrite I joined it and shipped the substitute, dedup, join and graph actions in the first week. The platform decision behind it: the workbook engine had hit [[lambda-outgrows-itself|the Lambda time limit and the memory ceiling]], and there was nothing left to tune.

2023-11-20·#excel-engine-moves-to-java

June 2023

2 entries
JUN 23
13
Work#aws#serverless#etl#node 1 min readRead entry

When a Lambda outgrows itself

13 Jun 2023

How a function grows to thousands of lines, and what that costs.

A streaming variant of the ETL landed as one commit with a processor Lambda of several thousand lines. It worked, and it was the moment [[excel-engine-moves-to-java|the Java rewrite five months later]] became inevitable.

2023-06-13·#lambda-outgrows-itself
JUN 23
07
Work#aws#serverless#ops 1 min readRead entry

The DLQ handler's first real failure

7 Jun 2023

A dead-letter handler is only tested when things fail.

The dead-letter-queue handler had been in place for six months and had never seen a real failure. The first poison message needed a "major fix" twice in one day.

2023-06-07·#dlq-first-real-failure

July 2022

1 entry
JUL 22
29
Work#aws#serverless#etl#node 1 min readRead entry

The intake pipeline gets its shape

29 Jul 2022

Validate, clean, process: the pipeline shape that every later version kept.

The intake pipeline got its own repository and its own shape: a preprocessor, an input handler, a validator and a cleaner as separate Lambdas, with the processor split out six weeks later. That shape survived three rewrites and a change of language, and the first version that used it is still in production.

2022-07-29·#intake-pipeline-shape

March 2022

1 entry
MAR 22
23
Personal#iot#hw#flutter#firebase#python#mobile 1 min readRead entry

Field check: two phones, two trackers

23 Mar 2022

The check a bench cannot do: a real GPS fix on a city street, seen on Android and iOS at once.

The third generation went into a field check with two trackers and two phones, Android and iOS. Each tracker owns one Firestore document, where the first generation searched a shared Realtime Database list for its own entry. It listens there for commands (light, voice, reboot) and writes battery, temperature and a timestamp back every five seconds; position comes from the modem's GPS.

2022-03-23·#pettrack-field-check

February 2022

1 entry
FEB 22
18
Learning#mobile#flutter#learn 1 min readRead entry

Reading price tags with on-device OCR

18 Feb 2022

A one-week, two-person Flutter experiment with on-device text recognition.

A travel helper meant to read a price tag through the camera and show it in another currency. In one week with a collaborator, one branch each, merged through pull requests, it reached the first half: a live camera view with on-device text recognition drawing what it read over the preview.

2022-02-18·#price-tags-on-device-ocr

December 2021

2 entries
DEC 21
13
Personal#ops#mobile#flutter 1 min readRead entry

MySpool mobile reaches 1.0

13 Dec 2021

Thirteen months and ninety versioned builds for a solo mobile release.

Version 1.0.0 of the sensor companion app, thirteen months after the prototype and ninety versioned builds later, with an iOS archive for testers roughly every fourth build.

2021-12-13·#myspool-mobile-1-0
DEC 21
09
Work#etl#data#node 1 min readRead entry

Leading zeros and exponents in identifiers

9 Dec 2021

Numbers that are really strings, met in the first month and never forgotten.

Three fixes in one day, all the same bug wearing different clothes. An identifier came out in exponent form. Another lost precision past fifteen digits. A third lost its leading zero. The spreadsheet had decided these were numbers; they were codes.

2021-12-09·#leading-zeros-and-exponents

November 2021

2 entries
NOV 21
24
Work#etl#data#node 1 min readRead entry

Stop hand-parsing spreadsheet formulas

24 Nov 2021

The week a home-grown formula evaluator stopped paying for itself.

The first serverless Excel ETL evaluated formulas with code I had written myself. In three days it needed six fixes: references that shifted when rows moved, division by zero, a lookup that missed. Each fix was a wrong assumption about how references behave when cells move.

2021-11-24·#stop-hand-parsing-formulas
NOV 21
03
Learning#embedded#iot#firebase#learn 1 min readRead entry

BLE-provisioned Wi-Fi on an ESP32

3 Nov 2021

The first microcontroller project, and the device side of a problem the sensor app solved another way.

A temperature and humidity sensor on an ESP32 that takes Wi-Fi credentials over Bluetooth LE, stores them in flash and posts readings to a cloud function. One commit, one weekend.

2021-11-03·#ble-provisioned-wifi-esp32

July 2021

2 entries
JUL 21
31
Personal#iot#embedded#hw#python#firebase 1 min readRead entry

Live video from a Raspberry Pi over LTE

31 Jul 2021

Let the camera's encoder do the video; the stream that stayed ran at a sixth of the first bitrate.

A pet tracker on a Raspberry Pi 4 with an LTE modem, a camera, an audio board and a UPS board. The app asks for video by setting a flag in Firebase; the Pi starts a stream and pushes it over RTMP to a cloud transcoder that serves HLS back to the phone.

2021-07-31·#live-video-pi-over-lte
JUL 21
08
Personal#iot#mobile#flutter 1 min readRead entry

MQTT spike to robot dashboard in a week

8 Jul 2021

Prove the transport in a throw-away app, then wire the UI.

A control app for a fleet of cleaning robots: sign-in, live robot state, commands, plans and reports. Before any of the screens, a throw-away demo app proved the MQTT path to the broker in isolation. Two days later the credentials and the robot-state card landed in the real app. The demo video went out a week after that.

2021-07-08·#mqtt-spike-to-robot-dashboard

May 2021

1 entry
MAY 21
10
Personal#aws#firebase#adr#flutter#mobile 1 min readRead entry

Push on Firebase, everything else on AWS

10 May 2021

A two-cloud setup chosen for one feature, and what it costs in configuration.

Auth and the API lived on AWS. Push notifications went to Firebase Cloud Messaging, because that was the path with a working client library on both mobile platforms the week it was needed.

2021-05-10·#push-on-firebase

January 2021

1 entry
JAN 21
21
Personal#web#adr#php 1 min readRead entry

Clone the catalogue or parametrise it

21 Jan 2021

Copy-per-model was the fast choice for a four-month job; here is what it cost.

The second and third vehicle models were delivered as full copies of the first catalogue, plus a landing page to choose between them, instead of one application with a model parameter.

2021-01-21·#clone-or-parametrise

November 2020

1 entry
NOV 20
07
Personal#aws#amplify#adr#flutter#mobile 1 min readRead entry

Cognito via Amplify from day one

7 Nov 2020

The first managed-auth decision, and the CloudFormation that came with it.

The mobile app's first commit already had AWS Amplify with Cognito for sign-in, with the CloudFormation generated by the Amplify CLI committed next to the Dart code.

2020-11-07·#cognito-via-amplify

October 2020

1 entry
OCT 20
19
Personal#mobile#flutter#adr 1 min readRead entry

Switching to Flutter

19 Oct 2020

An eight-day onboarding prototype that decided the next five years of mobile work.

After a Java app, a Kotlin app and a React Native app in one year, the next project started in Flutter: a sensor-onboarding prototype, connect, instructions, credentials, control page, in eight days.

2020-10-19·#switching-to-flutter

September 2020

1 entry
SEP 20
17
Personal#web#php 1 min readRead entry

Exploded-view drawings as image maps

17 Sep 2020

How a catalogue with hundreds of drawings was made navigable without a CMS.

A spare-parts catalogue for an all-terrain vehicle: hundreds of exploded-view drawings, each part clickable, each click landing in a revision-aware parts table with a basket that mails the order.

2020-09-17·#exploded-view-drawings-as-image-maps

July 2020

2 entries
JUL 20
28
Personal#embedded#hw#ops#android#mobile 1 min readRead entry

Six days of camfix builds

28 Jul 2020

Eight builds to stabilise a USB camera on a tablet; the final fix was one line.

Adding a USB camera feed inside a lesson slide took a JNI camera library and one afternoon. Making it survive on the actual tablet took eight builds in six days, each one a different combination of manifest permissions and foreground-service flags.

2020-07-28·#six-days-of-camfix-builds
JUL 20
14
Personal#mobile#learn#android 1 min readRead entry

React Native rhyme finder on Google Play

14 Jul 2020

Second store app, two months after the first, and the only React Native project.

A rhyme finder that shows a definition for any word you tap, built in React Native and listed on Google Play two months after the first Kotlin app. One external rhyme API, one dictionary lookup, a navigation stack and a handful of empty states.

2020-07-14·#react-native-rhyme-finder

May 2020

1 entry
MAY 20
11
Personal#mobile#android#learn 1 min readRead entry

First Google Play release in a week

11 May 2020

A Kotlin age calculator: five days of code, a public listing eight days after the first commit.

The first Kotlin app took five days to write and was on Google Play four days later: an age calculator with a custom numeric keyboard and a shake on invalid input. Small on purpose, so that the store process itself was the thing being learned.

2020-05-11·#first-google-play-release

February 2020

1 entry
FEB 20
11
Personal#embedded#adr#mobile#android 1 min readRead entry

One branch per tablet generation

11 Feb 2020

Branching by hardware target: what it cost in merges and why alpha suffixes replaced versions.

When a second tablet model arrived, the app got a build line per hardware generation instead of one binary with runtime detection. Each line carried its own version string with an alpha suffix, because the two lines never released in step.

2020-02-11·#one-branch-per-tablet-generation

November 2019

1 entry
NOV 19
27
Personal#mobile#android#ideas 1 min readRead entry

Three lessons, three languages

27 Nov 2019

Localising a content-heavy app early is cheap; retrofitting is not.

The first three interactive lessons shipped complete in all three languages on the same day, with strings split into per-language resource files from the first commit.

2019-11-27·#three-lessons-three-languages

October 2019

1 entry
OCT 19
12
Personal#iot#embedded#mobile#android 1 min readRead entry

First lab sensor talks to Android

12 Oct 2019

The first hardware-in-the-loop code I shipped, nine days after the first commit, on a science tablet with its own sensor port.

Nine days after the first commit of the Sensorium tablet app, a lab sensor on the tablet's own sensor port delivered its first reading into an Android activity. The vendor SDK arrived as a binary, so the integration was a foreground service that owned the connection and a callback that every lesson screen subscribed to.

2019-10-12·#first-ble-lab-sensor

Subscribe

Pulls new entries via RSS, no email, no tracking. Items carry the kind as a category and the event date in the description.

/rss.xml