Sinatra vs Rails — When I Reach for the Smaller Tool

by Eric Hanson, Backend Developer at Clean Systems Consulting

The service that does not need generators

You are building a webhook receiver. It has three endpoints: one to receive events, one to replay them, one health check for your load balancer. Someone on your team is about to run rails new and you need to talk them out of it. Rails would technically work — it always does — but you would be shipping a service with an ORM, an asset pipeline skeleton, mailer stubs, and 40 megabytes of framework for a service with 200 lines of business logic. Sinatra is the right tool here.

What Sinatra is and is not

Sinatra is a DSL built on Rack. It gives you route matching, request/response handling, and helpers for rendering responses. It does not give you generators, an ORM, a standard directory layout, a testing infrastructure, or any of the other opinions Rails brings. That is the point.

# The whole application can live in one file for simple services
require 'sinatra'
require 'sinatra/json'
require 'json'

set :logging, true

post '/webhooks/events' do
  payload = JSON.parse(request.body.read)
  EventProcessor.new(payload).process
  json status: 'accepted'
end

post '/webhooks/replay' do
  event_id = params[:event_id]
  event = EventStore.find(event_id)
  halt 404, json(error: 'Event not found') unless event
  EventProcessor.new(event.payload).process
  json status: 'replayed'
end

get '/health' do
  json status: 'ok', version: ENV['APP_VERSION']
end

That is a deployable Sinatra application. No MVC, no config/ directory tree, no Gemfile with 60 transitive dependencies. Boot time is under a second. Memory footprint is around 30-50MB depending on what you pull in.

The cases where Sinatra earns its place

Internal APIs and service adapters: A service whose only job is to translate between two external API formats, validate payloads, and forward them. There is no domain model. There is no persistence layer. Rails would be waste; Sinatra is exactly right.

Lambda and edge function targets: When your deployment target is a serverless function or a Rack adapter on a CDN edge runtime, boot time and memory matter directly. Sinatra's 50MB baseline beats Rails' 150-200MB comfortably.

Prototyping API contracts: When you need to stand up a fake API server quickly for frontend development or integration testing, Sinatra's lack of ceremony is a feature. You write what you mean without plumbing.

High-traffic stateless endpoints: Sinatra's Rack layer is thin. For services that are pure compute — no database, no external calls — Sinatra can handle more requests per worker than Rails because the middleware stack is shorter. Benchmarks on a 4-core server (ApacheBench, 1000 concurrent connections, simple JSON response) show Sinatra at roughly 8,000-10,000 RPS versus Rails at 4,000-6,000 RPS per process. Neither is a bottleneck at typical loads, but the difference is real.

Where Sinatra will make you regret the choice

Sinatra has no routing namespace by default. For a service with 30 endpoints, your route file becomes hard to navigate without discipline. Rails' controllers and concerns provide a structure that scales with complexity. You can add sinatra-contrib for route namespacing and modular apps, but you are now building framework infrastructure instead of your product.

Database integration is entirely manual. Sinatra does not know what ActiveRecord is. You configure your own ORM connection, your own migration tooling, your own test fixtures. If you are using ActiveRecord anyway (which you can), you have lost most of Sinatra's simplicity while keeping its convention gaps. At that point, Rails is almost certainly the better host for ActiveRecord.

# Sinatra with ActiveRecord gets messy fast
require 'sinatra'
require 'active_record'

ActiveRecord::Base.establish_connection(
  adapter: 'postgresql',
  database: ENV['DB_NAME'],
  host: ENV['DB_HOST'],
  username: ENV['DB_USER'],
  password: ENV['DB_PASSWORD']
)

# You are now maintaining what Rails gives you for free

Authentication is another gap. Devise does not run on Sinatra. Rolling your own token auth in Sinatra is straightforward; rolling your own session-based auth with security hardening is not.

The practical line

Use Sinatra for services that have three characteristics: fewer than 15 routes, no database persistence layer or a simple one-table read-only query layer, and a clear single responsibility. Anything beyond that and Rails starts paying for its conventions.

The mistake I see most often is teams using Sinatra for something small that grows, and finding themselves six months later with a custom router, a custom ORM integration, a custom authentication middleware — in short, a poorly specified Rails. If you anticipate the surface area growing, start with Rails. If the service genuinely will not grow beyond its initial scope, Sinatra keeps it honest.

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

Git Hooks: Automate the Checks Your Team Keeps Forgetting

Git hooks run scripts at specific points in the Git workflow — before a commit, before a push, after a merge. They are the lightweight automation layer that enforces standards locally before code ever reaches CI.

Read more

The Follow Up Message That Does Not Feel Desperate

The difference between a follow-up that works and one that damages your position is tone, timing, and whether you are adding something or just asking for something.

Read more

The Best Code Is the Code You Did Not Have to Write

Every line of code you write is a line that has to be tested, maintained, and understood. The highest-leverage engineering decisions are the ones that solve problems without adding code at all.

Read more

The Strategy Pattern in Java — Replacing Conditional Dispatch With Polymorphism

Conditional dispatch — switching on a type or status to select behavior — is the most common source of rigid code in Java applications. The strategy pattern replaces the switch with polymorphism, but the right implementation depends on what varies and how often it changes.

Read more