Skip to content

Product engineering

SaaS engineering

SaaS Development

A SaaS is not an app with a login. It is other people's data sharing your database, work that has to happen after the request returns, and a bill that scales with usage you have to be able to measure. We build that layer.

One database with a tenant on every row, and the work that leaves the requestPRD · 10
Overview

Tenancy is a decision you make once

The first real decision in a SaaS is how tenants are separated, and it is close to irreversible. A shared schema with a tenant column is cheap and fast until the day one customer needs their data somewhere else. A database per tenant is clean until you have four hundred of them to migrate. We pick deliberately, write down why, and enforce it at the data layer rather than trusting every future query.

The second is what happens outside the request. Ingesting events, running experiments, generating reports and processing uploads do not belong in an HTTP handler. They belong in a queue and a worker, with retries, dead letters and a way to see what failed — which is the difference between a product that survives its first busy week and one that quietly loses data.

Isolation you can prove
Tenant scoping is enforced where the data is, not in each query by hand, so a missed filter is a failed test rather than a leak.
Work happens off the request
Queues and workers absorb ingestion, exports and long jobs, so a slow third party never becomes a slow product.
Storage chosen per job
Transactional data in PostgreSQL, hot state in Redis, event volume in analytical storage. One database for all three is how SaaS products get slow.
What's included

What the engagement covers

  1. Tenant model, isolation strategy and enforcement at the data layer
  2. Plans, limits and usage accounting
  3. Event collection and an ingestion pipeline
  4. Background workers with retries and dead-letter handling
  5. PostgreSQL, Redis and analytical storage where volume needs it
  6. Object storage for uploads and generated artefacts

Technologies

  • Node.js
  • TypeScript
  • PostgreSQL
  • Redis
  • ClickHouse
  • Workers
  • Object Storage
  • Railway
Use cases

Where this helps most

  • An experimentation or analytics product

    High-volume event ingestion, per-tenant dashboards and queries that must stay fast as the event table grows.

  • A tool you already sell as a service

    An internal tool that customers now want access to, and which needs tenancy, plans and limits before it can be sold.

  • A Shopify app that outgrew one database

    An app with enough installs that job processing, webhooks and reporting need to move off the request path.

Process

How we deliver

  1. Tenancy and data model

    How tenants are separated, what a plan limits, and what usage has to be countable — decided before the first table, because it is the expensive one to change.

  2. The core product

    The application itself, with tenant scoping enforced at the data layer and the read paths shaped for how the product is actually used.

  3. Pipeline and workers

    Events in, jobs out. Queues, retries, dead letters and the observability to see which tenant a failure belonged to.

  4. Operate it

    Deployment, migrations that run safely against live tenants, and the runbook for the failures that will happen.

FAQ

SaaS Development

Shared schema or a database per tenant?
Usually a shared schema with enforced tenant scoping, because it is far cheaper to migrate and operate. A database per tenant earns its cost when there is a compliance or residency requirement, or when one customer is large enough to need their own performance envelope. We decide it against your actual constraints, not by preference.
Do we need a separate analytical database?
Only when event volume makes the transactional database slow at the queries the product depends on. Below that point it is complexity you are paying for early. We would rather add it when the shape of your data asks for it than build it on day one because it sounds right.
Can we self-host it or move providers later?
That is a design constraint, so tell us early. Keeping to portable pieces — containers, PostgreSQL, an object store with an S3-compatible API — costs little up front and preserves the option. Building on one provider's proprietary primitives is faster and closes it.
How do you handle migrations with live tenants?
Expand and contract: add the new shape, write to both, backfill, move reads, then drop the old one. It is more steps than a single ALTER, and it is the difference between a migration and an outage.
Let's build

Ready to start with saas development?

Tell us about your store and your goals. We'll come back with a clear, honest plan and a transparent quote.