The tools we actually
use - and the reasoning
behind every choice.
This isn't a marketing list of every tool we've ever touched. It's an honest account of what we build with, what we've deprecated, what we're evaluating, and - crucially - what we've deliberately decided not to use and why.
We have opinions about tooling - and we're willing to defend them. Here's the reasoning that shapes every decision on this page.
A tool that's been in production for eight years has a known failure mode. A tool that launched last quarter does not. We default to the battle-tested option unless there's a specific, articulable reason to do otherwise.
Self-hosted is fine if we can staff it. Managed is fine if the vendor is credible. The mistake is self-hosting something you can't support - so the choice depends on what the team can actually run, not what looks best on paper.
Vendor lock-in is a real cost. It shows up in migrations, in pricing leverage, and in hiring - teams that know Kubernetes are everywhere; teams that know a proprietary orchestration system are not. We prefer open standards and document when we deviate.
We evaluate tools partly on DX - not as a luxury, but because slow local builds, confusing error messages, and complex setup are time taxes that compound across a team. A two-minute deploy pipeline matters.
Nine categories. Every choice
explained, not just listed.
For each tool, you'll see why we use it, what we'd use instead in specific circumstances, and - where relevant - what convinced us to change from a previous choice.
The runtime and framework decisions that affect every API, service, and data pipeline we build. Language choices here cascade into hiring, tooling, and long-term maintainability - so we don't change them lightly.
Our primary runtime for API services, BFF layers, and event-driven microservices. TypeScript everywhere - the type safety investment pays back quickly on projects with multiple contributors.
Mandatory for anything ML/data - FastAPI for inference endpoints, Django for data-heavy admin platforms, and script-heavy data engineering pipelines.
For high-throughput services, CLI tooling, and infrastructure components where static binary deployment matters. Used selectively - where the performance profile justifies the context switch.
Smart contract development (Solana programs via Anchor), WASM components, and performance-critical systems code where memory safety matters as much as throughput.
For rapid-delivery web applications, internal tools, and admin platforms where time-to-ship matters more than raw performance. Rails' convention-over-configuration still wins for certain project profiles.
Currently evaluating for high-concurrency real-time applications (WebSocket-heavy platforms, live dashboards) where the Actor model and OTP supervision trees offer meaningful architectural advantages.
Web application frameworks, component libraries, and build tooling. This layer has evolved significantly in the last three years - we've been deliberate about following the ecosystem without chasing every new release.
Our default for web applications, marketing sites with dynamic requirements, and customer-facing dashboards. App Router for new projects.
Mandatory on all new frontend work, no exceptions. The refactoring confidence and IDE experience it provides outweigh the configuration overhead many times over.
Utility-first CSS for all new projects. Combined with a component-level design system, it gives teams the speed of inline styles without the maintenance cost.
Base component layer for accessible UI primitives. Copy-paste model means we own the components rather than depending on a third-party library's release cycle.
For animation-heavy interfaces where CSS transitions aren't sufficient. Used selectively - most interfaces don't need complex animation, and adding a heavy library for a few transitions is the wrong tradeoff.
Evaluating for content-heavy marketing sites and documentation platforms where zero-JS by default is a genuine advantage over Next.js.
Where and how we run things. Cloud platform choices, container orchestration, and the infrastructure-as-code tooling that makes all of it reproducible and auditable.
Default cloud for most client deployments. Deep service catalogue, mature tooling ecosystem, and the broadest hiring pool of any cloud platform.
Container orchestration for all production workloads above a certain complexity threshold. EKS on AWS, GKE on GCP. Self-managed for specific on-premise requirements.
Infrastructure-as-code for all cloud environments. OpenTofu (the OSS fork) for new engagements where licence concerns about HashiCorp's BSL change are relevant.
GitOps continuous delivery for Kubernetes. Declarative application state, drift detection, and multi-cluster management.
GCP for data/ML-heavy workloads (BigQuery, Vertex AI) and for clients with existing Google Workspace contracts. Azure for enterprise clients with Microsoft EA agreements and Active Directory dependencies.
Edge delivery, DDoS mitigation, WAF, and Zero Trust Network Access. Sits in front of most public-facing services regardless of underlying cloud.
Data platform, ML infrastructure, and the tooling that sits between raw data and production AI features. This category evolves faster than any other in the stack.
Cloud data warehouse for analytics workloads, client reporting platforms, and data products. Near-instant scaling and the separation of storage from compute makes it the practical choice for variable analytics loads.
SQL-based transformation layer for all data warehouses. Version-controlled, tested, documented transformations that treat SQL as first-class engineering artefacts.
Event streaming backbone for real-time data pipelines, microservice event buses, and change data capture. Confluent Cloud for managed deployments; self-managed MSK for cost-sensitive high-throughput use cases.
Unified analytics platform for ML training, large-scale batch processing, and data science collaboration. Primarily used for ML platform engagements rather than general analytics.
Deep learning framework for all custom model development. Fine-tuning, training infrastructure, and production inference.
Foundation model APIs for production LLM features - classification, generation, retrieval-augmented generation, and agents. Both OpenAI GPT-4o and Anthropic Claude 3.5/4 in active use depending on task profile.
The five questions we ask before
adding anything to the stack.
New tools always look good in a demo. The discipline is asking the questions that reveal the costs that aren't visible until you're running the thing at 2am.
Tools we've tried, evaluated, and
decided against - with honest reasons.
These aren't bad tools. They're tools where, for our specific context, the tradeoffs didn't work in our favour. Some may be right for your situation.
Heavy operational burden, XML-heavy configuration, and a plugin ecosystem that has seen better days. The core product hasn't evolved at the same pace as GitHub Actions or GitLab CI. For new projects, there's no scenario where we'd choose Jenkins.
A document store is the right answer for specific data shapes - but we've seen too many projects where it was chosen for developer convenience and later caused significant pain when relational constraints turned out to be needed after all.
The simplicity was genuinely excellent before the pricing and free tier changes of 2022–2023. For the cost at meaningful scale, AWS/GCP with managed Kubernetes delivers significantly more control and comparable DX.
For immutable infrastructure on Kubernetes, Ansible's stateful configuration management model creates more complexity than it solves. We use it for specific legacy environments that can't be containerised.
Vite has effectively replaced Webpack for new frontend projects - faster dev server, better DX, and configuration that doesn't require a specialist to maintain. Webpack remains on legacy projects where migration cost isn't justified.
Excellent product, but the pricing model scales in a way that surprises teams. For projects that fit the open-source observability stack, Prometheus + Grafana + Loki + Tempo delivers comparable coverage at a fraction of the cost.
What changed, when, and why.
Technology choices aren't permanent. The changelog below tracks significant additions, removals, and upgrades across the stack - with the reasoning that prompted each change.
We review the stack quarterly.
The goal isn't to chase new releases - it's to catch cases where the ecosystem has moved on enough that our defaults are no longer the best defaults. Most quarters, very little changes.
Questions about specific choices? Ask our engineering team.
HashiCorp's BSL licence change for Terraform in 2023 created uncertainty for open-source users. OpenTofu (the CNCF-backed fork) is now our default for new engagements. Separately, Next.js App Router matured enough in 14.x to become our default over Pages Router - the data fetching patterns are cleaner and Server Components reduce client bundle size on most applications.
Claude 3.5 Sonnet's coding and reasoning performance made it a genuine alternative to GPT-4o for many tasks, and its longer context window made it preferable for document-intensive applications. We now maintain model-agnostic LLM integration layers and choose the provider based on task profile rather than defaulting to one.
Webpack served the ecosystem well for nearly a decade. Vite's HMR speed, ES module-native development experience, and configuration simplicity make it the better default for new projects. Teams have consistently reported 80–90% faster local startup times after migrating.
Moving away from a third-party component library to the shadcn/ui model (copy-paste components built on Radix UI) gives us complete control over component internals, removes a dependency update vector, and produces more maintainable code. The tradeoff is initial setup time - worth it on projects that will be maintained for more than six months.
Jenkins had been on the "to replace" list for some time. The final catalyst was the hiring signal - engineers expect modern CI/CD, and Jenkins knowledge is neither transferable to other tools nor a skill candidates aspire to develop. GitHub Actions covers 95% of our CI requirements with significantly lower operational overhead.
Build something serious with
a team that takes the craft seriously.
If you're building a product and want engineers who are opinionated about the right way to do things - not just the fastest - we'd like to hear about it. And if you're an engineer who cares about these questions, we're always looking.