Skip to content
Engineering Opinion

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.

Vipul A M Vipul A M · · 3 min read
Tracking
Miru time tracking interface with timers and recent entries
This article is currently written in English. Navigation, dates, and calls to action follow your selected language.

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.

Share:
Vipul A M

Vipul A M

Co-founder at Saeloun. Building Miru. Rails contributor. Shipping from Pune, India.

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.
See Miru

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.

Tracking
Miru time tracking interface with timers and recent entries
Time Tracking Miru