Services
Services
We can work across the whole software development process, from end to end or at exactly the point where you need us. Each one below is set out with what it covers and what you get.
Analysis
Business analysis
We establish what the project is for, who it serves and what is currently going wrong. Before deciding what to build, we settle what is actually needed.
Often the thing a team most needs is not more code. It is an outside reader who will look at the system and say plainly what is wrong with it, in writing, with the reasoning attached.
This is frequently the right first engagement. It is small, time-boxed, and it leaves you with something useful whether or not you work with us afterwards.
How we read a system
We work from evidence, not impressions: production logs, APM traces, the error profile, the deployment history, the tests that exist and the ones that do not. Then the code, read against what the business actually needs it to do.
Findings come back ordered by blast radius and tractability — because fixing a hard problem with narrow impact before an easy one with wide impact is how remediation programmes lose their mandate. On an enterprise commerce platform averaging around 1,000 error log entries a day, working through the classes in deliberate order took that figure down to double digits.
The deliverable is a decision, not a document
The report ends with a sequenced plan: what to do first, what that buys you, what it costs, and what can safely wait. You should be able to hand it to your CTO and your CFO and have both understand it.
Architecture
Architecture decisions
We identify the viable options for the project, research them and run the preliminary tests. The decision rests on measurements rather than assumptions.
Most backends do not fail because a framework was chosen badly. They fail because nobody wrote down where one responsibility ends and the next begins, so every feature quietly widens the blast radius of the last one.
We start from the boundaries. What each service or module owns, what it is not allowed to know, and how it talks to everything else — then the API surface that follows from those decisions.
Isolation as an enforced contract
On our own platform work we hold service isolation as a rule the code cannot break rather than a convention a reviewer has to remember: services share no database, no cache instance and no runtime state, and never call each other directly. All inter-service traffic routes through a gateway that injects request context downstream services validate before doing anything.
That strictness is not free, and it is not always right. We will tell you when a modular monolith is the better answer for where you actually are.
Sequence is most of the job
When an existing system has to be broken up, the order matters more than the target diagram. On a lead-generation SaaS platform one workload dominated everything: the live-preview editor regenerated a JavaScript bundle on every keystroke, and with 10–15 concurrent users the whole platform degraded to multi-minute waits.
We isolated that workload first. That single step contained the dominant load in one place instead of letting it degrade every other capability — and it made the rest of the decomposition safe to do at a normal pace, alongside the product roadmap.
Development
Software development
Readable, maintainable code that follows the architecture we agreed on, written to carry the next few years of product change rather than just this release.
Code is where an architecture either survives or quietly stops being true. We write it to the boundaries that were agreed, in the idiom of the codebase it joins rather than the idiom we happen to prefer.
Rules engines, not configuration screens
Business logic grows past what any platform’s built-in features can express. A real promotional campaign reads: take the discount on product A; if the customer previously bought product X, stack a second one; but if their lifetime spend crosses a threshold, drop both and apply a third instead.
Written straight into application code, that becomes a recomputation on the product page, again in the cart, and again before payment — and with three to five conflicting campaigns live, the request simply times out. We replace it with a managed layer: an explicit priority list that resolves conflicts deterministically, active/inactive state so campaigns toggle without a deploy, and caching so the calculation happens once.
On one enterprise platform that single piece of work resolved the majority of the cart race conditions, the payment amount discrepancies and the timeouts.
Built to be handed over
Architecture that only one person can operate is a liability dressed as an asset. Everything we build ships with the quality gates that keep it honest, and with the business logic deliberately separated from the presentation layer — on one commerce platform, as ten decoupled plugins each owning a single responsibility, so core functionality stayed stable across theme and platform updates.
Integration
Integration
We connect your system to ERPs, CRMs, payment providers and third-party services, with a boundary in between so a change to someone else’s interface does not break your application.
Integrations are where systems you do not control reach into the one you do. The work is less about making the call succeed than about deciding what happens when it does not.
A boundary, so both sides can change
A corporate platform that exposes SOAP only, with no REST API, makes direct integration from the web tier brittle and couples your release cycle to someone else’s schema. We put a dedicated middleware service in between: consuming the internal platform over SOAP, exposing a clean REST API to the application. Both sides then evolve independently.
Master data and financial operations — users, offices, payments, refunds, cancellations — route through the system of record rather than being duplicated. The application stays free of data it has no business owning.
Keep the slow parts off the request path
Purchases, cancellations and refunds are processed asynchronously over a message queue, which also triggers the notification pipeline. A long corporate call stops being something the user waits for.
Correctness where money moves
A payment provider already integrated is not the same as a payment path that is correct. On one platform, overlapping discount rules miscalculated under race conditions and incorrect precedence ordering, so the amount charged could diverge from the amount owed. We diagnosed and fixed the underlying calculation defects — a defect class with direct financial and customer-trust consequences.
Testing
Testing
Unit, integration, end-to-end, smoke and security tests, set up as blocking gates in CI. Untested code is code that is only assumed to work.
A test suite is not a quality badge. It is the thing that lets the next person change the code without being afraid of it.
Gates, not conventions
Linting, static analysis and tests are worth far more as blocking CI gates than as a team agreement. We have introduced them to codebases that had neither, and on our own platform work they exist from the first commit — which is the only time putting them in is cheap.
Working inside a constraint
On one enterprise platform the client had ruled out automated testing, on the grounds that a manual QA engineer exercised the system as a real user. Rather than argue and deliver nothing, we put in the strongest gate achievable within that constraint: a linter on a codebase that had neither tests nor static analysis, with several developers newer to the language.
That is not the ideal answer. It was the one that could actually be adopted, and it caught a class of defect that had been reaching production.
Delivery
CI/CD
We turn releasing from a manual task into an automated, repeatable process. Every deployment passes a health check and the rollback path is defined in advance.
Most delivery problems are not tooling problems. They are the absence of an agreed answer to three questions: how does a change reach production, how do we know it worked, and what happens when it did not.
Environments that tell the truth
Development optimised for a fast local loop; staging isolating every service the way production does, all health-checked. A single container image so a new developer can run the full stack without installing anything on their machine.
Release as an auditable event
We have built pipelines where every change deploys to staging and emails stakeholders a summary of what changed; on approval, the same pipeline promotes it to production. That gave an enterprise client a documented review-and-approval trail without a release manager in the loop.
Deployments are triggered explicitly by tag or marker rather than firing on every push, with full snapshots taken before and after each release.
That combination is why, across an 18-month engagement under a 100% uptime guarantee, a production rollback was never needed.
Operations
Maintenance and monitoring
We watch production through centralised logging, dashboards and alerts, so a problem is seen and acted on before your customers report it.
An unstable system is rarely unstable for one reason. It is unstable for eight reasons at once, which is why opportunistic bug-fixing never seems to move the number.
The order we work in
- Baseline defects — the straightforward failures, eliminated wherever the originating code is still live. Cheap, and they clear the noise hiding everything else.
- Race conditions — concurrency bugs producing wrong totals and records that fail to materialise downstream.
- Timeouts — usually a calculation that compounds, not a slow query.
- Third-party resilience — hardening the payment, search and checkout paths against upstream services that stop responding.
- Platform-generated load — the spikes your own cache warm-up and server-side rendering create.
On an enterprise commerce platform running at roughly 1,000 error log entries a day — with only a rolling six-month log window to diagnose from — this sequence brought it to double digits.
Caching is a layered decision
A full-page cache in front of an application that makes ten uncached API calls per request has not solved the problem, it has hidden it. We build caching in stages: user-independent upstream responses first, then per-customer data under customer-scoped keys, then the composed output itself. Request paths that previously issued ten calls to external services have come back down to roughly three.
Observability first
None of the above is possible without being able to see production. Where there is no centralised logging, log taxonomy, dashboard or alert, that is where we start — because a system you cannot observe is a system you can only guess about.
Tooling
AI-assisted development
Agent and skill tooling that knows your codebase’s conventions, plus MCP servers giving assistants governed access to your systems. We are equally explicit about where the leverage stops.
We use these tools in daily production work, which is the only reason we offer them as a service. The leverage is real and it is also narrower than the market claims — we would rather tell you where the line is than sell past it.
Tooling that knows your codebase
A general-purpose assistant writes general-purpose code. The value appears when the tooling encodes your conventions: your framework idioms, your naming, your test structure, your review standards.
We maintain a public library of 21 domain-specific skills across PHP, Laravel, PHPUnit, microservices, Go, Kotlin, SwiftUI, Vue, Docker and SQL, plus 18 specialised agent definitions spanning four different AI coding tools. The same approach applies to a single codebase.
MCP servers
The Model Context Protocol is how an AI assistant gets real access to a system instead of guessing about it. We have published mysql-mcp, an MCP server for MySQL, and build them for internal systems — with access boundaries defined deliberately, because a tool that can read anything is a tool that will.
Design-to-code
We authored figma2compose and figma2swiftui, converting Figma designs directly into Jetpack Compose and SwiftUI implementations. Where a design system is consistent, this removes a genuinely repetitive translation step from mobile delivery.
Training
Training
We train your team to run and extend what we hand over. On site or remote, pitched to the subject and to the level of the people in the room.
A system nobody else can run is not finished, however good it is. Training is part of delivery rather than an upsell after it.
For the people who will maintain it
The most useful form this takes is not a slide deck. It is reference implementations in production code — the target architecture written out in your codebase, as a concrete standard to follow — plus code review and pair programming while we are still there to answer questions.
We have led backend code review and mentored developers through exactly this on a monolith decomposition, and the team was left with clear service boundaries, documented architectural direction and working examples rather than a document.
For the people who will use it
Not every handover is to engineers. On a B2B platform we delivered end-user training for the client’s sales and back-office teams, then supported them long after go-live, because the system only paid off once the people in the branches trusted it.
Consultancy
Consultancy
Across the whole software process, or only at the point where you need it. Sometimes the most useful answer is that nothing should be built.
Not every engagement should end in us writing code. Some of the most valuable work we have done was a recommendation the client then carried out themselves.
Saying the expensive thing early
On one commerce platform, catalogue search was a top-line problem: the fastest queries took ten seconds and some timed out entirely. The answer was not to optimise the existing search. We proposed moving it to a dedicated search engine, on the observation that the pipeline already feeding the commerce backend could, with minor modification, feed the index too — removing the load from the backend, cutting latency and unlocking similar-product recommendations as a side effect.
On the same platform, the costliest defect class was structural rather than a bug: orders that silently never entered production, or were manufactured against a physically impossible specification. We proposed the replacement flow and the deferred confirmation model; the team implemented it.
Being wrong about us, out loud
If a product you can buy solves your problem, we will say so. If a review is the right engagement rather than a build, we will propose the review, which is the smaller piece of work. We would rather be the people who told you that than the people who billed you for the alternative.