We're making it super-easy to run any travel business.
Which makes it a super-hard engineering problem.

Different pricing rules. Different payment collection flows. Different backoffice workflows. Frankly, different everything. Making all of it feel like one simple product is where the engineering gets hard and interesting.

How hard?
Haskell 164KRuby/Rails 128KJavaScript 66KTypeScript 30K
+ 226 tables in Postgres, all working in tandem to turn a complex offline process into a few effortless clicks.
// they say “measuring programming progress by lines of code is like measuring aircraft building progress by weight.” and we agree. but these stats are still interesting from a historical standpoint -- documenting how we've evolved, learning, solving, and re-learning one problem after another.

The decade (and a half) behind our codebase

Each language here was a deliberate choice to deal with engineering challenges we were facing at that time. Not all bets worked out -- and some were walked back.

▸ measured monthly from the monorepo · production code only · updated Jul 2026
2012Rails + jQuery

First commit. A Rails app with a jQuery front-end.

2013AngularJS for the dynamic forms

Booking forms got too dynamic for jQuery, so we reached for AngularJS.

2014“The year our servers crashed”

A heads-down, funded focus year -- 3,600 commits and traffic up 140%. Then our servers ran out of memory and we had to double capacity to keep up.

Read the story →
2017Haskell, for fearless refactoring

Refactoring a large Rails+JS codebase as we learned the market was a major PITA. We wanted a typed language to change the core without fear -- and reached for Haskell: typed, and it forces you to write correct code.

2017Angular 2 -- for the types

The same typed-safety instinct hit the front-end: Angular v1 + JS was hard to evolve without constant manual testing. Angular 2's real pull was TypeScript -- a typed front-end was exactly what we wanted. But adopting it meant a full framework rewrite, so we backed out. (We'd get the types a better way -- see 2026.)

2018Haskell merges into the monorepo

The separate Haskell service (~34K lines by 2017) folds into the main repo -- which is where its line joins the graph.

2018Elm, the other typed-front-end bet

Same goal, different route: Elm (typed, pure) for internal admin UI. Peaked around 10K lines, then unwound -- TypeScript on AngularJS ended up giving us the same type-safety without a second front-end stack, and Elm itself had gone a bit quiet as a language. Both Elm and the Angular 2 experiment became redundant.

2020COVID -- gave up the villa, went remote

Near-extinction for a travel SaaS: the industry ran at ~30% of 2019, and didn't fully recover until 2024. Product work slows through 2021-22, but the codebase never stops moving.

2020Automating our own ops -- in Haskell

As travel cratered, we turned inward: years spent automating our own operations -- billing, CRM, support, internal messaging -- almost all in Haskell, wired to Zoho. It's part of why the Haskell curve keeps climbing right through the COVID trough. → Automation & AI

2024Turbo/Hotwire -- couldn't handle dynamic forms

Server-rendered HTML-over-the-wire, with the UI rendered server-side in Haskell -- tempting, because it would lean even harder on Haskell. But our forms are highly dynamic (the very thing that pushed us off jQuery back in 2013), and Turbo felt like a step backward even with the server doing the rendering. Dropped, not forced.

2025LLMs in production -- the current chapter

The newest layer: AI/LLM workflows that read and draft from support tickets, analyse sales calls, and more -- built on top of that Haskell ops-automation. → Automation & AI

2026TypeScript closes the loop -- no rewrite

The answer to the Angular 2 and Elm dead-ends turned out to be simple: add TypeScript to the AngularJS we already had, and auto-generate the front-end types from our Haskell backend. That Haskell→TypeScript bridge got us 80-90% of the end-to-end type-safety we'd been chasing -- without a full front-end rewrite. TS roughly quadrupled in 2026, and we expect the trend to continue. No Angular 1 to Angular 2 migration. No wholesale rewrite to React.

2026Haskell becomes the largest codebase

Overtakes Ruby -- and both are still growing. Haskell just grew faster, rather than replacing anything.

AI-native, because we run the whole company on it.

Not a feature we bolted on -- the operating system of a small team. And not a pivot: the AI layer is the next chapter of a decade spent automating our own operations in Haskell.

2020

Automated our ops

~8,000 lines of Haskell wiring Zoho, billing, support and chat together.

2024

Added an AI layer

Ticket triage and answering bots on top of the automation we already ran.

now

AI-native ops

Every release ships AI-drafted notes, emailers and broadcasts.

Support

Triage & first-response

  • Bot triages the inbound backlog
  • Drafts first replies on new tickets
  • Help-center articles from releases
Marketing

Content from every release

  • Release notes from ship logs
  • Emailers from feature releases
  • WhatsApp broadcast campaigns
Sales

Sales intelligence

  • Zoho Meeting audio → structured insight
  • Lead capture & CRM sync
Operations

The back office, automated

  • Billing ↔ subscriptions autosync
  • Accounting journal + PAYG invoicing
  • 31 Haskell modules, all in-house
Engineering

AI in the loop

  • LLM-assisted development
  • Typed pipelines, not vibes
This site

Built the same way

  • Icons generated by our own pipeline
  • Copy & release notes drafted with AI

Open source

Libraries we wrote for our own use and are releasing for everyone else's. odd-jobs already runs in production well beyond VacationLabs.

odd-jobs
● released · BSD-3
PostgreSQL-backed job queue for Haskell with a built-in web admin UI.
haskell-zoho
○ pre-release
Bindings for the Zoho API suite -- CRM, Desk, and more.
record-splicer
○ pre-release
Template Haskell to derive projection/update sub-record types from large records.
haskell-bunny-cdn
◇ not yet public
Client for Bunny CDN & Bunny Stream -- storage, pull zones, video.

Talks

A note on hiring

We're hiring slowly -- and for a specific kind of person.

Hiring changed in the last couple of years, and a lot of what used to work just doesn't. A take-home assignment barely tells us anything now -- AI can breeze through it. So can most of the interview questions we used to lean on.

So we've slowed down and gotten specific about what we're looking for. More than any particular stack, we want people who can use AI without being used by it: who treat it as a sharp assistant and keep hold of the reins. You're driving it, not being driven by it.

We don't have a neat process to hand you yet -- we're working that out like everyone else. But if that resonates, email us and tell us how you actually work with these tools.

— Saurabh
Founder, VacationLabs
Email us →