Ruby on Rails vs Spring Boot — How I Choose for a New Project

by Arif Ikhsanudin, Backend Developer

The decision nobody wants to make twice

Your CTO has handed you a greenfield service and a six-week runway to first deployment. The team has two senior engineers — one who has spent the last four years in Java microservices land, one who came up through Rails startups. You are about to have the framework argument. Skip it. Here is how I actually make this call.

What Rails gets right that Spring Boot does not

Rails wins on time-to-first-feature. Convention over configuration means a new resource — model, controller, serializer, migration — is four files and one generator command. There is no XML, no annotation soup, no context configuration class. Active Record makes join-heavy queries readable without a query builder library. For a domain that is mostly CRUD with some business logic layered on top, Rails will put you in production faster than Spring Boot by a factor of two or three in my experience.

The other thing Rails gets right is the default stack coherence. Action Mailer, Active Job, Action Cable, Turbo — these are not third-party integrations. They are first-class citizens with shared conventions. When your service needs background jobs, you reach for Sidekiq backed by Redis. The integration is documented, battle-tested, and works identically across every Rails app I have built.

# A typical Rails service object — no ceremony
class ProcessPayment
  def initialize(order:, payment_method:)
    @order = order
    @payment_method = payment_method
  end

  def call
    ActiveRecord::Base.transaction do
      charge = @payment_method.charge(@order.total)
      @order.update!(status: :paid, charge_id: charge.id)
      OrderMailer.confirmation(@order).deliver_later
    end
  end
end

That is the whole thing. No dependency injection container. No interface declaration. No bean lifecycle.

What Spring Boot gets right that Rails does not

Concurrency is where Spring Boot earns its place. Rails is single-threaded per Puma worker by default. You can tune it, but the GIL (Global Interpreter Lock) in MRI Ruby means true parallelism requires multiple OS processes, not threads. A Spring Boot service running on the virtual thread model introduced in Java 21 (Project Loom) can handle tens of thousands of concurrent connections with a modest heap. If your service sits on a high-throughput messaging pipeline — say, 5,000 requests per second sustained — Rails will require significantly more horizontal scaling to match the same throughput.

Spring Boot also wins when the domain model is genuinely complex. Hibernate's type system, Spring Data's repository abstractions, and the Java type checker catch a category of bugs at compile time that Rails will only surface in production or in tests if you wrote them. When you have a payment processing service with 15 entity types, 40 business rules, and regulatory compliance requirements, that type safety is not pedantry — it is incident prevention.

@Service
@Transactional
public class PaymentService {

    public PaymentResult process(Order order, PaymentMethod method) {
        // The compiler tells you if order or method is nullable
        // before this ships — Rails won't
        var charge = method.charge(order.getTotal());
        order.setStatus(OrderStatus.PAID);
        order.setChargeId(charge.getId());
        orderRepository.save(order);
        eventPublisher.publish(new OrderPaidEvent(order.getId()));
        return PaymentResult.success(charge);
    }
}

The actual decision criteria

Choose Rails when:

  • Your team has strong Ruby experience or is small and needs to move fast
  • The domain is CRUD-heavy with moderate business logic
  • You need to validate product-market fit and expect the schema to evolve rapidly
  • You are building an internal tool, admin dashboard, or API that serves a single frontend

Choose Spring Boot when:

  • You need sustained high throughput (>1,000 RPS per instance) or low-latency SLAs (< 50ms P99)
  • The domain is complex, with strict type contracts between services
  • Your team is Java/Kotlin-native and you cannot afford a ramp-up cost
  • You are operating in a regulated industry where auditability and compile-time correctness matter

The cases where I have been wrong: I once pushed Rails on a team of three Java engineers because I estimated the delivery speed gain would outweigh the learning curve. It did not. The team wrote defensive, un-idiomatic Ruby that performed worse than equivalent Java would have, because they were fighting the language instead of using it. Team composition overrides everything else on this list.

What to do this week

If you are stuck in this decision right now: build the same endpoint in both frameworks with your actual team. Time it — not the runtime performance, the development time. Whichever takes fewer hours to produce a tested, deployed endpoint wins, because that ratio holds for the rest of the project. The performance gap between Rails and Spring Boot is real but rarely the bottleneck. The productivity gap between a team using its native stack versus a foreign one almost always is.

Scale Your Backend - Need an Experienced Backend Developer?

We provide backend engineers who join your team as contractors to help build, improve, and scale your backend systems.

We focus on clean backend design, clear documentation, and systems that remain reliable as products grow. Our goal is to strengthen your team and deliver backend systems that are easy to operate and maintain.

We work from our own development environments and support teams across US, EU, and APAC timezones. Our workflow emphasizes documentation and asynchronous collaboration to keep development efficient and focused.

  • Production Backend Experience. Experience building and maintaining backend systems, APIs, and databases used in production.
  • Scalable Architecture. Design backend systems that stay reliable as your product and traffic grow.
  • Contractor Friendly. Flexible engagement for short projects, long-term support, or extra help during releases.
  • Focus on Backend Reliability. Improve API performance, database stability, and overall backend reliability.
  • Documentation-Driven Development. Development guided by clear documentation so teams stay aligned and work efficiently.
  • Domain-Driven Design. Design backend systems around real business processes and product needs.

Tell us about your project

Our offices

  • Copenhagen
    1 Carlsberg Gate
    1260, København, Denmark
  • Magelang
    12 Jalan Bligo
    56485, Magelang, Indonesia

More articles

A Good API Is One Developers Never Have to Ask Questions About

APIs fail when they require interpretation instead of execution. The best APIs eliminate ambiguity through consistent design, predictable behavior, and self-evident contracts.

Read more

NULL in SQL Does Not Mean What You Think It Means

NULL represents the absence of a value, not zero, not an empty string, and not false — its three-valued logic and propagation rules produce query results that are consistently surprising to developers who treat it as a regular value.

Read more

Service Locator vs Dependency Injection in Java — Understanding the Tradeoffs

Both patterns resolve dependencies, but they make opposite choices about who controls the lookup. The difference has concrete consequences for testability, transparency, and how errors surface.

Read more

Testing Is Not About Proving Your Code Works. It Is About Knowing When It Breaks.

Most developers write tests to confirm their code does what they just wrote it to do. That is the wrong goal entirely — and it explains why test suites so often fail the team the moment something actually goes wrong.

Read more