Solution patterns

The engineering, without invented results

We would rather publish the engineering than the marketing. Each pattern below describes the situation it addresses, how we build it, what it supports and the parts that tend to go wrong.

Note

These are solution patterns, not client case studies. Each one describes a reusable engineering approach and the problem shape it addresses. No client is named and no result is claimed.

Note

Nothing here reports a measured outcome. The outcome sections describe what each pattern is designed to support, and what a team should expect to be able to do once it is in place.

Note

Named references, implementation detail and engagement history can be discussed directly on request, subject to client confidentiality.

Note

This page is structured so that verified case studies can be added alongside these patterns without changing the underlying format.

The patterns

6 shapes of problem we build repeatedly.

A pattern is general by definition. Your specifics are what turn it into an engineering decision.

Cross-sector

Enquiry-to-sale automation

The situation

Enquiries arrive faster than any team can respond to them, and they land in whatever channel the customer chose. The commercial data exists, but it lives in inboxes, spreadsheets and personal notebooks rather than in a system that can be relied on.

  • First response depends on who happens to see the message, and varies with workload
  • Quote requests sit in a spreadsheet with no owner and no follow-up date
  • Enquiry, conversation, quotation and invoice data are re-keyed between systems
  • Nobody can say how much pipeline is genuinely live and how much has quietly gone cold
  • Pricing and approval rules are applied inconsistently by different people

The approach

We start at capture, because the quality of everything downstream depends on what arrives and in what state. A single intake layer normalises enquiries from every channel into one structured record, then the workflow engine takes over: qualification, routing, follow-up scheduling and quotation drafting. Every consequential step routes to a person with the context they need and the authority to decide. Commercial terms are held as configuration rather than hard-coded logic, so pricing changes do not require a release.

  • Unified intake across web forms, email, phone transcription and partner referrals
  • Structured enquiry record with a defined state machine and explicit ownership at every state
  • Rule-based routing and SLA clocks that escalate rather than wait silently
  • Follow-up sequences as scheduled, logged tasks instead of personal reminders
  • Quotation generation from a maintained price book with versioned terms
  • Handover into invoicing and payment without re-keying the commercial detail

What it supports

This pattern is designed to support a sales function that responds consistently rather than heroically. It is built to make the pipeline visible, the next action obvious and the commercial record trustworthy.

  • Makes response time and pipeline state visible instead of anecdotal
  • Removes manual re-keying between the CRM, quoting and finance systems
  • Frees the team to work the opportunities that need a person rather than the queue
  • Keeps pricing and approval rules consistent across every deal
  • Leaves an auditable record of what was offered, by whom and when

Watchouts

  • Automating first-touch contact before intake quality is understood simply sends bad data through faster
  • Over-escalating degrades trust in the alerts; SLA clocks need thresholds agreed with the team that receives them
  • Quotation logic embedded in code turns every pricing change into a release
  • A pipeline with no defined states measures nothing, because there is nothing to count
  • Migration from spreadsheets fails quietly when historical data cannot be mapped to the new model
Event-driven workflow engine State machines with SLA timers Idempotent webhooks and inbound adapters Queued background processing Template and rule-based document generation Role-based access control Append-only activity and audit log Notification orchestration with delivery receipts
Cross-sector

Document processing

The situation

Most back offices run on documents that were designed for people to read, not for systems to interpret. Volume is high, the layouts change without notice, and a single misread field can hold up an entire process chain downstream.

  • Documents arrive as scans and photographs with inconsistent quality and orientation
  • The same logical field sits in a different place on every template
  • High-volume intake makes manual keying the single largest cost in the process
  • Errors are found late, often downstream, after work has already proceeded on them
  • Nobody can quantify how much of the backlog was processed correctly

The approach

We treat extraction as a measured problem rather than a demo. Each field is defined with its type, its acceptable values and the confidence below which a human must see it. Documents are classified first so that extraction is driven by a known layout family, then parsed, validated against arithmetic and business rules, and only then written to the system of record. Every extraction keeps a link back to its source region so a reviewer can verify a decision in seconds.

  • Layout classification before field extraction so each template has its own parser path
  • Field-level confidence with a defined threshold for human review
  • Source-region mapping so every extracted value remains traceable to the document
  • Arithmetic and business-rule validation applied before data reaches the ledger
  • Review queues ordered by confidence and by downstream value at stake
  • Evaluation set built from real documents and re-run on every model or template change

What it supports

This pattern is designed to support high-volume intake without accepting silent errors. Its purpose is to move the effort from reading documents to checking the ones that need judgement.

  • Removes routine re-keying from the intake path
  • Surfaces low-confidence cases for review instead of passing them through as fact
  • Makes every extracted value traceable to its source document
  • Provides a measurable baseline for extraction quality before further automation is added
  • Allows historical backlog processing under the same rules as new intake

Watchouts

  • Template drift is the usual failure mode; a supplier changes a form and accuracy falls without any error being raised
  • Confidence scores are not probabilities and cannot be compared across fields without calibration against real examples
  • Retraining on unreviewed output reinforces the model’s own mistakes
  • Bulk back-processing without sampling and reconciliation produces large volumes of unverifiable data
  • PII in source documents requires retention and deletion rules decided before ingestion, not afterwards
Document classification and routing OCR with layout and table awareness Schema-constrained field extraction Confidence scoring with human-review thresholds Reference-data matching and fuzzy resolution Human-in-the-loop review queues Versioned evaluation sets and regression checks Immutable source-document storage
Education

Education workflow automation

The situation

Education administration is a chain of deadlines, dependencies and small commitments made by large numbers of people, often with no single system of record. The work is highly repeatable, deeply seasonal, and unforgiving when it goes wrong at a deadline.

  • The same student, course or staff record exists in several systems with different identifiers
  • Registration, fee and attendance processes re-run differently every intake cycle
  • Deadlines are tracked in shared spreadsheets and calendars that no one fully trusts
  • Communication to students, parents and staff is repetitive, manual and hard to personalise at scale
  • Reporting for accreditation is assembled by hand at the point it is most urgently needed

The approach

The design problem is a shared identity spine: one student, one course, one enrolment, referenced consistently everywhere. Once that exists, each administrative process becomes an explicit workflow with defined inputs, deadlines, exceptions and audit trails. We build the exception path first, because seasonal processes live or die on how they handle the student who does not fit the normal sequence. Communication is treated as part of the workflow, not a separate marketing concern.

  • Canonical identity model for students, staff, courses and enrolments
  • Process workflows with explicit deadlines, dependencies and documented exception paths
  • Attendance and assessment records captured once and reused everywhere
  • Fee, invoice and payment handling integrated with institutional finance rather than duplicated
  • Rule-based communication triggered by process state with delivery and read tracking
  • Accreditation and management reporting generated from operational data, not re-keyed

What it supports

This pattern is designed to support staff who should be teaching and advising rather than reconciling spreadsheets. It is intended to give every process an owner, a deadline and a record.

  • Gives each process a defined state, owner and deadline rather than an informal understanding
  • Removes duplicate data entry between registration, finance and academic records
  • Lets routine administrative correspondence be triggered and tracked automatically
  • Makes exception handling a designed path instead of an escalation to a senior administrator
  • Allows reporting to be produced from operational data whenever it is needed

Watchouts

  • Academic calendars shift; logic hard-coded to a fixed term structure becomes wrong every year
  • Institutional change management determines adoption more than software quality, and needs its own plan
  • Consent, retention and guardian access rules differ by jurisdiction and must be settled first
  • Bulk student import is where data quality problems surface, and reconciliation takes longer than the import
  • Timetabling is a constraint-satisfaction problem, and automation cannot remove the underlying scarcity of staff time
Canonical student and course data model Scheduled workflow orchestration with academic calendar awareness Rules engine for eligibility, prerequisites and deadlines Self-service portal with role-scoped access for students, staff and parents Attendance and assessment capture with offline-tolerant mobile input Billing and payment integration with institutional ledgers Timetable and resource scheduling constraints Regulatory and accreditation reporting pipelines
Product companies

SaaS platform engineering

The situation

Multi-tenancy is usually treated as a schema decision and then becomes a source of incidents across the whole codebase. Product teams also accumulate bespoke variants per customer, so the platform becomes a growing set of conditionals that nobody can reason about.

  • Tenant identity is spread across the codebase instead of being enforced at one boundary
  • Background jobs and caches have no reliable notion of which tenant they belong to
  • Plan limits and entitlements are checked in the interface but not in the domain logic
  • Every customer-specific request produces another branch and another regression risk
  • Releases are infrequent because the pipeline is slow, which makes each release larger and riskier

The approach

We make tenancy a property of the request and of the data access layer, so a query cannot accidentally cross a customer boundary. Entitlements become a first-class domain concept evaluated server-side, which means the interface can never grant a capability the plan does not include. Feature differences are modelled as configuration and extension points rather than branching. Everything else follows from delivery discipline: fast pipeline, reversible releases, and per-tenant observability so a single customer problem can be diagnosed without affecting the rest.

  • Tenant context resolved once per request and enforced at the data-access boundary
  • Entitlements and plan limits evaluated in domain logic, never only in the interface
  • Configurable feature flags replacing per-customer code branches
  • Usage metering and billing events recorded immutably and reconciled against provider data
  • Tenant-scoped queues, caches, rate limits and observability dimensions
  • Trunk-based delivery with feature flags and automated rollback triggers

What it supports

This pattern is designed to support a product that can be sold to organisations of different sizes and shapes without the codebase fragmenting. It is built to keep the cost of adding a tenant low and the risk of leaking one low.

  • Keeps the cost of onboarding a new customer shape low and predictable
  • Makes isolation a property of the platform rather than a discipline every developer must maintain
  • Lets plan and pricing changes ship as configuration rather than as a release
  • Shortens the path from commit to production while keeping each release reversible
  • Makes a single tenant’s failure diagnosable without impacting the others

Watchouts

  • Retrofitting tenant scoping after launch means auditing every query, and the gaps are found in production
  • Schema-per-tenant approaches make migrations, backups and connection management dramatically harder
  • A metered usage pipeline that is not idempotent produces billing disputes that are expensive to unwind
  • Feature flags accumulate; without ownership and expiry dates they become a second, undocumented codebase
  • Premature multi-region design adds cost and complexity long before it is required
Row-level tenant isolation with enforced query scoping Entitlement and feature-flag service Usage metering with idempotent billing events Subscription and payment provider integration Tenant-aware queues, caching and rate limiting Infrastructure as code with per-environment pipelines Per-tenant metrics, logs and tracing Contract testing for public and partner APIs
Cross-sector

Legacy modernization

The situation

The systems that matter most are usually the ones nobody dares touch. They carry the real business rules, run on infrastructure nobody can rebuild, and are understood only by a shrinking group of people. Change has become too risky, so improvement has effectively stopped.

  • Business logic is interleaved with storage, so neither can be replaced alone
  • Original knowledge is distributed across people, and documentation does not exist
  • A small change to shared code has an unpredictable blast radius across customers
  • Data volume and history make a single migration event the highest-risk step in the plan
  • There is no interface, so every integration is bespoke and every integration is fragile

The approach

Before anything moves, we build an accurate functional picture with the people who operate the system, and wrap the riskiest paths in characterisation tests so behaviour is pinned down before it changes. A facade goes in front, and new capability is built behind it while the legacy path keeps serving traffic. Traffic shifts by measured segments, with reversal at every step. Data moves incrementally with reconciliation at each stage, and the old system is only decommissioned once nothing reads from it.

  • Functional and dependency map built with the people who operate the system
  • Characterisation tests around critical paths before any refactoring
  • Facade or gateway in front of the legacy platform, versioned from the start
  • One capability extracted at a time with traffic shifted gradually and reversibly
  • Incremental data migration with reconciliation checks at every stage
  • Explicit decommissioning plan, owned by someone with authority to sign it off

What it supports

This pattern is designed to support a business that keeps operating throughout. Its purpose is to reduce the cost and risk of change without requiring the old system to be switched off on a particular date.

  • Allows change to resume in a system that had become too risky to modify
  • Removes the single irreversible cutover as the only path to progress
  • Produces a versioned interface that makes the system replaceable in future
  • Keeps legacy scope shrinking measurably, phase by phase
  • Leaves the replacement on ordinary, understood infrastructure rather than a bespoke foundation

Watchouts

  • Rewrites are almost always the wrong instrument; the value is in the facade and the extraction sequence
  • Data migration is the part most likely to be underestimated, particularly history and reconciliation
  • Running the old and new paths in parallel has real operating cost and needs funding agreed up front
  • Stalling before decommissioning leaves two systems running indefinitely, which is its own kind of failure
  • If the operational knowledge is not captured while those people are still present, it is usually lost
Anti-corruption adapters over legacy interfaces Strangler facade with versioned contracts CDC or incremental batch replication with reconciliation Dual-run and shadow comparison against live behaviour Consumer-driven contract tests for each extracted capability Parallel run infrastructure and traffic splitting Infrastructure as code for the replacement platform Structured logging introduced alongside the legacy path
Cross-sector

API integration

The situation

Operational friction is rarely caused by a single system. It is caused by the gaps between them, where data is copied by hand or moved by scripts that nobody owns. Each integration tends to be built once, for one purpose, and then left to run unattended.

  • Systems disagree about identifiers, status values, currencies and time zones
  • Point-to-point connections mean a change to one interface breaks several flows at once
  • A failed sync is often not detected, so the two systems disagree silently
  • API credentials end up in configuration files, chat messages and shared documents
  • There is no reliable way to prove the position in one system matches another

The approach

All external contact goes through one layer, so mapping, credentials, retries and audit live in a single place rather than being spread across application code. The canonical internal model is defined first, and every partner is adapted into it explicitly, which is where the real translation work actually lives. Failure is treated as the normal case rather than the exception: inbound events are idempotent, outbound calls are retried with backoff and circuit breaking, and every synchronisation can be reconciled and replayed. Consumers are authenticated with scoped, revocable credentials and every call is attributable.

  • Single integration layer with provider abstraction so vendors can be replaced
  • Canonical internal model with explicit, versioned mappings per partner
  • Idempotent inbound handlers keyed on a provider event identifier
  • Timeouts, exponential backoff, circuit breaking and dead-letter handling
  • Scheduled reconciliation reports that prove two systems agree
  • Scoped, revocable credentials with secrets held outside source and never logged

What it supports

This pattern is designed to support connected systems that can be trusted to agree with each other, and to make disagreement visible before it becomes a business problem.

  • Removes point-to-point coupling, so one interface change stops being a multi-team event
  • Makes a stalled or failed synchronisation visible rather than silent
  • Allows every integration to be replayed or reconciled on demand
  • Gives each partner the least access it needs, revocable without a redeployment
  • Provides an audit trail from an inbound event to the state change it caused

Watchouts

  • Vendor sandboxes rarely behave like production, so rate limits and failure modes surface late unless tested deliberately
  • Retries without idempotency guarantee duplicate writes, and duplicates are far harder to remove than to prevent
  • Retaining every partner quirk inside the mapping layer eventually makes that layer the hardest thing to change
  • Pagination and incremental sync semantics differ per provider and are the usual cause of incomplete syncs
  • Integration code that is never monitored becomes an unknown dependency nobody is accountable for
Canonical domain model with anti-corruption adapters Signed webhooks with replay protection Idempotency keys on inbound and outbound writes Queues with dead-letter handling and replay tooling Retry policies with exponential backoff and circuit breakers OAuth 2.0 / OIDC and scoped service credentials Contract tests and consumer-driven compatibility checks Continuous reconciliation between connected systems
Go deeper

The capability behind each pattern.

Every pattern above is delivered through one of our capabilities, usually two. These pages describe how that work is scoped and sequenced.

Capabilities

Delivery capabilities

Scoped on their own, combined in practice.

Where these show up

Sector versions

The same pattern under a different operating model.

  • Education Where education workflow automation belongs.
  • Healthcare Consent and audit change the sequencing.
  • Automobile Networks, bookings and parts demand.
  • B2B Enquiry to sale at multi-tenant scale.
Our own build

Folow

The enquiry to sale pattern as a product we operate.

Recognise your situation in one of these?

Patterns are general. The engineering decisions are not, and they are worth discussing against your actual constraints.

Start a conversation Explore capabilities