Skip to content
Miru 3.0 is here — expenses, CLI, dark mode, and 6 report types. Read the announcement →
Engineering Rails

Why We Chose Rails 8 and React 18 for Miru 3.0

In a world obsessed with Next.js and serverless, we bet on Rails 8 and React 18. Here's why boring tech wins.

Vipul A M · · 4 min read

Every few months, someone on Hacker News declares that Rails is dead. They say it with the confidence of someone who’s never shipped a product to paying customers on a deadline. Meanwhile, Shopify runs on Rails. GitHub runs on Rails. Basecamp runs on Rails. And Miru 3.0, which we just shipped with expense management, a CLI, six report types, and a redesigned everything, runs on Rails 8.

Here’s why.


Rails 8: The Framework That Stopped Apologizing

Rails has always been opinionated. But for a while, it felt like the framework was apologizing for those opinions. Trying to be everything to everyone. Supporting five different JavaScript bundling approaches. Adding Action Cable complexity for real-time features that most apps don’t need.

Rails 8 stopped apologizing. And that’s why we chose it.

Solid Queue replaced the need for Redis and Sidekiq for background jobs. One fewer dependency. One fewer thing to monitor in production. One fewer thing that breaks at 2 AM. Solid Queue runs on the same PostgreSQL database you’re already running. The job queue is a table. The workers are threads. It’s boring and it works.

Solid Cache does the same thing for caching. No Memcached. No Redis. Just your database. For a product like Miru that serves hundreds of small teams, this is perfect. We don’t need a distributed caching layer. We need a cache that works and doesn’t add operational complexity.

Kamal 2 for deployments. We can push to production from a single command. No Kubernetes. No complex CI/CD pipeline. kamal deploy and you’re done. For a small team shipping fast, this is worth more than any infrastructure abstraction.

The philosophy is clear: reduce dependencies, simplify operations, ship faster. That’s exactly how we want to build software.


React 18 with TypeScript: The Frontend That Knows Its Place

We get questions about this one. “Why React and not Hotwire? Aren’t you a Rails shop? Shouldn’t you be using the Rails way?”

Here’s our take: Hotwire is great for content-heavy apps. But Miru is an interactive application. Time tracking interfaces, invoice editors, drag-and-drop report builders, real-time form validation — these need the component model. They need state management. They need the ecosystem.

We chose React 18 with TypeScript, not a full framework like Next.js. This is an important distinction. Next.js is a deployment platform disguised as a framework. It wants to own your routing, your API layer, your server-side rendering, your hosting. We didn’t need any of that. We needed a component library that plays nicely with Rails.

React 18 gives us concurrent rendering for smoother UI updates. TypeScript gives us type safety that catches bugs before they reach production. And because we’re not locked into a meta-framework, we control every layer of the stack.

The frontend is a single-page application that talks to the Rails API. Clean separation. The React app doesn’t know or care that Rails is serving the API. The Rails app doesn’t know or care that React is rendering the UI. If we ever want to swap one side, we can. We probably won’t. But we can.


Vite Over Webpack: Speed as a Feature

We switched from Webpack to Vite in this release. The developer experience difference is obscene.

Cold starts went from 30+ seconds to under 2 seconds. Hot module replacement is instant, not “instant after a 4-second pause.” The build configuration is a fraction of the size. We deleted an entire webpack.config.js that had grown to 200 lines of accumulated configuration debt.

Vite isn’t exotic anymore. It’s the default for new React projects. But for teams migrating from Webpack, the switch feels like going from dial-up to broadband. It changed how often our developers save and preview. When the feedback loop is instant, you iterate faster. When you iterate faster, you ship better software.


PostgreSQL: The Only Database You Need

We use PostgreSQL. Just PostgreSQL. Not PostgreSQL plus Redis plus Elasticsearch plus a vector database for AI features.

PostgreSQL handles our relational data, our full-text search, our JSON columns for flexible schemas, our background job queue (via Solid Queue), and our cache (via Solid Cache). One database. One backup strategy. One set of credentials. One thing to monitor.

There’s a trend in the industry toward polyglot persistence — using the “right” database for each use case. In theory, elegant. In practice, a maintenance nightmare for small teams. Every additional data store is another thing that can fail, another connection pool to tune, another backup to verify, another skill your team needs.

PostgreSQL does 95% of what any web application needs. The remaining 5% isn’t worth the operational complexity of adding a second database. Not at our scale. Maybe not at yours either.


Boring Tech Wins

DHH has been saying this for twenty years. Basecamp has been proving it for just as long. The most productive technology choice isn’t the newest one. It’s the one your team knows deeply, that has solved most of the problems you’re going to encounter, and that will still be around in ten years.

Rails 8. React 18. PostgreSQL. Vite. These aren’t cutting-edge choices. They’re battle-tested choices. And for a small team shipping a real product to paying customers, battle-tested beats cutting-edge every single time.

The Miru codebase is open source. You can read every line of it at github.com/saeloun/miru-web. No magic. No clever abstractions. Just boring tech that works.

Share:
VA

Vipul A M

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

Try Miru today

Free to start. No credit card required.

Start Tracking Free