Tag: Ai
プログラマ道 (puroguramā-dō)
プログラマ道1.
Companion piece to the 100x post. That one was the framing argument: AI tooling is +0.99 with a wielding bonus, not a multiplier. This is the ground-level version. How I actually drive the thing day to day, what I will not delegate, and why none of this is new.
I drive Claude every day. Across Woosmap services in Python, a music app in Go and SwiftUI on the weekends (Tunes), a SNES toolchain in Python (a816, xdds), and a fair amount of 65c816 ROM hacking. The tool is real. It does not flip the table on the discipline an engineer needs. It raises the floor a notch and rewards the wielder. What the wielder is actually doing, when it works, is the part nobody writes down.
The 100x engineer, and the unit nobody defines
The 100x engineer is back in the discourse. This time the pitch is that an engineer with the right AI tooling can do the work of a hundred. Sometimes the number is 10x, sometimes it is 100x, occasionally someone goes wild and writes 1000x. The number is always round and the unit is always missing.
What are we counting?
If a 100x engineer using Claude can do 365 days of work in 3.65 days, whose 365 days? A staff engineer shipping platform infrastructure? A junior wiring up CRUD endpoints? My grandmother on a good day? The denominator is doing all the work in that sentence and nobody ever writes it down.
The complete machine, fading
My iPod 5.5g is old enough to drink alcohol in any country that has a drinking age. I bought it used, swapped the battery and the disk for an SD card mod, and it still does the one thing it was sold to do: play music. No firmware update has ever taken a feature away. No company has decided the device is too old to deserve syncing. Apple, to their credit, still ships iTunes and Music with iPod sync on macOS. But even the day they stop, the iPod will keep playing whatever is already on the disk. The original purpose has not eroded.
Tag: Claude
プログラマ道 (puroguramā-dō)
プログラマ道1.
Companion piece to the 100x post. That one was the framing argument: AI tooling is +0.99 with a wielding bonus, not a multiplier. This is the ground-level version. How I actually drive the thing day to day, what I will not delegate, and why none of this is new.
I drive Claude every day. Across Woosmap services in Python, a music app in Go and SwiftUI on the weekends (Tunes), a SNES toolchain in Python (a816, xdds), and a fair amount of 65c816 ROM hacking. The tool is real. It does not flip the table on the discipline an engineer needs. It raises the floor a notch and rewards the wielder. What the wielder is actually doing, when it works, is the part nobody writes down.
Tag: Discipline
プログラマ道 (puroguramā-dō)
プログラマ道1.
Companion piece to the 100x post. That one was the framing argument: AI tooling is +0.99 with a wielding bonus, not a multiplier. This is the ground-level version. How I actually drive the thing day to day, what I will not delegate, and why none of this is new.
I drive Claude every day. Across Woosmap services in Python, a music app in Go and SwiftUI on the weekends (Tunes), a SNES toolchain in Python (a816, xdds), and a fair amount of 65c816 ROM hacking. The tool is real. It does not flip the table on the discipline an engineer needs. It raises the floor a notch and rewards the wielder. What the wielder is actually doing, when it works, is the part nobody writes down.
Tag: Productivity
The 100x engineer, and the unit nobody defines
The 100x engineer is back in the discourse. This time the pitch is that an engineer with the right AI tooling can do the work of a hundred. Sometimes the number is 10x, sometimes it is 100x, occasionally someone goes wild and writes 1000x. The number is always round and the unit is always missing.
What are we counting?
If a 100x engineer using Claude can do 365 days of work in 3.65 days, whose 365 days? A staff engineer shipping platform infrastructure? A junior wiring up CRUD endpoints? My grandmother on a good day? The denominator is doing all the work in that sentence and nobody ever writes it down.
Tag: A816
25 years to a complete machine
Since around the year 2000 I have been piling features into a French Final Fantasy IV translation patch. Variable-width fonts in dialog, in battle messages, in menu descriptions. Redrawn battle, load/save, main, and two-column magic menus. An expanded inventory of 0x48 slots rendered through a paged window instead of the static grid the original engine assumed. Most of these were features the SNES ROM hacking scene of the era said could not be done on real hardware. Other hackers eventually pulled off equivalents on other titles, fair enough; modifying a compiled ROM is more convoluted than editing source code, but not impossible. Twenty-five years on, “impossible” reads as a fair approximation of how hard, not of whether.
Tag: Ff4
25 years to a complete machine
Since around the year 2000 I have been piling features into a French Final Fantasy IV translation patch. Variable-width fonts in dialog, in battle messages, in menu descriptions. Redrawn battle, load/save, main, and two-column magic menus. An expanded inventory of 0x48 slots rendered through a paged window instead of the static grid the original engine assumed. Most of these were features the SNES ROM hacking scene of the era said could not be done on real hardware. Other hackers eventually pulled off equivalents on other titles, fair enough; modifying a compiled ROM is more convoluted than editing source code, but not impossible. Twenty-five years on, “impossible” reads as a fair approximation of how hard, not of whether.
Tag: Romhacking
25 years to a complete machine
Since around the year 2000 I have been piling features into a French Final Fantasy IV translation patch. Variable-width fonts in dialog, in battle messages, in menu descriptions. Redrawn battle, load/save, main, and two-column magic menus. An expanded inventory of 0x48 slots rendered through a paged window instead of the static grid the original engine assumed. Most of these were features the SNES ROM hacking scene of the era said could not be done on real hardware. Other hackers eventually pulled off equivalents on other titles, fair enough; modifying a compiled ROM is more convoluted than editing source code, but not impossible. Twenty-five years on, “impossible” reads as a fair approximation of how hard, not of whether.
Tag: Snes
25 years to a complete machine
Since around the year 2000 I have been piling features into a French Final Fantasy IV translation patch. Variable-width fonts in dialog, in battle messages, in menu descriptions. Redrawn battle, load/save, main, and two-column magic menus. An expanded inventory of 0x48 slots rendered through a paged window instead of the static grid the original engine assumed. Most of these were features the SNES ROM hacking scene of the era said could not be done on real hardware. Other hackers eventually pulled off equivalents on other titles, fair enough; modifying a compiled ROM is more convoluted than editing source code, but not impossible. Twenty-five years on, “impossible” reads as a fair approximation of how hard, not of whether.
Tag: Cloud
The complete machine, fading
My iPod 5.5g is old enough to drink alcohol in any country that has a drinking age. I bought it used, swapped the battery and the disk for an SD card mod, and it still does the one thing it was sold to do: play music. No firmware update has ever taken a feature away. No company has decided the device is too old to deserve syncing. Apple, to their credit, still ships iTunes and Music with iPod sync on macOS. But even the day they stop, the iPod will keep playing whatever is already on the disk. The original purpose has not eroded.
Tag: Hardware
The complete machine, fading
My iPod 5.5g is old enough to drink alcohol in any country that has a drinking age. I bought it used, swapped the battery and the disk for an SD card mod, and it still does the one thing it was sold to do: play music. No firmware update has ever taken a feature away. No company has decided the device is too old to deserve syncing. Apple, to their credit, still ships iTunes and Music with iPod sync on macOS. But even the day they stop, the iPod will keep playing whatever is already on the disk. The original purpose has not eroded.
Tag: Macos
Higgins: a neurosymbolic menubar buddy
Higgins is a local assistant that lives in the menubar. A Qwen 2.5 7B model runs on-device via MLX, a SQLite triple store remembers things across sessions, and a small collection of tools — AppleScript runners, EventKit bridges, gh CLI wrappers — lets him actually do work on the Mac. No cloud, no subscription, no data leaving the machine.
The name is a nod to Magnum P.I.’s reserved English major-domo. The vibe was aspirational; most of the engineering effort went into making sure he doesn’t cheerfully hallucinate your dentist appointment into 2023.
Tag: Mlx
Higgins: a neurosymbolic menubar buddy
Higgins is a local assistant that lives in the menubar. A Qwen 2.5 7B model runs on-device via MLX, a SQLite triple store remembers things across sessions, and a small collection of tools — AppleScript runners, EventKit bridges, gh CLI wrappers — lets him actually do work on the Mac. No cloud, no subscription, no data leaving the machine.
The name is a nod to Magnum P.I.’s reserved English major-domo. The vibe was aspirational; most of the engineering effort went into making sure he doesn’t cheerfully hallucinate your dentist appointment into 2023.
Tag: Swift
Higgins: a neurosymbolic menubar buddy
Higgins is a local assistant that lives in the menubar. A Qwen 2.5 7B model runs on-device via MLX, a SQLite triple store remembers things across sessions, and a small collection of tools — AppleScript runners, EventKit bridges, gh CLI wrappers — lets him actually do work on the Mac. No cloud, no subscription, no data leaving the machine.
The name is a nod to Magnum P.I.’s reserved English major-domo. The vibe was aspirational; most of the engineering effort went into making sure he doesn’t cheerfully hallucinate your dentist appointment into 2023.
The most useless way to port a macOS app
I grew up fascinated by projects like GNUStep, Haiku, Etoile, Wine, and ReactOS. Engineering feats, all of them. They reverse-engineer or reimplement entire operating system APIs so that software written for one platform can run on another. And they almost always end up in the same place: impressive technically, starved for contributors, forever chasing a moving target they can never quite catch.
I never liked the state of the Linux desktop either. Not because it’s bad per se, but because it’s fragmented. A KDE app on GNOME looks alien. Firefox rolls its own everything. GTK and Qt will never agree on anything. Every toolkit draws its own widgets, manages its own text rendering, handles its own accessibility story. The result is a desktop that feels like a coalition of independent projects rather than a coherent system.
Tunes
iTunes Match worked great for years: you uploaded your library, Apple matched what it could, and you had access to everything from any device. But Apple is clearly moving everyone toward Apple Music, and iTunes Match has been slowly rotting. The writing was on the wall.
I didn’t want to subscribe to Apple Music. I wanted to keep my library, my ratings, my play counts, my playlists, all the metadata accumulated over 20 years. So I built my own streaming setup: a Go backend that serves the library over HTTP, and SwiftUI apps that play it on iOS, macOS, and tvOS.
Tag: Vector-Tiles
Freddie part 2: from tiles to map
In part 1 I covered the hard problems: COG multi-resolution, SDF raster smoothing, and Barbapapa topology-preserving Laplacian smoothing. Freddie could generate good-looking 10m land cover vector tiles. This post is about what happened next: killing the algorithm I spent a week perfecting, solving a surprisingly dumb storage problem, and wiring the tiles all the way through to the map SDK.
Barbapapa is dead
I killed Barbapapa a few weeks after shipping it.
When "mostly aligned" stops being enough
Every product starts by depending on open source it doesn’t fully understand. That’s fine. It’s the whole point. You stand on the shoulders of people who solved problems you haven’t even encountered yet, and you ship. The dependency is invisible because it works.
Then the product grows, and you start noticing the edges. The upstream project makes choices you wouldn’t have made. The schema carries fields you don’t use. The routing engine takes a turn you can’t explain. The update process becomes a gamble: bump to the latest version and hope nothing changed in a way that breaks your use case. You’re accountable to your customers for software you treat as a black box.
Scaling Freddie to 10m landcover
Serving vector tiles from 10-meter resolution land cover data. What breaks when you 100x the input resolution, and how topology-preserving smoothing fixes the ugly parts.
Background
Freddie is our tile server for land cover data. It reads GeoTIFF raster files (pixels classified as wood, grass, built-up, water, etc.) and serves them as vector tiles or raster tiles over HTTP. Nothing fancy. Read pixels, make shapes, encode, serve.
The original setup used ESA WorldCover at ~100m resolution. Native resolution sits around z8 in Web Mercator, so serving z0–z8 tiles was straightforward. Sample pixels, run-length encode, merge adjacent faces of the same class, encode to MVT. Done. It just worked, and I didn’t think much about it.
Tag: Linux
The most useless way to port a macOS app
I grew up fascinated by projects like GNUStep, Haiku, Etoile, Wine, and ReactOS. Engineering feats, all of them. They reverse-engineer or reimplement entire operating system APIs so that software written for one platform can run on another. And they almost always end up in the same place: impressive technically, starved for contributors, forever chasing a moving target they can never quite catch.
I never liked the state of the Linux desktop either. Not because it’s bad per se, but because it’s fragmented. A KDE app on GNOME looks alien. Firefox rolls its own everything. GTK and Qt will never agree on anything. Every toolkit draws its own widgets, manages its own text rendering, handles its own accessibility story. The result is a desktop that feels like a coalition of independent projects rather than a coherent system.
Tag: Rust
The most useless way to port a macOS app
I grew up fascinated by projects like GNUStep, Haiku, Etoile, Wine, and ReactOS. Engineering feats, all of them. They reverse-engineer or reimplement entire operating system APIs so that software written for one platform can run on another. And they almost always end up in the same place: impressive technically, starved for contributors, forever chasing a moving target they can never quite catch.
I never liked the state of the Linux desktop either. Not because it’s bad per se, but because it’s fragmented. A KDE app on GNOME looks alien. Firefox rolls its own everything. GTK and Qt will never agree on anything. Every toolkit draws its own widgets, manages its own text rendering, handles its own accessibility story. The result is a desktop that feels like a coalition of independent projects rather than a coherent system.
Calculon part 2: costing profiles, performance, and maneuvers
Part 1 covered the basics: six crates, Valhalla’s tile format, a driving cost model, bidirectional A*, many-to-many matrix, and an Axum HTTP server. Everything worked for the driving case on the Monaco dataset.
Since then, the engine gained bicycle and pedestrian costing, memory-mapped tiles, a comparison webapp, turn-by-turn directions in 34 languages, and enough performance work to make it usable on France-scale data. This is what changed.
Building a routing engine in Rust
A routing engine in Rust. Bidirectional A*, many-to-many distance matrices, Valhalla-compatible API. Six crates, about 8,000 lines.
Why
We use Valhalla for routing and distance matrices at Woosmap. It works most of the time. When it doesn’t, we’re stuck. We treat it as a black box: file an issue, upgrade, hope the next release fixes things. When the matrix returns wrong costs for certain edge cases or the engine routes you through someone’s driveway, there’s no realistic way for us to dig in. It’s a massive C++ codebase that assumes you’ve been living in it for years.
Building a GPU map renderer from scratch
Building a vector map renderer in Rust with wgpu. From “everything renders as points” to a full style-driven renderer running on desktop, iOS, and the browser.
Why
We built our maps stack on top of Mapbox GL: the JS SDK, then native SDKs on the C++ core.
For static maps we wrapped the C++ SDK in a Python library (maparazzo). It worked but the C++ renderer leaked memory. OpenGL contexts kept state around after objects were destroyed. We tried pooling Map instances, reusing GL contexts. It helped but never fully solved it.
Tag: Swiftui
The most useless way to port a macOS app
I grew up fascinated by projects like GNUStep, Haiku, Etoile, Wine, and ReactOS. Engineering feats, all of them. They reverse-engineer or reimplement entire operating system APIs so that software written for one platform can run on another. And they almost always end up in the same place: impressive technically, starved for contributors, forever chasing a moving target they can never quite catch.
I never liked the state of the Linux desktop either. Not because it’s bad per se, but because it’s fragmented. A KDE app on GNOME looks alien. Firefox rolls its own everything. GTK and Qt will never agree on anything. Every toolkit draws its own widgets, manages its own text rendering, handles its own accessibility story. The result is a desktop that feels like a coalition of independent projects rather than a coherent system.
Tree: a browser that remembers where you came from
Every browser has tabs. Linear, flat, disposable. Open thirty of them researching one topic and they sit in a row with no indication that twelve of them came from the same Wikipedia article. Close the wrong one and the context is gone.
Tree is a macOS browser that replaces tabs with a tree. Every page knows its parent. Cmd+Click a link and it becomes a child node of the current page. The sidebar shows the full hierarchy: research paths branch naturally, and you can always trace back to where you started.
Tag: Wgpu
The most useless way to port a macOS app
I grew up fascinated by projects like GNUStep, Haiku, Etoile, Wine, and ReactOS. Engineering feats, all of them. They reverse-engineer or reimplement entire operating system APIs so that software written for one platform can run on another. And they almost always end up in the same place: impressive technically, starved for contributors, forever chasing a moving target they can never quite catch.
I never liked the state of the Linux desktop either. Not because it’s bad per se, but because it’s fragmented. A KDE app on GNOME looks alien. Firefox rolls its own everything. GTK and Qt will never agree on anything. Every toolkit draws its own widgets, manages its own text rendering, handles its own accessibility story. The result is a desktop that feels like a coalition of independent projects rather than a coherent system.
Building a GPU map renderer from scratch
Building a vector map renderer in Rust with wgpu. From “everything renders as points” to a full style-driven renderer running on desktop, iOS, and the browser.
Why
We built our maps stack on top of Mapbox GL: the JS SDK, then native SDKs on the C++ core.
For static maps we wrapped the C++ SDK in a Python library (maparazzo). It worked but the C++ renderer leaked memory. OpenGL contexts kept state around after objects were destroyed. We tried pooling Map instances, reusing GL contexts. It helped but never fully solved it.
Tag: Planetiler
When "mostly aligned" stops being enough
Every product starts by depending on open source it doesn’t fully understand. That’s fine. It’s the whole point. You stand on the shoulders of people who solved problems you haven’t even encountered yet, and you ship. The dependency is invisible because it works.
Then the product grows, and you start noticing the edges. The upstream project makes choices you wouldn’t have made. The schema carries fields you don’t use. The routing engine takes a turn you can’t explain. The update process becomes a gamble: bump to the latest version and hope nothing changed in a way that breaks your use case. You’re accountable to your customers for software you treat as a black box.
Tag: Routing
When "mostly aligned" stops being enough
Every product starts by depending on open source it doesn’t fully understand. That’s fine. It’s the whole point. You stand on the shoulders of people who solved problems you haven’t even encountered yet, and you ship. The dependency is invisible because it works.
Then the product grows, and you start noticing the edges. The upstream project makes choices you wouldn’t have made. The schema carries fields you don’t use. The routing engine takes a turn you can’t explain. The update process becomes a gamble: bump to the latest version and hope nothing changed in a way that breaks your use case. You’re accountable to your customers for software you treat as a black box.
Tag: Docker
Multi-arch Docker builds in GitHub Actions
We needed ARM64 containers. Our Python services run on mixed infrastructure: amd64 in CI and some production clusters, arm64 on newer nodes. Building on one arch and emulating the other with QEMU was painfully slow and broke native extensions. So we added proper multi-arch builds to our GitHub Actions CI.
It took a week to get right. Then five more fixes over two weeks as each failure mode revealed itself in production. This is what went wrong and how we fixed it.
Migrating Python containers to Wolfi and uv
Our Python services ran on ubuntu:24.04 with pip-installed dependencies. It worked, but the images carried hundreds of packages we never used, Trivy scans were noisy with OS-level CVEs, and builds were slower than they needed to be. Over a couple of months we migrated to Chainguard’s Wolfi base image and uv for dependency management. This is how it went for the maps service, the one that renders static map tiles with a C++/Python hybrid stack.
Tag: Github-Actions
Multi-arch Docker builds in GitHub Actions
We needed ARM64 containers. Our Python services run on mixed infrastructure: amd64 in CI and some production clusters, arm64 on newer nodes. Building on one arch and emulating the other with QEMU was painfully slow and broke native extensions. So we added proper multi-arch builds to our GitHub Actions CI.
It took a week to get right. Then five more fixes over two weeks as each failure mode revealed itself in production. This is what went wrong and how we fixed it.
Tag: Go
Full-text search and lyrics in a Go music server
The Tunes server started as a way to stream my music library from a Raspberry Pi. It’s accumulated features since then. Two recent additions changed how I interact with it day-to-day: accent-insensitive full-text search across the entire library, and lyrics, both embedded from audio files and fetched from LRClib.
The search problem
Searching a music library is harder than it sounds. You want “bjork” to match “Björk”. You want “cafe” to match “Café”. You want “creme” to match “Crème Brûlée” in an album title. And you want it fast enough that results appear as you type.
Tunes
iTunes Match worked great for years: you uploaded your library, Apple matched what it could, and you had access to everything from any device. But Apple is clearly moving everyone toward Apple Music, and iTunes Match has been slowly rotting. The writing was on the wall.
I didn’t want to subscribe to Apple Music. I wanted to keep my library, my ratings, my play counts, my playlists, all the metadata accumulated over 20 years. So I built my own streaming setup: a Go backend that serves the library over HTTP, and SwiftUI apps that play it on iOS, macOS, and tvOS.
Tag: Python
Rate limiting with a single Lua script
We needed rate limiting across all our services: FastAPI, Django, you name it. The usual approach is to grab a library, wire it up per-framework, and accept the slight differences in behavior between them. We went a different way: one Lua script that runs atomically in Redis, with everything else being thin wrappers around it.
This is how the rate limiting system in application-kit works, including per-project overrides, element-based counting, and a monitor mode for gradual rollouts.
Migrating Python containers to Wolfi and uv
Our Python services ran on ubuntu:24.04 with pip-installed dependencies. It worked, but the images carried hundreds of packages we never used, Trivy scans were noisy with OS-level CVEs, and builds were slower than they needed to be. Over a couple of months we migrated to Chainguard’s Wolfi base image and uv for dependency management. This is how it went for the maps service, the one that renders static map tiles with a C++/Python hybrid stack.
A map style compiler in Python
Generating Mapbox GL GL style JSON from Python code instead of editing 3,000-line JSON files by hand. Hierarchical style resolution, a Django-style filter DSL, injection-based layer ordering, and a Go sprite generator called from Python via ctypes.
The problem with style JSON
A Mapbox GL GL style is a single JSON file that describes everything about how a map looks. Background color, road widths at every zoom level, label fonts, icon placement, landcover tints, building extrusion heights. All of it.
Tag: Redis
Rate limiting with a single Lua script
We needed rate limiting across all our services: FastAPI, Django, you name it. The usual approach is to grab a library, wire it up per-framework, and accept the slight differences in behavior between them. We went a different way: one Lua script that runs atomically in Redis, with everything else being thin wrappers around it.
This is how the rate limiting system in application-kit works, including per-project overrides, element-based counting, and a monitor mode for gradual rollouts.
Tag: Mapbox
Building a maps SDK from a Mapbox GL fork
Rewriting the Woosmap Maps SDK on top of a Mapbox GL JS fork, with a Google Maps-compatible API surface, built with Bun, and an experiment in offscreen rendering.
Why
The Woosmap Maps SDK depended on Mapbox GL JS. Then Mapbox changed the license. Version 2.0 moved to the Business Source License, not open source anymore. The last truly open version was 1.13.1 (December 2020). No more upstream bug fixes, no WebGL2 improvements, no new features. We were stuck on a frozen codebase.