A typed superset of JavaScript that compiles to plain JavaScript.
Why we reach for itOur default language. Types catch a large share of integration errors before code runs, and they double as documentation for whoever inherits the project.
An inventory, not a logo wall. Every entry says what the thing is, why it is in our stack, and how routinely we use it — including the ones we are still evaluating and have not put on client delivery.
The level is about our own experience with a technology, not about the technology's quality. It is the single most useful thing we can tell you about an entry, so it appears on every one.
Nothing on this page implies a partnership, a certification, a reseller arrangement or any endorsement by a vendor. We hold none, and we would rather say so plainly than let a row of logos suggest otherwise. Every name here belongs to its own owner and is used only to describe what we build with.
The languages we write production code in. We default to one strongly typed language across the stack because it removes a whole class of integration mistake, and we reach for others where the problem genuinely calls for them.
A typed superset of JavaScript that compiles to plain JavaScript.
Why we reach for itOur default language. Types catch a large share of integration errors before code runs, and they double as documentation for whoever inherits the project.
A JavaScript runtime built on V8, used for server-side applications.
Why we reach for itThe server runtime behind most of what we build. Sharing a language between browser and server means validation rules and domain types are written once.
A general-purpose language with strong data and scientific tooling.
Why we reach for itUsed for data processing, scheduled jobs and anything touching machine learning, where its libraries are simply better than the alternatives.
A compiled language designed for concurrent network services.
Why we reach for itChosen for services where predictable memory use and a single static binary matter more than ecosystem breadth — high-throughput APIs and infrastructure tooling.
A widely deployed server-side language, dominant in existing web estates.
Why we reach for itWe maintain and extend PHP systems we did not write. A large amount of working software runs on it, and replacing something functional is rarely the responsible recommendation.
Microsoft's cross-platform application framework, primarily used with C#.
Why we reach for itUsed where an organisation already runs on the Microsoft stack and integration with existing internal systems is the deciding factor.
What the browser actually receives. Our bias is to send less JavaScript, render on the server where we can, and add interactivity only where it earns its download cost.
A library for building user interfaces from composable components.
Why we reach for itThe component model we build interfaces with. Its size means the hiring pool is deep — you are not locked to us by an obscure framework choice.
A React framework with server rendering, routing and build tooling.
Why we reach for itOur default application framework. Server rendering gives fast, indexable first loads; the same codebase handles the marketing site and the authenticated application behind it.
A utility-first CSS framework driven by a design token configuration.
Why we reach for itUsed with a strict token layer so spacing, colour and type stay consistent. We pair it with hand-written component CSS for the parts of a design system that deserve names.
A site framework that ships zero JavaScript by default.
Why we reach for itOur choice for content-heavy sites with little interactivity, where the fastest possible page is worth more than an application framework.
Native where the platform matters, shared where it does not. The decision belongs at the start of a project, because reversing it later is expensive.
Apple's language for iOS, iPadOS and macOS development.
Why we reach for itUsed when an iOS application needs deep platform integration — background processing, hardware access, or interface behaviour that has to feel native rather than approximate it.
The primary language for modern Android development.
Why we reach for itThe Android counterpart to Swift, for the same reasons: platform depth, background work and predictable behaviour across a very wide device range.
A framework for building native mobile apps with React.
Why we reach for itOur usual answer when one product must ship on both platforms and the interface is mostly forms, lists and data. Shares language and often logic with the web application.
A cross-platform UI toolkit from Google using the Dart language.
Why we reach for itConsidered where a highly custom interface must look identical on both platforms and the team is not otherwise invested in the JavaScript ecosystem.
The part of a system that outlives every rewrite. We normalise properly, constrain at the database rather than only in application code, and index for the volume you will have in three years.
An open-source relational database with strong consistency guarantees.
Why we reach for itOur default database. Real constraints, real transactions, JSON when you need it, full-text search when you do not want to run a second system, and no licence cost.
A widely deployed open-source relational database.
Why we reach for itUsed where an existing estate already runs on it. We would not usually choose it for a new build over PostgreSQL, but migrating a working database is rarely the highest-value work available.
An in-memory data store used for caching, queues and rate limiting.
Why we reach for itAdded when a measurement shows it is needed — caching hot queries, backing job queues, or coordinating rate limits across multiple application instances.
A type-safe database toolkit and migration system for TypeScript.
Why we reach for itGives us a single typed schema, generated client and versioned migrations. Queries are parameterised by construction, which removes SQL injection as a class of defect.
A distributed search and analytics engine.
Why we reach for itIntroduced when search is a primary feature rather than a convenience — faceted catalogue search, log analysis, or relevance tuning that a database cannot express well.
We are not tied to one provider. The right choice depends on where your data must live, what your team can operate, and what the bill looks like at your actual scale rather than at a conference demo scale.
A broad cloud platform covering compute, storage, networking and managed services.
Why we reach for itThe most complete option, and the usual choice when a project needs a specific managed service or a particular data residency guarantee.
Microsoft's cloud platform, closely integrated with its enterprise products.
Why we reach for itChosen where an organisation already runs Microsoft identity and productivity services and wants one directory, one bill and one support relationship.
Google's cloud platform, with strong data and analytics services.
Why we reach for itUsed where the workload is data-heavy and its analytics products are a genuine advantage rather than a preference.
A global network providing CDN, DNS, DDoS protection and edge compute.
Why we reach for itSits in front of most of what we deploy: caching close to users, TLS termination, and absorbing traffic that should never reach an origin server.
A managed hosting platform for frontend and full-stack applications.
Why we reach for itA pragmatic default for smaller projects: preview deployments per branch and no infrastructure for your team to operate. We move to self-managed infrastructure when cost or control makes that the better answer.
Deployment should be boring, reversible and repeatable. If a release depends on someone remembering the order of the steps, that is a defect in the process, not a fact of life.
Containerisation for packaging applications with their dependencies.
Why we reach for itGives us the same runtime on a developer laptop, in CI and in production. Removes the entire category of problem that begins "it works on my machine".
An orchestration system for containerised workloads.
Why we reach for itUsed where scale or multi-service topology genuinely warrants it. For most organisations asking for it, a managed platform is cheaper to run and easier to hire for — we will say so.
Infrastructure defined as version-controlled code.
Why we reach for itInfrastructure lives in the repository alongside the application, so environments can be rebuilt from source and changes are reviewed like any other change.
Continuous integration and deployment pipelines defined in the repository.
Why we reach for itRuns tests, type checks, security scans and deployments on every change. Nothing reaches production without passing the same pipeline.
Dashboards and alerting over metrics and logs.
Why we reach for itWhere operational visibility lives. Dashboards are handed over to you at the end of an engagement, not kept on our account.
Application error tracking and performance monitoring.
Why we reach for itTells us an exception happened before a user reports it, with the stack trace and the release that introduced it. Configured to scrub personal data before anything leaves your systems.
Systems your own team operates day to day. The measure of a good choice here is whether a marketing or commercial colleague can do their job without raising a ticket.
A TypeScript-native headless content management system.
Why we reach for itUsed when a project needs a content model defined in code and reviewed like code, with an admin interface generated from it. Self-hosted, so content stays in your database.
A payment platform for one-off and recurring payments.
Why we reach for itIntegrated through its hosted flows so card details never reach our servers. Sits behind our own gateway layer, so switching provider later is a configuration change.
Applied where it measurably beats a simpler approach. Most problems described to us as AI problems are search, classification or data quality problems, and are better solved as those.
Hosted large language and embedding models accessed over an API.
Why we reach for itUsed for text classification, extraction and summarisation inside a product. We design the surrounding system so a wrong answer is recoverable and never silently authoritative.
A family of large language models available over an API.
Why we reach for itUsed for the same class of work as above, particularly where long documents or careful instruction-following matter. Provider choice is kept behind an interface so it can change.
A PostgreSQL extension for storing and searching vector embeddings.
Why we reach for itLets semantic search live in the database you already run and back up, rather than adding a separate vector store and a second thing to operate.
Automated where it pays for itself, manual where judgement is required. The purpose of a test suite is to let you change the system confidently, not to reach a coverage number.
Browser automation for end-to-end testing across engines.
Why we reach for itCovers the handful of journeys that must never break — sign in, checkout, submit. Runs in CI on every change, across Chromium, Firefox and WebKit.
A fast unit and integration test runner for TypeScript projects.
Why we reach for itCovers business logic and boundaries. Fast enough to run on save, which is the only reason developers actually run tests.
An automated accessibility testing engine.
Why we reach for itCatches the mechanical accessibility failures in CI. It finds roughly a third of real issues, which is why we also test with a keyboard and a screen reader.
Tell us what it is built on and what it is doing to you. We will say plainly whether the answer is a rewrite, a migration, or leaving it alone and fixing the two things that actually hurt.
We read every enquiry ourselves. If we are not the right fit, we will say so and tell you what to look for instead.