Company Technology Stack

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.

Our principles
Boring beats novel
Own the ops burden
Open source preferred
No lock-in without reason

We have opinions about tooling - and we're willing to defend them. Here's the reasoning that shapes every decision on this page.

Boring technology is good technology

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.

We own the operational burden

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.

Open source unless there's a good reason

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.

The stack should enable engineers, not slow them

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.

Backend Engineering

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.

06
Node.js / TypeScript Primary

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.

The async model and npm ecosystem are the main reasons. For I/O-bound services (which most are), Node.js performance is more than adequate and the hiring pool is deep.
Python Primary

Mandatory for anything ML/data - FastAPI for inference endpoints, Django for data-heavy admin platforms, and script-heavy data engineering pipelines.

The scientific Python ecosystem (NumPy, Pandas, PyTorch, scikit-learn) has no realistic competitor. For web APIs outside ML context, we'd reach for Node first.
Go Secondary

For high-throughput services, CLI tooling, and infrastructure components where static binary deployment matters. Used selectively - where the performance profile justifies the context switch.

The build performance, minimal runtime dependencies, and excellent concurrency model are the draws. We don't use Go for general web services because the TypeScript ecosystem offers more without a meaningful performance gap at typical load.
Rust Secondary

Smart contract development (Solana programs via Anchor), WASM components, and performance-critical systems code where memory safety matters as much as throughput.

The learning curve is real and the compile times are a tax on iteration speed. We use Rust where it's the right tool - primarily blockchain and systems work - not as a general-purpose language.
Ruby on Rails Secondary

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.

We're honest that Rails isn't the default anymore - that's Node/TypeScript. But for a team that knows Rails and a project that fits its strengths, it remains genuinely excellent.
Elixir / Phoenix Evaluating

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.

The LiveView approach to reactive UIs without heavy JavaScript is genuinely interesting for internal tooling. Not yet in primary rotation - but on the roadmap for the right project.
Frontend & Web

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.

06
React + Next.js Primary

Our default for web applications, marketing sites with dynamic requirements, and customer-facing dashboards. App Router for new projects.

The React ecosystem is the largest in frontend engineering - component libraries, testing tools, and engineers who know it are all readily available. Next.js App Router has resolved most of our previous SSR/SSG frustrations.
TypeScript Primary

Mandatory on all new frontend work, no exceptions. The refactoring confidence and IDE experience it provides outweigh the configuration overhead many times over.

The TypeScript vs JavaScript argument is largely over in our team. Plain JavaScript is only acceptable in tooling scripts where the TS compilation step adds friction without benefit.
Tailwind CSS Primary

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.

The criticism about readability is real but manageable. The productivity gain over traditional CSS architecture is significant enough on team projects that we consider it the default.
shadcn/ui + Radix Primary

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.

Accessibility from the ground up, headless primitives, and full control over styling. The decision to own the components rather than import a library reflects our preference for long-term maintainability over short-term convenience.
Framer Motion Secondary

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.

The performance is good when used correctly. The risk is overuse - teams that reach for Framer Motion by default rather than by necessity end up with heavier bundles and harder-to-maintain animation logic.
Astro Evaluating

Evaluating for content-heavy marketing sites and documentation platforms where zero-JS by default is a genuine advantage over Next.js.

The island architecture is compelling for sites where most content is static. Not yet the default - our team is more productive in Next.js - but likely to become the recommendation for specific project types.
Cloud & Infrastructure

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.

06
AWS Primary

Default cloud for most client deployments. Deep service catalogue, mature tooling ecosystem, and the broadest hiring pool of any cloud platform.

The lock-in concern is real - using RDS or SQS ties you to AWS in a way that Kubernetes doesn't. We use managed services when the operational burden reduction is worth it and vanilla alternatives when portability matters more.
Kubernetes (EKS/GKE) Primary

Container orchestration for all production workloads above a certain complexity threshold. EKS on AWS, GKE on GCP. Self-managed for specific on-premise requirements.

Kubernetes has won. The learning curve was real in 2018; in 2025 it's table stakes. The managed services (EKS, GKE) have removed most of the operational pain that used to justify alternatives.
Terraform / OpenTofu Primary

Infrastructure-as-code for all cloud environments. OpenTofu (the OSS fork) for new engagements where licence concerns about HashiCorp's BSL change are relevant.

The HCL language is genuinely limiting for complex logic - but the ecosystem, provider coverage, and team familiarity make it the right choice for most IaC work. Pulumi for TypeScript-heavy teams where the HCL friction becomes significant.
ArgoCD Primary

GitOps continuous delivery for Kubernetes. Declarative application state, drift detection, and multi-cluster management.

The GitOps mental model - the cluster should reflect the repo, always - is genuinely better than push-based deployment for most production workloads. ArgoCD has the best UX and community of the GitOps tools.
GCP / Azure Secondary

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.

Neither is the default. Both have genuine strengths in specific contexts. The mistake is choosing cloud provider based on marketing or familiarity rather than fit for the actual workload.
Cloudflare (CDN + Zero Trust) Secondary

Edge delivery, DDoS mitigation, WAF, and Zero Trust Network Access. Sits in front of most public-facing services regardless of underlying cloud.

The Workers platform is increasingly interesting for edge compute - though we're cautious about overusing it for logic that belongs in the application layer.
Data & AI/ML

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.

06
Snowflake Primary

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.

The cost model requires discipline - compute consumption is easy to lose track of. We implement query cost monitoring and spend alerts on all Snowflake deployments from day one.
dbt Core Primary

SQL-based transformation layer for all data warehouses. Version-controlled, tested, documented transformations that treat SQL as first-class engineering artefacts.

dbt has standardised how analysts and engineers collaborate on data transformations. The testing and documentation infrastructure it provides would otherwise need to be built ad hoc.
Apache Kafka / Confluent Primary

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.

Kafka's durability model and consumer group architecture are genuinely difficult to replicate with alternatives. For simple async messaging, AWS SQS/SNS is often sufficient and cheaper.
Databricks Primary

Unified analytics platform for ML training, large-scale batch processing, and data science collaboration. Primarily used for ML platform engagements rather than general analytics.

The cost is meaningful for smaller organisations. For enterprise ML platforms and teams doing serious ML engineering, the Delta Lake integration and MLflow tracking make it the right choice.
PyTorch Primary

Deep learning framework for all custom model development. Fine-tuning, training infrastructure, and production inference.

PyTorch has won the research-to-production battle. TensorFlow is maintained but the community direction is clear. New model work starts in PyTorch.
OpenAI / Anthropic APIs Primary

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 model-agnostic layer matters. We build with LiteLLM or equivalent abstraction so the underlying provider can be swapped without rewriting application code.

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.

Tool
Battle-tested?
Team can run it?
Ecosystem health
Lock-in risk
Verdict
Kubernetes
Yes
Partial
Primary
Terraform
Yes
Partial
Primary
Kafka
Yes
No
Primary
Astro
Yes
No
Evaluating
Pulumi
Yes
Partial
Secondary
Nomad
Yes
No
Dropped
Elixir
Partial
No
Evaluating
Serverless FW
Yes
Yes
Context-specific

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.

CI/CD
Jenkins

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.

We use instead: GitHub Actions / GitLab CI
Database
MongoDB (as primary DB)

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.

We use instead: PostgreSQL for relational, Redis for caching
Platform
Heroku

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.

We use instead: AWS ECS or EKS with Fargate for simplicity
Infrastructure
Ansible for config management

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.

We use instead: Terraform + Helm for K8s; Ansible retained only for legacy
Build Tooling
Webpack (greenfield)

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.

We use instead: Vite for new projects; esbuild for libraries
Observability
Datadog (as first choice)

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.

We use instead: Prometheus / Grafana stack; Datadog where vendor support is needed

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.

Q1 2025
Last full stack review - 4 additions, 2 removals, 6 upgrades across all categories.
Q1 2025
Added OpenTofu; moved to Next.js App Router default

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.

+ OpenTofu + Next.js App Router − Pages Router default ↑ Terraform → OpenTofu migration path
Q3 2024
Added Anthropic Claude to primary LLM providers

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.

+ Claude 3.5 / 4 (Anthropic) ↑ LiteLLM provider abstraction ↑ Prompt versioning and evaluation pipeline
Q2 2024
Vite becomes default build tool; Webpack moved to legacy

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.

+ Vite (primary) − Webpack (new projects) ↑ esbuild for library builds
Q4 2023
shadcn/ui replaces previous component library dependency

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.

+ shadcn/ui + Radix UI − Third-party component library dependency ↑ Accessible component baseline
Q2 2023
Dropped Jenkins; standardised on GitHub Actions

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.

− Jenkins + GitHub Actions (primary) ↑ GitLab CI for self-hosted requirements

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.

Start a Project Conversation View Open Roles
Stack reviewed quarterly, not when convenient
Open source contributions welcome
Engineering questions answered honestly