Skip to content
We're hiring: .NET software engineer

> Our tech stack

Opinionated technology choices for a platform that needs to be reliable, fast, and simple to maintain.

Backend

#

.NET 10 + ASP.NET Core Razor Pages

backend

Server-side rendering because it's the right tool for form-heavy business apps. No SPA complexity, no client-side state management, no hydration bugs. Razor Pages give us a clear request/response model with full-stack type safety.

#

Entity Framework Core

backend

Convention-based ORM that handles migrations, change tracking, and query generation. Snake_case naming with Npgsql. Rich domain entities with private setters and factory methods.

#

FluentValidation

backend

Server-side validation as the single source of truth. No client-side duplication. Inline field validation on blur via custom tag helpers.

#

PostgreSQL

backend

Reliable, performant, open source. Hosted on Azure Flexible Server with zero-downtime migrations via the expand-contract pattern.

#

Redis

backend

Hybrid cache (in-memory + distributed). Session state and output caching for the public-facing sites.

Frontend

#

Tailwind CSS v4

frontend

Utility-first CSS with no custom component library to maintain. Design tokens via @theme. Dark mode via custom variant.

#

Alpine.js + Alpine-AJAX

frontend

Lightweight interactivity without a build step. Alpine-AJAX gives us partial page updates (like HTMX) with Alpine.js reactivity. No SPAs, no virtual DOM, no bundle size anxiety.

Infrastructure

#

Azure Container Apps

infrastructure

Serverless containers with built-in scaling, zero-downtime revisions, and managed certificates. Scale to zero in non-production.

#

Bicep

infrastructure

Infrastructure as code that's actually readable. Every environment (production, test, PR previews) is defined in the same templates.

#

GitHub Actions

infrastructure

CI/CD with PR preview environments, what-if analysis for infrastructure changes, and automated certificate bootstrapping.

#

Docker

infrastructure

Multi-stage builds: Tailwind CSS compilation, .NET SDK for build, aspnet runtime for the final image.

Observability

#

OpenTelemetry

observability

Distributed tracing and metrics. Vendor-neutral instrumentation.

#

Serilog + Seq

observability

Structured logging with a beautiful local query UI. In production, logs flow to Azure Monitor.

Why Alpine-AJAX over a traditional SPA?

We tried the SPA approach. React, Next.js, client-side routing, state management libraries, API layers, serialisation, hydration, bundle splitting. All to render forms and tables for a business app.

The complexity wasn't justified. We were maintaining two applications (a .NET API and a React frontend) to do the job of one. Every feature required changes in both places. Type safety stopped at the API boundary. Client-side state drifted from the server. And the developer experience (waiting for webpack to rebuild while you tweak a label) was painful.

Alpine-AJAX gives us the UX people expect from SPAs (partial page updates, no full reloads, smooth transitions) without any of that overhead. The server renders HTML. Alpine-AJAX swaps fragments in the DOM. There's no JSON serialisation, no client-side routing, no state management, no virtual DOM diffing.

The result: one codebase, full-stack type safety, server-side validation as the single source of truth, and a frontend that's a few kilobytes of JavaScript instead of a few megabytes. Pages load fast. Forms just work. And we ship features in half the time.

koala
  • _

Want to work with this stack?

View careers

We use cookies to help improve your experience.