Custom software & platforms
Build the version a new engineer can still change in year five
We build custom products end to end — domain model, API contracts, multi-tenancy, billing, release path — with the seams and tests that keep them changeable. The measure is not the demo in month two; it is whether someone who never met us can safely ship a change in year five.
Engagement at a glance
- Capabilities
- 05
- Deliverables
- 08 artefacts
- Stack groups
- 04
- Ways to start
- 03
Engagement options
- Architecture & scoping sprint2–3 weeks
- Product build3–6 months to first production release
- Legacy modernisation6–12 months, in slices
What breaks
The failure modes we keep meeting
Cheap to design around at the start. Expensive to discover once the system is carrying real traffic.
There are no seams, so every change is a rewrite
A codebase rots when nothing separates the domain from the delivery mechanism. Business rules live inside controllers, ORM models are passed to the template layer, and a payment provider’s SDK types leak into thirty files. The only tests are browser tests, so a two-line change runs a nine-minute suite and still misses the regression. Swapping anything means touching everything.
Tenant isolation is one forgotten WHERE clause away
Most multi-tenant leaks are not clever attacks. They are a new endpoint, written on a Friday, where the tenant filter was never applied — or a background job that iterates a queue without re-scoping the connection. When isolation depends on every developer remembering, the failure is scheduled rather than possible. It surfaces as one customer seeing another’s invoice, and there is no recovering that conversation.
The schema is frozen and the deploy is a ritual
A table grows to ninety columns, everything nullable because a migration once locked production for forty minutes. Nobody will touch it again. Releases become a Saturday ceremony with a runbook in a shared document, a manual database step, and no rollback that anyone has tested. From there, change slows not because the team is weak but because the cost of being wrong has no floor.
Capabilities
What the work actually consists of
01
Discovery, domain modelling and scoping
Before writing code we find the nouns the business actually argues about, and the invariants that must never be violated. The model that comes out of this decides how expensive every later change will be.
- Event storming with operators and support staff, not only the sponsor, to surface the workflows that exist rather than the ones on the org chart
- Bounded contexts and an explicit context map — which team owns which concept, and what the translation is at each boundary
- Invariants written down as executable assertions: what must always be true about an order, a policy, a tenant
- A thin walking skeleton through every layer in week one, deployed to a real environment, to prove the seams before the volume of code arrives
- A scope ledger that separates the first production slice from what can honestly wait
02
API design, versioning and contract tests
An API is a promise with a long expiry date. We design the surface deliberately, generate clients from it, and make breaking it a build failure rather than an incident.
- REST with OpenAPI 3.1, GraphQL where the client genuinely needs shape control, or gRPC between internal services — chosen per boundary, not per fashion
- Additive-only evolution with explicit deprecation windows and sunset headers; versioned URLs only where a genuine breaking change is unavoidable
- Consumer-driven contract tests in CI so a provider cannot merge a change that breaks a known consumer
- Idempotency keys, cursor pagination, RFC 9457 problem details and consistent error taxonomies designed once, not per endpoint
- Generated typed clients and mock servers so front-end and mobile work can start before the backend is finished
03
Backend services, multi-tenancy and access control
Tenancy and authorisation are structural decisions. We push them below the application code so that forgetting them is not possible, then prove the property with tests that try to break it.
- Tenancy model chosen against the real constraint — shared schema with row-level security, schema-per-tenant, or a dedicated database where a regulator or a large customer requires it
- PostgreSQL row-level security bound to a session variable set by the connection layer, so an un-scoped query returns nothing instead of returning everything
- Authentication via OIDC with a managed identity provider; authorisation as policy — relationship-based for hierarchies, RBAC where roles really are flat
- A negative test suite whose whole purpose is cross-tenant access attempts, run on every commit
- Per-tenant rate limits, quotas and noisy-neighbour controls so one customer’s bulk import cannot degrade everyone else
04
Background work, payments and the boring critical paths
The work that runs out of band is where money is lost quietly. Jobs, retries and billing get durable execution and exactly the paranoia they deserve.
- Durable workflows for anything multi-step — onboarding, refunds, fulfilment — with retries, timeouts and compensation modelled explicitly rather than hidden in a cron script
- Transactional outbox and consumer idempotency so a retried message does not charge a card twice
- Subscription and usage billing on Stripe or Razorpay, with proration, dunning, tax handling and a reconciliation job that compares the provider’s ledger to ours daily
- Webhook ingestion with signature verification, replay tolerance and a dead-letter path a human can actually inspect
- Scheduled work that is observable: every job emits start, outcome and duration, and a stuck queue pages someone
05
Testing strategy, legacy migration and the release path
A system is only as changeable as its safety net and its deploy story. We build both from the first week, and we move legacy systems across in slices rather than in one hopeful cutover.
- Fast unit and domain tests below the HTTP boundary, contract tests at every integration, and a deliberately small end-to-end suite covering the paths that lose money
- Ephemeral preview environments per pull request, seeded with realistic anonymised data
- Expand-and-contract migrations with online index builds, so schema change is routine and never requires a maintenance window
- Strangler-fig migration from the legacy system: a routing facade, one capability moved at a time, dual-write and reconciliation while both run
- Trunk-based delivery with feature flags, progressive rollout and a rollback that has been executed in a drill, not merely documented
Outcomes
Targets, and where each number comes from
The basis line under every figure is the point of this section. Where a number is a design standard rather than a measured delivery, it says so — we would rather be checkable than impressive.
- Lead time from merge to production, with the pipeline as the only route
- Under 1 hourLead time from merge to production, with the pipeline as the only routeDesign target. The elite band in the DORA State of DevOps research is under one hour; we architect the release path to sit inside it from the first release.
- Hand-written tenant filters relied on for isolation
- ZeroHand-written tenant filters relied on for isolationDesign target — isolation enforced by database row-level security and a scoped connection, then verified by a cross-tenant negative test suite, rather than by review discipline.
- Of test coverage carried below the HTTP boundary, running in minutes
- 70–85%Of test coverage carried below the HTTP boundary, running in minutesTypical split for this class of system once contract tests replace broad end-to-end coverage; the exact ratio depends on how much of the domain is genuinely stateful.
- To the first production slice behind a feature flag
- Weeks, not quartersTo the first production slice behind a feature flagTypical range for a scoped greenfield service with a decided domain model. A strangler migration from a live legacy system starts slower and should be planned as such.
Stack
What we reach for, and when
Defaults, not dogma. The list below is what we would propose on a blank page; an existing estate, a procurement constraint or a team's operating experience all legitimately move it.
- TypeScript on Node.js 24 LTS
- Go 1.25
- Python 3.13 with FastAPI
- Java 25 LTS / Spring Boot where the estate is already JVM
- Next.js and React Server Components for product surfaces
- OpenAPI 3.1 with generated clients
- GraphQL via Apollo Federation
- gRPC and Protobuf managed with Buf
- Pact for consumer-driven contract tests
- Zod and TypeSpec as the single source of schema truth
- PostgreSQL 17 with row-level security
- Drizzle ORM, Prisma or sqlc depending on runtime
- Atlas and Flyway for expand-and-contract migrations
- Redis 8 for caching, locks and rate limits
- Debezium change data capture into the analytics path
- Temporal for durable multi-step workflows
- River, BullMQ or Kafka/Redpanda for queues and events
- Stripe Billing and Razorpay, with daily ledger reconciliation
- GitHub Actions, Terraform and OpenFeature-compatible flags
- OpenTelemetry traces, metrics and logs wired in from day one
Deliverables
What you keep
Everything below lands in your repositories and your accounts, under your licence, with the reasoning written down. There is no runtime you have to keep renting from us.
08 artefacts, handed over
- A domain model and context map: the entities, their invariants, and the boundaries between owning teams — the document a new engineer reads first
- A running walking skeleton deployed to a real environment in the first fortnight, with authentication, one real use case and a green pipeline
- The production service or product itself, in your repository and your cloud account, with no runtime dependency on Digiaeon
- An API specification with generated clients, a mock server and contract tests wired into CI
- A tenancy and authorisation design note stating the isolation model, its enforcement point, and the negative tests that prove it
- A migration and rollback playbook: expand-and-contract procedure, tested rollback, and the strangler sequence if a legacy system is in scope
- A test suite with its pyramid documented — what is covered where, and what is deliberately not covered
- An operations pack: runbooks, dashboards, alert definitions with owners, and a recorded handover walkthrough for your team
Engagements
Three honest ways to start
Sized so the first one can end. Each option is designed to produce something usable even if we never work together again.
Architecture & scoping sprint
2–3 weeks
A build about to start, or a system already in production that has become expensive to change and needs an honest read before more money goes in.
Includes
- Event storming and domain modelling sessions with your operators
- Tenancy, identity and data-residency decisions recorded as ADRs with tradeoffs stated
- A walking skeleton through every layer, deployed and running
- A costed delivery plan with the first production slice named
Product build
3–6 months to first production release
A greenfield product, or a significant new capability inside an existing estate, where the system is expected to be operated for years.
Includes
- A small embedded team: engineering lead, two to four engineers, design where the surface warrants it
- Two-week cycles with a working deployment at the end of each
- API contracts, multi-tenancy, billing and background workflows built as first-class concerns
- Delivery pipeline, observability and runbooks shipped with the software, not after it
Legacy modernisation
6–12 months, in slices
A revenue-critical system that cannot be switched off, where a big-bang rewrite has already been attempted or wisely rejected.
Includes
- Dependency and data-flow mapping of the existing system, including the undocumented integrations
- A routing facade and a capability-by-capability strangler sequence, ordered by risk and business value
- Dual-write with continuous reconciliation, and a documented abort path at every step
- Knowledge transfer to your team throughout, with the goal of us becoming unnecessary
Questions
The awkward ones
The questions that decide whether this is worth starting — answered the way we would answer them on a call.
Usually you should. Custom software earns its cost only where the process is your actual differentiator, or where the fit is so poor that the configuration layer becomes a codebase nobody can test. We will say so in the scoping sprint if a SaaS product plus a thin integration is the honest answer — that conversation is cheaper for both of us than a build that should not have happened.
Adjacent work
These are the practices this one leans on and the reference architectures that show it assembled.
Related solutions
Reference architectures
Next step
Bring us the problem you keep deferring.
A 45-minute working session on your Product Engineering work. We will tell you what we would build, what we would not build, and roughly what it costs. No deck.
