- Is the generated app multi-tenant out of the box?
- Not by default. The starting schema is single-user: records hang off a user identifier, which is right for a personal tool and wrong for anything a team shares. Ask for organizations explicitly, then make sure every domain table carries the organization reference alongside the user who created the row. Retrofitting that column later means a migration, a backfill, and an audit of every query you have written, which is a bad week compared with one sentence in the initial prompt.
- How should roles and permissions be checked?
- Put the role on the membership joining a user to an organization, not on the user, because the same person can be an owner in one account and a viewer in another. Then funnel every check through one authorisation function that takes the subject, the organization, and the action. Permission logic scattered across routes drifts within weeks: one endpoint forgets the check, and the bug report arrives from a customer who saw another customer data.
- Where do plan entitlements come from?
- From your own database, written by verified billing webhooks. When a subscription is created, updated, or cancelled, the handler validates the signature and updates the subscriptions row for that organization; the application then reads its own row to decide what is unlocked. Never take the plan from a client request or from a value cached at sign-in, and never call the billing API on the request path just to answer whether a button should be enabled.
- How are seats counted when someone joins or leaves?
- Decide first whether a seat is an accepted membership or an issued invitation, because the two produce different invoices and customers notice. Then reconcile in both directions: change the subscription quantity when membership changes, and correct your own count when a billing event tells you the quantity changed elsewhere. Providers handle proration for the mid-cycle case, so let the billing system compute the money and keep your side limited to the number.
- How do trials and expiry work without a scheduler on every request?
- Store a trial end timestamp on the organization and evaluate it when a request arrives, which makes expiry correct without any background work. Use a scheduled trigger only for the things that must happen even when nobody visits: the reminder email a few days out, the downgrade notice, the report of accounts that lapsed. Deriving state at read time and notifying on a schedule keeps the two concerns from tangling.
- Can customers change plans themselves?
- Yes, and the least code is to send them to the billing provider hosted portal for upgrades, downgrades, payment method changes, and cancellation. Your application does not process any of it; it reacts to the events that follow. That removes an entire surface of edge cases -- expired cards, proration arithmetic, tax identifiers -- from code you would otherwise maintain, and it keeps card details away from your worker entirely.
- What does a safe invitation flow look like?
- A dedicated table with a single-use token, the organization, the intended role, an inviter, an expiry, and a redemption timestamp. Send the token by email, accept it once, and record who accepted. Do not reuse the password reset path, and do not derive an invitation from an email address alone: an unbounded invite link forwarded into a group chat is how a stranger ends up inside a customer account with a role you did not intend to grant.
- Can users sign in with Google or GitHub?
- The user table already carries a provider column and a provider identifier for exactly this, so the schema does not fight you. What you add is the callback route in the worker, the client secret held in the platform secrets manager, and a rule for what happens when an address arriving from a provider matches an existing password account. Link deliberately after verification rather than merging silently, because silent merging is an account takeover waiting for a mismatch.
- Should the marketing site and the product share one project?
- They can, and there is a trade-off worth naming. The generated front end is a client-rendered single-page application, which is ideal for a dashboard behind a login and unremarkable for pages that need to rank in search. If organic acquisition is central, keep the public pages somewhere that serves complete HTML and let the application own everything after sign-in. If acquisition is sales-led, one project is simpler and the trade-off costs you nothing.
- How should usage be metered for billing?
- Append immutable events with an idempotency key rather than incrementing a counter, because a retried request that double-increments becomes an invoice dispute. Roll the events up on a schedule into a period summary you can show the customer, and keep the raw events long enough to answer a question about a specific charge. Metered billing is the one place where being able to reconstruct a number from its inputs is worth the storage it costs.