Build platform, not product: how shared primitives let one person launch several SaaS products

My platform started as a Markdown editor. Over time, a web reader, APIs, an internal panel, licensing, auth and the clear idea of a publishing service appeared around it. The platform did not come from a blank page. It was hidden inside the product.

Note

The signal was not "I want to launch many SaaS products". It was this: every new product reused the same renderer, the same identity layer, the same quotas and the same delivery flow.

From product to platform

flowchart TD editor["Editor"] --> render["Render core"] editor --> auth["Identity service"] editor --> licensing["Licensing service"] publish["Publishing"] --> render publish --> auth publish --> licensing publish --> deploy["Deploy + edge service"] portal["Customer portal"] --> render portal --> auth portal --> licensing portal --> deploy exportApi["Exports API"] --> renderService["Render service"] renderService --> render

The difference between product and platform is not the size of the diagram. It is whether the shared pieces have their own contracts and can support new products without being copied.

Signs that you already have a platform

Repeated code

The same renderer appears in the editor, the web and dashboards. It is not a utility. It is core.

Shared services

Identity, licensing, billing, quotas and deployment keep showing up in every product.

Low marginal cost

The next product does not start from zero. It composes existing primitives and adds a thin layer.

Inventory of primitives

A useful platform is small. If everything is a primitive, nothing is.

Primitive Contract Products that use it
Render core Enriched Markdown to safe decorated HTML Editor, web, dashboards, publishing, portal
Identity service Identity, sessions, MFA and scopes Any product with login
Licensing service Plans, quotas, entitlements and billing Editor, publishing, portal, API
Server-side render service Server-side rendering and exports Publishing, export API, portal
Publishing service Deploy, themes, domains and audit Publishing, portal
Edge / object storage layer Assets and static sites Publishing, portal
Important

A primitive knows its domain. A product knows the primitive. If a primitive starts knowing products, it becomes a disguised monolith.

Products on the same base

Product What it sells What it reuses What it adds
Editor Local-first authoring Render, identity, license Desktop UI, Git, local export
Publishing Publish Markdown repos Render, identity, license, deploy Themes, domains, sites
Customer portal Private delivery to customers Render, identity, license, deploy Customer-project model
Exports API PDF/HTML/Word through an API Render, identity, license Public endpoints and quotas

The platform lets products share infrastructure without forcing them to share the same user experience.

The economics of reuse

The value appears when marginal cost drops:

$$
new_product_cost = specific_layer + integration(primitives)
$$

If the specific layer is small, one person can sustain more than one product. If every idea needs identity, billing, rendering and deployment from scratch, there is no platform. There is only a list of projects.

Boundaries that prevent a monolith

flowchart LR product["Product"] -->|composes| primitive["Primitive"] primitive -->|exposes contract| product bad["Product-specific case"] -. should not live inside .-> primitive primitive -. should not know .-> catalog["Product catalog"]
Boundary Practical rule
Identity Issues tokens with scopes; does not decide product screens
Licensing Models plans and quotas; does not contain commercial copy
Render Renders Markdown; does not know if it is in editor, publish or portal
Deploy Publishes bundles; does not decide navigation or content
Checklist before accepting a new primitive
  • It has a clear input and output contract.
  • At least two real products need it, or one critical product and another waiting.
  • It does not contain copy, layout or product-specific cases.
  • It can be tested without starting the whole platform.
  • It has an owner, README and version.
  • It replaces duplication that already hurts, not a future hypothesis.

How I plan new products

The first question is no longer "what do we need to build?". It is:

Tip

Which existing primitives does this product reuse, and which new primitive does it force into existence?

Answer Decision
Reuses almost everything and adds a thin layer Good candidate
Reuses part of the system but creates a valuable primitive Can enter the roadmap
Requires a different platform Does not belong here
Pollutes existing primitives with strange cases Reject or isolate it

The organizational part

Even as one person, the architecture needs clear compartments:

  • One repo or package per primitive.
  • Tests where the contract lives.
  • Products as consumers, not owners of the core.
  • Central documentation for decisions and boundaries.
  • Small migrations, with visual parity when UI is involved.

Discipline matters more than headcount. If I mix domains in my head, the code will mix them too.

The rule I keep

Platforms are not built from scratch. They are recognized when the same primitives already support several products and you start naming, versioning and protecting their boundaries.


This article is part of my platform notes. If you are interested in this work, follow the blog or contact me.