A FastAPI backend for a fitness and health-tracking mobile app, delivering AI-driven workout planning, wearable device integration, and real-time AI coaching chat — running serverless on GCP Cloud Run with Terraform-managed infrastructure.
Read as Markdown ↗
I built Voxx's backend on FastAPI and async SQLAlchemy 2.0, where the core problem was that workout plans can't be static: a user's sleep, HRV, strain, and pain signals shift daily, so the plan has to revise accordingly — reliably and fast. I split revision into two layers: daily plan updates run AI-first with a rule-based fallback (a failed Gemini call silently drops to deterministic rules), while the reschedule-driven repair of the 14-day plan is fully deterministic, running without a database or AI call, following a priority cascade — swap, then micro-shift, then add buffer, then downscale, then drop, then suggest a full regenerate. AI is used only where it's actually needed — content generation — while the decision logic stays deterministic, both for cost and for testability. On the wearable integration side I hit a real production incident: large backlog webhooks from Apple Health (~9.7MB, mostly per-second heart-rate samples) were hitting the global 5MB body limit and returning 413s, and the Insights screen was silently showing 'No Data' to users. I fixed it with a path-specific body-limit override, lossless shrinking of raw_data (dropping the unused detailed HR array, ~4.3MB down to 27KB per record), guards against malformed content-length/chunked bodies, and a 503 + Retry-After response during Redis outages so Terra would retry — tracing the issue to its root cause and preventing permanent data loss. I also treated specification ambiguity as a product decision rather than an engineering guess: when a requirement like '48 hours' admitted more than one reading, I wrote up the interpretation I chose (e.g., calendar-day based) with its rationale and put it in front of the client for sign-off, flagging it as reversible with a small change — making the ambiguity visible instead of papering over it with a guess. Across the codebase I defined a 9-rule composition pattern for service/collaborator boundaries (class vs. plain function vs. dataclass, package vs. flat file) and applied it consistently across modules — for example, the injury-risk/ACWR calculators are independent, stateless pure functions, with subtle behavioral differences like '0.0 strain means missing data' documented explicitly in code, since it has to change consistently across four different producers.