Software Engineering

Opinionated on purpose. The inside of the repo, already decided.

We bootstrap your application repository the way we run our own products in production — Bun, TypeScript, Elysia, Postgres, React, Clerk — multi-tenant from line one, typed end to end, with the contract drift, the money math, and the quality gates already handled. 5 weeks. The code is yours from day one.

The first six weeks of a greenfield repository set the next two years of velocity. This is the version of those six weeks we have already paid for on our own products.

Fixed price, fixed scope. Catalog P6 · 5 weeks.

This is opinionated software. That is the product.

A starter kit gives you options. We give you decisions. Every choice below was made once, argued about in production, and revised where it did not hold up — across the SaaS applications we still operate. You are not buying a template with the blanks left in; you are buying the arguments already lost, so your team spends week one on the domain instead of on the build system.

The honest version of that trade: if you disagree with the stack, this is the wrong product, and we say so on the call. If you agree with it — or have no strong feeling and want a defensible default — you skip a sprint of scaffolding and inherit patterns that already survived live traffic.

Runtime & language
Bun + TypeScript in strict mode with noUncheckedIndexedAccess, one root tsconfig with project references.
not Node-vs-Bun-vs-Deno, and the six weeks of tsconfig archaeology that follow a loose start.
API layer
Elysia + TypeBox routes that emit their own OpenAPI spec, served at /docs through Scalar.
not Express vs Fastify vs Nest, and a hand-maintained spec that drifts from the routes by week three.
Client contract
A Kubb-generated typed client committed to git, with a TanStack Query hook per route.
not tRPC vs a hand-rolled fetch wrapper vs “we’ll type the responses later.”
Data layer
Drizzle on Postgres, schema and migration history checked in, migrations run as a one-shot task.
not Prisma vs TypeORM vs raw SQL, and schema work happening at process start.
Identity
Clerk with server-side token verification, plus a hashed API-key path for machine callers.
not Building a session layer, then rebuilding it the first time SSO comes up in a deal.
Frontend
React 19 + TanStack Router file-based routes, TanStack Query on the generated client, Tailwind v4.
not A framework debate that ends in a rendering model nobody on the team can explain.
Public site
A separate Astro static site for marketing, docs, and a live status page.
not Shipping SEO pages out of the authenticated SPA and paying for it in Core Web Vitals.
Tooling
Biome as the single formatter and linter, wired into a pre-commit chain that has to pass.
not Maintaining Prettier and ESLint in parallel, and a CI that lints code nobody formatted.

The components, deployed to the shape of your application.

Ten components make up the bootstrap. Most ship in every engagement because they are the floor, not a feature. The rest are deployed when your domain calls for them — decided in week one during scoping, not discovered as a change order in week four.

Always in Deployed when your domain needs it

Monorepo with real contract boundaries

Always in

Eight first-class packages, and a contract package that makes duplicate truth a compile error rather than a code-review convention.

  • Bun workspaces with eight packages: shared, database, backend, worker, scheduled-jobs, api-client, frontend-app, frontend-public. Deploy the ones your domain needs; the boundaries hold either way.
  • A shared contract package — enums, Zod schemas, error classes — imported identically by the backend, the worker, and the jobs. One definition, three consumers, no drift.
  • Module-per-domain backend layout (src/modules/<name>/{routes,service}.ts) with constructor-injected services wired once at composition time. No globals, no service locator.
  • TypeScript strict plus noUncheckedIndexedAccess across every package, with project references so a build order exists on day one.

Typed API client, generated and drift-checked

Always in

The frontend cannot call an endpoint that does not exist, because the client is generated from the routes and the build fails when it falls behind.

  • Elysia + TypeBox routes auto-generate the OpenAPI spec; Scalar serves it at /docs for anyone integrating.
  • Kubb generates the typed client and a TanStack Query hook per route (useListX, useCreateX, …), committed to git as a workspace package.
  • A verify:api-client pre-commit hook regenerates spec and client and fails if the committed client drifts from the routes. Contract drift is impossible to merge.
  • The frontend consumes the generated client exclusively — a backend rename surfaces as a TypeScript error in the UI, not a runtime 404 in production.

Postgres data layer with multi-tenancy baked in

Always in

Tenant scope is a property of the data layer, not a habit the next engineer is expected to remember.

  • Drizzle ORM with schema, relations, and migration history checked into the database package.
  • drizzle-kit migrations run by a dedicated one-shot runner, separate from app start — no schema work racing your health check.
  • Org-scoped model from line one: organizations, users, organization_members, plus scoping helpers that make an unscoped query a TypeScript error rather than a data-leak incident.
  • Money stored in micros — integer millionths. No floats, no sub-cent rounding drift if billing ever lands on top.

Auth, authorization, and an RBAC skeleton

Always in

Two callers — humans and machines — each with its own path, and one permission vocabulary shared by the API and the UI.

  • Clerk on the dashboard path with server-side token verification. Client-decoded claims are never trusted; a Clerk-to-local user sync runs on the first authenticated hit.
  • An API-key path with SHA-256 hashes at rest, an 11-character display prefix, and an in-memory LRU cache with TTL. Keys are shown once at creation and are never recoverable.
  • A shared Action enum plus an owner / admin / member role table — the same enum the backend enforces is the one the frontend reads, so the UI never offers a button the API rejects.
  • Resource limits (per-org, per-app ceilings) plumbed as a service-layer pattern instead of scattered across handlers.

React frontend, seeded and wired

Always in

A running app on day one — routed, authenticated, typed against the real API, with a design system you can replace but do not have to.

  • React 19 + TanStack Router with file-based routes and a generated route tree; public and authenticated route groups mirroring the API surface.
  • TanStack Query wired to the generated client, auth context via Clerk.
  • Tailwind v4 + a component layer, shipped with a seed design system documented in a checked-in DESIGN.md — tinted neutrals, one accent, mono for data. Functional on day one, replaceable whenever brand lands.
  • An Astro public site with home, pricing, docs, and a live status page hooked to the backend health endpoint.

Worker, queue, and scheduled jobs

Scoped in week one

Deployed when the domain has async work. The idempotency is structural — a redelivered message is a no-op by construction, not by handler discipline.

  • A Lambda + SQS worker package using the batchItemFailures pattern: only failing records redrive, the rest acknowledge.
  • Claim-before-side-effect idempotency wired as a base class. At-least-once delivery stops being a correctness problem.
  • A scheduled-jobs package with a base handler (processed counts, per-entity errors, structured exit status) and period-key idempotency — an (entity, period, reason) uniqueness short-circuits a re-fire.
  • Metric emission and tracing initialized, with a PII-stripping structured logger so message bodies never reach your logs.

Security posture by default

Always in

The controls that are cheap on day one and expensive to retrofit after the first pen test.

  • Security headers on every response — content-type options, frame-deny, referrer policy, permissions policy.
  • Allow-list CORS read from typed env. A wildcard origin throws at boot rather than in production traffic.
  • A request-body cap at the framework layer and a per-route rate limiter with an LRU cap and periodic sweep.
  • SSRF protection ahead of any outbound fetch — localhost, RFC1918 ranges, and the 169.254/16 metadata range are blocked before the call is made.
  • An audit-log shape (actor, resource, result, meta) routed to its own child logger, so the security trail is not buried in application noise.

Quality gates and local-dev parity

Always in

One command brings the stack up. One chain decides whether a commit is allowed to exist.

  • A pre-commit chain: file hygiene, typecheck, format, lint, api-client verification, unit tests, build. No commit lands on a red stack.
  • A commit-message hook enforcing a ticket prefix and a linked-issue line, wired straight into your Jira or Linear project.
  • GitHub Actions CI and deploy workflows consuming shared reusable workflows — versioned centrally, not copy-pasted per repo.
  • docker-compose for local Postgres, DynamoDB Local, and an SQS/scheduler emulator, plus the Lambda runtime emulator for worker testing.
  • A Makefile front door — make up, seed, local-reset-db, integration-tests, test-all, generate — with container-runtime auto-detection so it works under Podman or Docker unchanged.
  • An idempotent seed: re-running the reset does not accumulate rows.

A test pyramid that is wired, not aspirational

Always in

Three levels, each with something real running in it before your team writes a line of domain code.

  • Vitest at the workspace level with sample unit tests over the provider-chain primitives — circuit breaker, retry policy, token-bucket limiter — generic enough to reuse in any domain.
  • A backend integration harness running against real Postgres and DynamoDB through the local stack: reset, seed, suite.
  • An AI-driven browser end-to-end protocol: JSON test specs executed against a real frontend with real test credentials, with starter specs for auth, navigation, and responsiveness.
  • One end-to-end smoke spec that proves the whole stack works the day it is handed over.

Documentation as a deliverable

Always in

The repo shape that keeps both a new engineer and an AI agent productive — because both fail for the same reason: missing context.

  • A docs/ register — architecture, implementation plan, key decisions, plans, tasks, and a running log — in the same shape we run internally.
  • DESIGN.md (visual register) and PRODUCT.md (brand and product register) pre-filled with your name, ICP, and positioning captured during scoping.
  • A root CLAUDE.md that declares project shape, quality-gate commands, and the actual gotchas — so an agent is useful in the repo on day one and is not working from a document that lies.
  • A 90-minute recorded walkthrough of the repo for your engineering team.

Scoped on request

Append-heavy DynamoDB adapter for message, event, or audit-trail data shapes; the worker, queue, and scheduled-jobs package; a second locale wired through i18n (files seeded, copy not translated); additional end-to-end specs beyond the seeded three; the credit-ledger money model left wired for a later billing integration. Scoped in week one, before a line of code is written.

The standards that come attached.

These are not aspirations written into a wiki nobody opens. Each one is enforced by something that fails — a type error, a failed hook, a process that refuses to boot. A standard that depends on remembering is not a standard.

  • Contract drift is impossible to merge.

    The generated client is regenerated and diffed in the pre-commit chain. A route rename that would have been a runtime 404 is a failed commit instead.

  • Tenant scope lives at the data layer.

    Every repository method takes the organization and applies it as a filter. The route guard is the suspenders; the data-layer filter is the belt.

  • Money is an integer.

    Micros — millionths — everywhere money exists. Floats are how a ledger and an invoice disagree by a cent for two years.

  • Claim before the side effect.

    Queues deliver at least once. Handlers that claim the work before performing it are idempotent by construction, not by the discipline of whoever writes the next consumer.

  • No raw environment reads in business code.

    Configuration is parsed and typed once at boot. A missing or wildcard value fails the process on startup, not a request at 2am.

  • Logs are redacted at the boundary.

    Message bodies, tokens, and content fields are stripped by the logger itself. Redaction that depends on remembering is redaction that fails.

  • The UI never offers a button the API rejects.

    One permission enum, imported by the enforcement layer and the render layer. Authorization mismatches become type errors.

  • The whole gate chain runs before the commit exists.

    Typecheck, format, lint, contract verification, unit tests, build. CI confirms what your laptop already proved instead of discovering it.

Where this stops.

An opinionated product is only useful if the opinions are stated before you buy. Here are ours, including the ones that cost us the deal.

If your team is not a TypeScript team, this is not your product.

The bootstrap is TypeScript end to end — runtime, API, data layer, client, worker, jobs. If your engineers are strongest in Go, Python, Rails, .NET, or Java, the correct answer is that you should not buy this. The patterns underneath it — tenant scope at the data layer, generated contracts, claim-before-side-effect workers — travel to any language, but the artifact we hand over does not. In that case the useful conversation is about the AWS substrate instead.

See Platform Engineering

Ship the new SaaS, end to end.

The bootstrap delivers the inside of the repository. The Container Platform delivers the AWS the repository runs on. Bought together, they take a greenfield product from decision to deployed — one statement of work, delivered in sequenced phases so quality does not get traded for parallelism. Combinations are negotiated per deal; there are no pre-priced bundles.

  1. Application Bootstrap

    The monorepo, the typed contract toolchain, the multi-tenant data model, auth and RBAC, the frontend, the gates, the tests, the docs register. Partially parallel with foundation work when an AWS engagement runs alongside it.

    P6 · 5 weeks

    Read the scope
  2. Container Platform — ECS

    The shared ECS Fargate substrate and the per-app deployment surface — service, registry, migrations task, certificates, secrets, an ARN-scoped deploy role, and the alarm pack that catches what AWS-native monitoring cannot see.

    P4 · 4–6 weeks

    Read the scope

Every pattern here was paid for by an application we still operate.

Our own products, running in production. The bootstrap is not a synthesis of blog posts — it is the intersection of those codebases, kept where they agreed and revised where they did not.

  1. Comms

    A single HTTP API that sends Email and WhatsApp through one contract, one bill, and one place to debug delivery.

    Where the provider-chain primitives and the hashed API-key path come from.

    Read the architecture
  2. ExpertCV

    Résumé and cover-letter generation grounded in a per-request EvidenceMap, so the LLM cannot fabricate.

    Where the typed-contract toolchain and the credit-ledger money model come from.

    Read the architecture
  3. PilatesTime

    B2B SaaS for pilates studios — PIX, multi-tenant RBAC, and a billing audit log that costs nothing extra to keep.

    Where the RBAC skeleton and the audit-log shape come from.

    Read the architecture
  4. RuralOps

    Bilingual ag-ops SaaS on Bun + Elysia, with multi-tenancy at the schema layer and AI assistant features that don’t pretend to know things they don’t.

    Where the scheduled-jobs base handler and the i18n wiring come from.

    Read the architecture

Starting a new product? Bring us the first architecture decision.

30 minutes with the founder. The Scorecard itself is AWS-shaped — eight dimensions scored against the controls a production account should clear — but if your question is the repository rather than the account, say so when you book and we will spend the call on that instead.

We will be honest about whether you need us. If your team already has these patterns, or the stack is wrong for you, we will say so on the call rather than in month two.

Get the Scorecard

Free. No follow-up call unless you ask for one.

Skip the form. Talk to a person.

Plain email. No marketing reply, no scheduling tool, no funnel. Tell us what you're shipping and we'll tell you whether we're the right fit.