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.
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.
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.
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.
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.
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.
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.
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.
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."
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.
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.
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.
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.
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.
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.
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.
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.