Why We Don't Use TypeScript on the Backend
TypeScript is great for React components. It's overkill for a Rails API. Here's our take on where types help and where they just add noise.
Why We Don’t Use TypeScript on the Backend is where we draw the line on how software should be built.
TypeScript is great for React components. It’s overkill for a Rails API. Here’s our take on where types help and where they just add noise. We write from operating experience, not trend-chasing.
Where Types Shine
The frontend is where TypeScript earns its keep. A React application is fundamentally about data flowing through components. Props go in, JSX comes out. When you have 200 components passing data to each other, types are the difference between confidence and chaos.
Without TypeScript, refactoring a shared component is terrifying. You rename a prop, and you have no idea what breaks until runtime. With TypeScript, the compiler tells you every callsite that needs updating before you even save the file. That’s not ceremony — that’s a safety net that pays for itself on day one.
API responses are the other obvious win. When your backend returns a JSON blob, TypeScript interfaces ensure your frontend handles it correctly. Missing field? Compile error. Changed field name? Compile error. The contract between frontend and backend is enforced by the compiler instead of by hope.
Where Types Add Noise
The backend is a different story. Rails is a convention-driven framework. The conventions are the type system.
When you write has_many :invoices in a Rails model, Rails knows the invoices table exists, knows the foreign key is user_id, knows how to generate the SQL, and gives you methods like user.invoices.where(status: :sent). Adding a type annotation to this doesn’t make it safer — Rails already handles it. Adding a type annotation makes it slower to write and harder to read.
Ruby’s duck typing isn’t a weakness. It’s a design choice that prioritizes developer speed. When I write a service object that takes anything responding to #call, I don’t need to define an interface. I don’t need a generic type parameter. I just write the code, and if I pass the wrong thing, my test suite catches it in 4 minutes.
That’s the key: tests replace types on the backend. Our Rails test suite covers every endpoint, every service object, every edge case. It runs in 4 minutes on CI. A failing test tells me exactly what’s wrong in a way that a type error sometimes doesn’t — because a type error tells me the shape is wrong, but a test tells me the behavior is wrong. Behavior is what matters.
The TypeScript-Everywhere Trap
There’s a movement to use TypeScript everywhere. Full-stack TypeScript. One language, one type system, shared types between frontend and backend. It sounds elegant.
In practice, it means choosing Node.js or Deno for your backend, which means giving up Rails. Giving up Rails means giving up ActiveRecord, Action Mailer, Solid Queue, Solid Cache, Turbo, Stimulus, and twenty years of battle-tested conventions that let a small team move fast. We’re not doing that to avoid learning two languages.
The developers who advocate TypeScript everywhere are often coming from JavaScript backends that genuinely needed types because JavaScript’s implicit coercion and lack of conventions made everything unpredictable. Fair enough. But Ruby isn’t JavaScript. Rails isn’t Express. The problem TypeScript solves on the backend barely exists when your framework does the heavy lifting.
Our Rule
TypeScript where data flows through many hands — the frontend. Ruby where conventions and tests provide the guardrails — the backend. No ideology. Just pragmatism.
The right answer to “should we use TypeScript?” isn’t yes or no. It’s “where?”
Hard Stop
If you agree, build this way. If you disagree, test the opposite and measure the real cost.
Start with Miru or read the docs.
Vipul A M
Co-founder at Saeloun. Building Miru. Rails contributor. Shipping from Pune, India.
Read next
We Run a Monolith and We're Not Sorry
Microservices are a scaling solution for organizations, not technology. Here's why a Rails monolith serves Miru perfectly.
How We Use gbrain to Build Miru
How Saeloun uses gbrain, gstack, Codex, Claude, MCP, and repo signals to build Miru with memory, safer AI automation, and proof before claims.
How We Track Time with AI Agents and the Miru CLI
A practical guide to automated time tracking for teams using Claude Code, Codex, and other AI coding tools. Real workflows, real scripts, zero browser tabs.
Put it to work
Run one cleaner billing cycle in Miru.
If this article is about tracking time, billing clients, comparing tools, or automating work, Miru is the product version of that idea. Start free, invite the team, and send the next invoice from tracked work.
What you get
- Time tracking, invoices, expenses, and payments in one place.
- Free for up to 5 users. Pro is $1/member/month.
- Open source, with CLI, API, MCP, and self-hosting paths.
The article is the argument. Miru is the workflow.
Track the work, approve the hours, send the invoice, and get paid without bolting together three separate tools.