A deliberately boring technology stack

We keep to mainstream, well-documented tools. Not from caution, but because your system has to be maintainable by engineers who are not us — which is exactly the position you should be in.

Chosen per project · Justified in writing · Never for the CV

01 — The rule

Four questions before anything enters a project

Every dependency is a commitment somebody has to keep for years. Before we add one, it has to survive these.

  1. Can we hire for it? If the local market cannot staff it, you are buying a dependency on us specifically.
  2. Will it exist in five years? Governance, release history and the size of the community matter more than benchmarks.
  3. What does it cost to remove? Anything that reaches into every file is a decision that cannot be reversed cheaply.
  4. Does it earn its weight? A library that replaces thirty lines of code is not a saving.

When a project genuinely calls for something unusual, we make the case in writing, note the trade-off in an architecture decision record, and get your agreement before committing.

This website, as a worked example

Twenty pages, no framework, no build step in production, no third-party requests, no cookies. The whole thing is HTML, one stylesheet, one script and two self-hosted typefaces.

JavaScript shipped
One file, ~13 KB before compression
Third-party requests
None
Cookies
None
Runtime dependencies
None

It is not the right architecture for every product. It is the right architecture for this one, and that distinction is the entire point.

02 — The stack

What we work with day to day

Not an exhaustive list of everything anyone here has ever touched — the things we are genuinely fluent in and willing to support.

TypeScript & Node.js

Our default for business applications and APIs. One language across the stack, a mature ecosystem, and a type system that makes refactoring safe.

  • Fastify, Express, NestJS
  • Strict mode, no implicit any

Python

Where data processing, scripting or scientific libraries lead. Typed, linted and packaged like any other production code.

  • FastAPI, Django
  • pandas, pydantic

Go

For services where predictable latency, small binaries and low memory matter — integration workers, high-throughput APIs, CLI tooling.

  • Standard library first
  • Single-binary deployment
  • TypeScript
  • Node.js
  • Python
  • Go
  • SQL
  • Bash

Standards first

Semantic HTML and modern CSS do more than most teams assume. Grid, container queries, custom properties and view transitions replace a lot of former JavaScript.

  • No runtime CSS-in-JS
  • Design tokens in CSS variables

React & Next.js

When an interface has genuine application state. Server components where they reduce shipped JavaScript, client components where interaction requires them.

  • React, Next.js, Vue
  • Vite for tooling

Mobile

React Native for shared codebases; Swift and Kotlin when the app leans on the platform. The choice is argued per project, not assumed.

  • React Native
  • Swift, Kotlin
  • HTML
  • CSS
  • React
  • Next.js
  • Vue
  • React Native
  • Swift
  • Kotlin

PostgreSQL

Our default system of record. Constraints in the database rather than only in the application, and migrations versioned alongside the code.

  • Foreign keys and checks enforced
  • Reversible migrations

Caching & queues

Redis for caching, locks and lightweight queues. Dedicated brokers when delivery guarantees justify the extra moving part.

  • Redis
  • RabbitMQ, Kafka

Analytical stores

When reporting queries start hurting the transactional database, we separate them rather than adding indexes forever.

  • ClickHouse
  • Materialised views
  • PostgreSQL
  • MySQL
  • Redis
  • ClickHouse
  • SQLite

Infrastructure as code

Terraform in your repository, reviewed like application code. No environment exists that cannot be rebuilt from a commit.

  • Terraform
  • Docker

Runtime

Containers on a managed platform by default. Kubernetes when the scale and the team genuinely justify the operational cost.

  • ECS, Cloud Run
  • Kubernetes

Observability

Structured logs, metrics and traces from the first deployment, not retrofitted after the first outage.

  • Prometheus, Grafana
  • OpenTelemetry
  • Terraform
  • Docker
  • Kubernetes
  • AWS
  • Google Cloud
  • Hetzner
  • Grafana

Automated tests

Unit tests for logic, integration tests at every boundary, end-to-end tests for the journeys whose failure a customer would notice.

  • Vitest, Jest, pytest
  • Playwright

Static analysis

Type checking, linting and formatting enforced in CI so review conversations are about design rather than semicolons.

  • ESLint, Ruff
  • Prettier

Supply chain

Dependency scanning on a schedule, pinned versions, and an upgrade plan rather than an annual panic.

  • Automated advisories
  • Lockfiles committed
  • Playwright
  • Vitest
  • pytest
  • ESLint
  • GitHub Actions

03 — A note on AI tooling

Where machine assistance fits, and where it does not

Our engineers use AI assistants the way they use any other tool — for boilerplate, for exploring an unfamiliar API, for a first draft of a test. What does not change is that a human reads every line, a second engineer reviews it, and the automated suite has to pass before anything merges.

We do not send client source code or production data to a third-party service without your written agreement, and where a client prohibits such tooling entirely we work without it. If you would like this stated explicitly in the contract, ask — we will put it there.

Already have a stack you have to live with?

Tell us what it is. We would rather work inside your constraints than sell you a rewrite you do not need.

HALDIR LTD · HE 459882 · Nicosia, Cyprus