← all work

the work / mar - may 2026

jettyai · gutbut

GutBut, a consumer AI gut-health companion built at JettyAI. Four codebases, shipped solo in seven weeks.

my part

Lead engineer and sole full-stack engineer. Architecture, schema, both clients, the admin tool, and the deploy pipeline.

the outcome

~25k LOC
in 7 weeks, internal beta

TypeScriptNestJSNext.js (App Router)React Native (Expo)Supabase PostgresRailwayPostHog

what it is

GutBut is a consumer AI gut-health companion. Users chat with an assistant grounded in their own meal and symptom logs, see a daily tip in the morning, and get a quiet nudge when their log starts to show a pattern.

I built it solo, end to end, over seven weeks.

what i did

I was the sole full-stack engineer on the product. I owned the architecture, the database, the backend, the web app, the mobile app, and a separate admin console for prompt management and product telemetry. The AI side ran as a sibling Python pipeline on a parallel track; I owned the contract between it and the rest of the stack.

Four codebases shipped in parallel: a NestJS backend, a Next.js web app with a server-side proxy, a React Native iOS app, and an internal admin tool.

why i built it this way

Most consumer wellness apps fail one of two ways. They become journals with a chat icon stapled on, where nothing you logged ever shows up in the conversation. Or they become chatbots that fake personalisation by stuffing a profile block into the system prompt and still forget what you told them ten minutes later.

I wanted the conversation to be the product, grounded in real logs and real long-term memory. That meant a backend that owned the data, an AI pipeline that owned retrieval, two clients sharing one wire contract, and an admin tool that kept the prompt under human control.

key points

One pinned wire contract shared across all four codebases. A field rename became one change, not four. This was the single decision that made solo full-stack at this speed actually possible.

Streaming chat over standard HTTP rather than WebSockets. Chat is one-shot per turn; standard HTTP gave me browser auto-reconnect and worked cleanly through every network in between.

Long-lived containers rather than serverless. Streaming connections and serverless are a bad fit, and the operational cost of a real container at this scale is negligible.

No ORM. Direct database access for everyday reads and writes, and the database itself for anything that needed to be atomic. Types came straight from the schema.

Push notifications mirrored into an in-app inbox. Push delivery is a coin flip; an inbox is deterministic. The user can always see what was sent, even when the OS quietly drops it.

how i tackled the harder pieces

A daily quota that survives abuse. The first version of the counter handed a fresh allowance to anyone who deleted their account and signed up again with the same email. The fix was to re-key the counter on a hash of the email so the easy reset path no longer worked. I was honest about what this does and doesn't cover: it removes the zero-friction reroll, not the determined attacker.

Voice input that doesn't hallucinate silence into the conversation. The transcription model, given two seconds of room hum, will cheerfully turn it into 'Thank you.' or 'Thanks for watching.' I added a short domain prompt that biased the decoder toward gut and wellness vocabulary, then a small filter that catches the residual cases. Empty input beats wrong input.

Notification policy that doesn't ping the user at three in the morning. Six different reasons to send a push, all of them competing for the user's attention. I pulled the decision out of the cron jobs into a single function that takes a state object and returns allow or deny with a reason. Quiet hours, per-engine caps, daily caps, priority preemption: all in one place, all testable without standing up the rest of the system.

claude code as a daily instrument

Seven weeks of solo full-stack across four codebases is only possible with a real coding partner. I treated Claude Code like a senior engineer pairing with me, one I could brief once on a problem and trust to come back with a real implementation.

I used it for the structural work: designing the wire contract, writing migrations, sketching the notification policy module, building out admin screens. I used it for the boring work: types from the database schema, repetitive form scaffolding, deploy configuration, README updates. And I used it for the surprising work: walking through a streaming failure with me late at night, or reading a stack trace before I had.

It compressed the shipping cadence in a way I do not think a single engineer hits otherwise. Claude Code carried the parts where the only blocker was throughput, which left me free to spend real attention on the decisions that actually mattered.

outcome

The product reached internal beta on the timeline I had committed to. The architecture supports a 10K-concurrent-user scale target without rework. A new client (a watch app, a CLI, a partner integration) inherits the wire format for free.