tezvyn:

Design the data model and backend for a 7-day trial at scale

AI-drafted, machine-checkedSource: docs.stripe.comintermediate
Design the data model and backend for a 7-day trial at scale

Tests state machine design for time-bound entitlements at scale. A strong answer covers: an idempotent enrollment API, a trial ledger with timezone-aware expiration, and an event-driven expiration pipeline.

WHAT THIS TESTS: Your ability to design a stateful, time-bound entitlement system for high scale. The interviewer cares about data model precision, race-condition handling, and whether you treat trials as first-class subscription states rather than simple boolean flags.

A GOOD ANSWER COVERS: First, the data model. You need a users table, a trials table with trial_id, user_id, started_at, ends_at, timezone, status (trialing, converted, expired, cancelled), and source metadata. You also need an entitlements or features table that resolves access by checking active trials or paid plans. Second, enrollment logic. On signup, an idempotent enrollment service creates the trial record and publishes a TrialStarted event. If the user already has a trial, the service returns the existing record to avoid duplicates. Third, expiration and state management. Instead of a global cron job scanning millions of rows, use a delayed message queue or a scheduler that enqueues an expiration task per user at their exact ends_at timestamp. Alternatively, shard time-bucketed workers, but delayed events are preferred. Fourth, conversion and billing integration. When the trial ends, the entitlement service checks for a paid subscription. If none exists, revoke access and emit TrialExpired. If the user converts, Stripe sends webhook events like invoice.paid or customer.subscription.updated, which your service consumes to update status to active and extend entitlements. Fifth, edge cases. Handle early cancellation by setting status to cancelled with a cancelled_at timestamp. Handle timezone awareness so a user who signs up at 11 PM does not lose hours. Handle clock skew by using database timestamps or vector clocks rather than client time.

COMMON WRONG ANSWERS: Proposing a single nightly cron job that queries every user to check trial end dates. This does not scale past a few hundred thousand users and creates a thundering herd. Storing trial state only in Redis without a persistent source of truth. Using client-side clocks to determine expiration. Forgetting idempotency so double signups create duplicate trials. Ignoring the endowment effect mechanics by not having a downgrade hook that explicitly removes the feature, which is the actual product lever.

LIKELY FOLLOW-UPS: How would you change this if the trial length were variable per user cohort? How do you handle a user who signs up, cancels immediately, then signs up again to get another trial? How would you support in-app purchases where Stripe is not the source of truth? What metrics would you log to measure the endowment effect?

ONE CONCRETE EXAMPLE: A user signs up at 2024-01-01 09:00 PST. The enrollment API writes a trial record ending at 2024-01-08 09:00 PST and publishes a delayed SQS message visible at that exact time. The entitlements service returns true for the premium feature because status is trialing. On expiration, the worker consumes the message, sees no paid subscription, sets status to expired, and emits an event that triggers an in-app downgrade modal highlighting what the user is about to lose.

Source: docs.stripe.com

Read the original → docs.stripe.com

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.