How VibeGym Works: A Small App, Real Checks and Trade-offs
A first-party case study of VibeGym's site, learning app and newsletter: the data flow, a real failure, verification steps and reusable lessons.
By VibeGym Team · · Updated

Start with the responsibility of each part
VibeGym is a useful example because it contains two different experiences: public guides that should be easy to read and an account-based learning app that saves progress. We use a static Astro site for the first and a Next.js application for the second. A FastAPI service handles account and training data in PostgreSQL.
This article describes our own implementation and a specific improvement made in October 2026. It is not a recipe saying every first project needs four technologies. For a one-page site, starting with less is usually easier to inspect.
| Part | Responsibility | What should happen if another part fails? |
|---|---|---|
| Public site | Guides, curricula and a free sample exercise | The text remains readable without app login or analytics |
| Learning app | Lessons, practice and account screens | It shows a useful state while account data loads |
| API | Validate requests, enforce access and update progress | It returns an explicit failure when an operation cannot finish |
| Database | Store account, progress and subscription state | Changes follow a migration and can be checked independently |
The important design choice is the boundary. A public article does not need to fetch a learner’s account. A browser interface must not decide by itself whether someone may access another person’s records.
A concrete failure: a form with nowhere to send the request
The earlier marketing newsletter form expected a configured endpoint. That endpoint was absent on the published site, so a valid email could not become a subscription. The form displayed an error rather than pretending the email had been saved, but the feature still did not complete its job.
The acceptance condition for the repair was more precise than “make the button work”:
- A valid request creates a pending subscription and sends a confirmation email.
- Only the recipient’s confirmation activates the subscription.
- A provider failure produces a retry state rather than success.
- Repeated requests do not send repeated emails immediately.
- Unsubscribing invalidates an unused confirmation link as well as stopping future newsletter delivery.
This is the same way you can turn a vague task into a one-feature brief.
Model the states before writing the UI
The subscription moves through pending → confirmed → unsubscribed. A later request from an unsubscribed address still needs a new confirmation. The API keeps a hash of the confirmation token and an expiry time. The email link opens a page where the reader explicitly confirms the action.
The UI has its own observable states: waiting for a response, request accepted, provider unavailable and connection failed. “Request received” is different from “subscription confirmed.” Keeping those words accurate helps the reader understand what to do next.
A second repair concerned analytics. The old marketing implementation could call the page-view function before the asynchronously loaded library was ready. The revised flow waits for both consent and the provider. It carries the first landing page into later app events after opt-in, so a guide visit can be associated with a later lesson or checkout event.
What we checked
For the newsletter work, we ran HTTP integration tests against a separate local PostgreSQL database. The tests exercised confirmation, repeated confirmation, unsubscribe, invalidated links, fresh opt-in, expired tokens, duplicate email requests, provider failure and a spam honeypot. Together with the existing email-provider tests, that targeted run passed 47 tests on 3 October 2026.
Separate browser-analytics tests checked that attribution is not persisted before consent, survives a move from the site to the app and is cleared when consent is withdrawn. They also checked that URL tokens and the search query in a referrer are not copied into our acquisition fields.
These checks do not prove an email reached a particular inbox, that an external analytics service retained an event or that every user flow is flawless. Those need provider and browser verification. A test result is evidence for the cases the test actually exercised.
Publish the useful parts of the product
The public course pages read lesson titles and summaries from the app’s catalog at build time. This avoids maintaining two separate versions of the syllabus. The free debugging exercise explains the consequence of three possible next steps. The templates are downloadable without an account.
For a learner, this means there is something concrete to inspect before entering a signup flow. For the product, it creates pages whose value is visible directly in the document, rather than hidden behind a quiz or login.
Trade-offs we would discuss before copying this stack
A static content site is straightforward to serve, but content changes require a build. Separate app and marketing origins make responsibilities clear, but attribution and consent need deliberate handling. A database-backed confirmation flow supports reliable state checks, but it also introduces migrations, delivery failures and data-retention responsibilities.
For your first project, copy the discipline before copying the architecture: write the intended behavior, decide where data lives, keep a reversible checkpoint, test the failed case and inspect the deployed result. The review checklist is a starting point; the debugging guide shows how to collect evidence when a check fails.
#case study#debugging#verification#building apps


