Let's find the work AI should be doing in your organization.
Whether you are exploring an idea or improving a system already in use, we’ll help you decide what is worth doing next.
Backend engineering is the work of preserving meaning across boundaries: a transaction that commits once, a tenant that cannot see another tenant, a job that can be retried safely, an API that can evolve, and a failure that leaves enough evidence to recover. We choose frameworks, databases, queues and runtime topology around those guarantees — not the other way around.
These are backend responsibilities before they are technology choices. The tools show where our work has landed; each linked section explains the engineering contract behind them.
| Layer | What we use |
|---|---|
| Framework & system shape | RailsPayloadSvelteKit serverModular monolithsServices |
| API & contracts | RESTOpenAPIGraphQLRPCWebhooks |
| Data architecture | PostgreSQLD1 / SQLitepgvectorRedisS3 / R2 |
| TDD & verification | MinitestRSpecVitestPlaywrightWebMock |
| Jobs, events & integrations | Solid QueueSidekiqBullMQCloudflare Workflows |
| Security & tenancy | OAuth 2JWTPolicy authorizationRate limitsEncryption |
| Runtime topology | Cloudflare WorkersECS / FargateKamalServerlessContainers |
| Delivery & infrastructure | DockerAWS CDKMigrationsCI/CDHealth checks |
| Observability & recovery | Structured logsNew RelicMetricsJob dashboardsBackups |
The first backend decision is not Rails versus Node. It is where consistency has to be immediate, which work can happen later, what the team must operate, and how many independent failure domains the product can afford. We usually begin with a modular monolith because one deployable unit keeps transactions, refactoring and operational ownership legible. We separate services only when a boundary earns independent scaling, security, release cadence or runtime requirements.
Rails gives long-lived transactional products a coherent domain model, mature migrations, policy authorization and background work in one system. Payload gives TypeScript products a typed schema, generated admin surface and local server API beside REST and GraphQL. SvelteKit on Cloudflare Workers is useful when a small backend can live close to its users and depend on platform bindings rather than long-running processes.
These are implementation choices, not agency boundaries. We can inherit another serious backend framework and apply the same decision model without first replacing it.
A service boundary has to pay for its network hop, deployment surface and new failure mode. “Microservice” is not a synonym for well-structured code.
An API is a compatibility promise. We specify its inputs, outputs, authentication, errors and evolution rules where they can be tested, then generate documentation and clients from the same contract. REST is the default for durable public boundaries; GraphQL, RPC and framework-local APIs are selected when their coupling and query model are explicit rather than accidental.
The schema is useful only when production behaviour and the published contract can fail the same test.
PostgreSQL is our default because most product data has relationships, constraints and changes that need to commit together. We add document, search, vector, cache and object stores for access patterns they serve better—not to avoid modelling the source of truth. Each additional store needs an owner, a derivation path and a repair strategy.
“Non-relational” is not one database category. A document, cache, vector index and object store solve different problems and fail in different ways.
Test-driven development is most valuable at the backend boundaries where a small ambiguity becomes durable data. We write the next behaviour as an example, implement the smallest coherent change, then refactor with the contract held in place. The suite is layered so domain feedback stays fast while requests, databases, workers and deployments receive the integration evidence they need.
Coverage is evidence of execution, not evidence of the right assertions. We optimise for meaningful boundaries and failure cases.
A job queue is not a reliability strategy by itself. Workers need idempotency, bounded retries, visible terminal failure and enough context to reconcile with the system of record. The same rules apply to schedules, notifications, webhooks, ingestion pipelines and third-party APIs: acknowledge only what is durable, assume delivery can repeat, and preserve a path to repair.
At-least-once delivery is common. Exactly-once business effect is something the handler earns through its data model.
Authentication proves an identity; authorization proves that identity may perform this action on this record in this tenant. We keep those decisions on the server boundary, scope data before it is returned, rate-limit expensive or abusable paths, and record access to sensitive material. Security is implemented as testable application behaviour and reinforced by the runtime—not postponed to an infrastructure checklist.
A tenant ID in a request is user input. Isolation begins with the authenticated context, not with trusting that parameter.
Serverless and containers are runtime choices, not competing ideologies. Edge workers are excellent for bounded request work with platform storage and durable orchestration. Long-running containers are the better fit for connection pools, background workers, browser automation, media processing and workloads that need specialised binaries. We can combine both when the trust or execution boundary demands it.
Containerization packages a workload. It does not decide whether that workload should be long-running, independently scaled or publicly reachable.
A backend release changes code, schema, workers, configuration and sometimes network topology. We describe infrastructure in code, build immutable artifacts, sequence migrations explicitly, and make health checks prove more than process existence. Deployment is complete only when the new version can serve traffic, its workers understand the schema, and rollback has a defined data story.
A green image build is not a green release. The system is the artifact plus the schema, bindings, workers and traffic policy around it.
Observability starts with the questions an operator must answer: which tenant or request failed, what changed, whether the failure is spreading, and what can be retried safely. We connect structured events, service metrics, traces, job state and deployment identity so an alert points toward a decision. Recovery then turns that evidence into tested procedures for replay, rollback and restoration.
The useful question is not “do we have logs?” It is “can the person on call decide what is safe to do next?”
These practices come from running multi-tenant commerce platforms, community products, AI workspaces, event discovery systems and encrypted document vaults — across Rails, Payload, PostgreSQL, Redis, AWS containers and Cloudflare's serverless runtime.
Owning those systems after launch is why the page is opinionated about executable API contracts, migrations, tenant isolation, idempotent workers, dedicated migrators, encryption boundaries and recovery evidence. They are the details that decide whether a failure becomes a brief incident or corrupted state.
Team Foundry is our software factory — what the team uses day in, day out to deliver projects. It accelerates the work and validates it: every change arrives with the checks it passed, the session that produced it, and a person accountable for it.
How Team Foundry worksWhether you are exploring an idea or improving a system already in use, we’ll help you decide what is worth doing next.
Questions before you book?
Read the FAQFollow along