Writing

Notes from two decades of platforms and teams.

Engineering leadership, integrations at scale, and what a fractional CTO actually does. New posts roughly twice a month — also in RSS.

October 8, 2026

Three production bugs Sentry found in its first eight hours

An unset environment variable silently sending outbound email nowhere, a recurring background job with a misnamed method that had failed on every run since the demo went live, and a framework library that deliberately swallows its own errors instead of raising them. None of them was going to be caught by the tests a team normally writes.

September 30, 2026

The Turbo Frame that 403'd on every page load, forever

It looked exactly like a caching bug - the same red 'Authorization Failure' flash on every load, surviving refreshes and re-logins. Nothing was cached. A hidden dialog's Turbo Frame was loading eagerly on every render, 403ing, and an error handler's redirect_back was sending the user back to the one page guaranteed to trigger it again.

September 23, 2026

I wrote the integration checklist. Then I ignored it.

Adding Uber Direct next to DoorDash found four places the first integration had encoded a DoorDash-specific assumption as a delivery-integration assumption: OAuth instead of per-request JWT, no location registration at all, a fake-courier bot instead of a dashboard simulator, and webhook payloads with no status field. All four are axes on a checklist I had published seven weeks earlier. What changes after a hundred integrations is not what you know.

September 16, 2026

CanCan authorizes STI base classes before Rails reclassifies them

Fixing a spurious admin authorization flash led to a genuinely broad permission grant on a Single Table Inheritance base class - and the first fix, while correct about the symptom, quietly let a read-only admin role generate and download exports it had no business touching. The fix was Claude's. So was the review that caught it.

September 8, 2026

Two Rails versions get security patches today. On 7 November, it's one.

Rails 7.2 stopped receiving security fixes on 9 August 2026. Rails 8.0 stops on 7 November. That leaves 8.1 as the only series under the security window, and 8.1 itself drops out of bug-fix support on 10 October. Here is the actual calendar, the second clock most teams forget about, and the five minutes it takes to find out where your app sits on both.

September 7, 2026

Doorkeeper's most dangerous defaults are the ones you didn't set

Doorkeeper's generator gets you a working OAuth server in minutes - and silently ships an open admin UI, plaintext secrets, tokens that die every two hours, and a grant flow that skips consent entirely. None of it looks wrong in the initializer, because none of it is actually in the initializer.

September 1, 2026

Remote MCP on Rails: everything that broke

A real remote MCP server on Rails needs OAuth 2.1, three RFCs, and a healthy distrust of defaults. The failures were in Doorkeeper's silent defaults, the MCP gem's transport, and a generated fix that was stricter than the spec it implemented.

August 18, 2026

The Assistant That Couldn't Say Hello: Building a Grounded RAG Menu Chat for Spree Commerce

spree_menu_chat is a read-only RAG assistant grounded in a Spree store's real, synced menu and policies — Gemini, Voyage embeddings, and pgvector, with the read-only guardrail enforced structurally, not just prompted. A \r\n-vs-\n parsing bug silently broke every streamed answer behind a fully green spec suite, and even after that was fixed, the assistant still couldn't handle a plain "hi."

August 17, 2026

The Subscriber That Was Never Subscribed: Adding DoorDash Delivery to a Spree Commerce Platform

spree_doordash adds live-quoted, auto-dispatched DoorDash Drive delivery to the Spree + Square restaurant platform. Two real bugs — a phone number format DoorDash silently rejects, and an event subscriber that was never actually wired up — only surfaced once a real storefront checkout was driven through, not the 87-example spec suite that kept passing regardless.

August 14, 2026

Why I Open-Sourced a Square Integration for Spree Commerce

spree_square started as one restaurant's Square integration. Here's why it made sense to release it as open source, what it actually took to ship it responsibly — a full commit-history security review, closing test-coverage gaps first — and the bug the first real release caught within days.

August 13, 2026

The Cash Register Is the Database: Building a Restaurant Ordering Platform on Spree + Square

Why Square — not a second admin UI — is the source of truth for the menu in a Spree Commerce restaurant platform: the architecture that fell out of that call, a payment trick that never touches Square's card APIs, and two webhook quirks that only showed up against a live sandbox.

August 12, 2026

Shipping WhatsApp's Native Catalog + Cart: A Field Guide to the Undocumented Gotchas

What it actually takes to get a Rails app talking to WhatsApp's Cloud API with real Catalog and Cart support — eight undocumented walls, in the order I hit them, and the checklist that would have saved me a day.

August 10, 2026

I'm Learning MCP by Building a Server Claude Can Actually Log Into

A hands-on log of building an MCP server against a real Spree Commerce store seeded with 99,441 real, anonymised Olist orders: a silent data-loss bug from a fast insert path, a join fan-out that made my own analytics lie by 42x, and standing up OAuth with PKCE, discovery and dynamic client registration instead of skipping auth for a quick test.

August 7, 2026

I'm Learning Voice AI by Building an AI Receptionist for a Restaurant

A hands-on log of building a voice AI phone agent for restaurant ordering with no prior voice-AI background — the architecture bet that paid off, a toolchain bug with zero AI glamour, and the moment a confidently wrong AI got caught by a boring safeguard.

August 3, 2026

The API Design Checklist I Wish I Had at Integration #1

10 lessons on idempotency, webhooks, auth, and observability from 200+ production API integrations — and the checklist that came out of learning them the hard way.

July 18, 2026

Now taking fractional CTO and advisory engagements

After two decades building platforms and teams (WeWork's hyper-growth years, then eight years scaling a restaurant-integrations organization to 20,000+ restaurant locations), I'm opening a small fractional CTO and advisory practice.