You DON'T understand Rails until you understand the full Request cycle
Rails isn't magic. Rails is Rack, TCP, threads, middleware, SQL and Ruby objects on the heap. Understand what happens before and after your controller.
You DON'T understand Rails until you understand the full Request cycle
There's a lie the Rails community told for many years.
"Rails is magic."
No.
Rails isn't magic.
Rails is:
- Rack
- TCP
- Threads
- Middleware
- SQL
- IO
- Serialization
- Ruby objects on the heap
And the problem is that a lot of people spend years writing:
def show
@user = User.find(params[:id])
end
…without understanding what happens before and after that line.
Then production starts to:
- get slow
- eat memory
- block threads
- time out
- spike CPU
- lock the database
…and debugging becomes superstition.
Because whoever doesn't understand the request cycle…
…debugs in the dark.
The "controller = Rails" illusion
Most beginners think Rails is:
- controller
- model
- view
That's it.
But controller is literally ONE STAGE of the whole pipeline.
When you receive an HTTP request:
GET /users/1
There's a whole ecosystem happening before:
User.find(params[:id])
And that's exactly where these things live:
- bottlenecks
- concurrency
- observability
- authentication
- sessions
- real performance
It all starts outside of Rails
First important fact:
Rails DOES NOT accept TCP connections.
That's done by:
- Puma
- Unicorn
- Passenger
Rails is just a Rack application.
That changes your whole perception of the stack.
The real flow starts like this:
Browser
↓
Linux Kernel
↓
TCP Socket
↓
Puma
↓
Rack
↓
Rails
See the difference?
Rails is FAR from being the first layer.
Puma and the reality of production
Puma listens on TCP sockets in Linux.
When a request arrives:
- it accepts the connection
- picks/reuses a thread
- creates the request context
- hands it off to Rack
And here a classic problem is born:
"My Rails is slow."
Maybe it isn't.
Maybe you're:
- blocking threads
- doing too much synchronous IO
- destroying concurrency
- causing starvation
- holding DB connections too long
Rails performance is almost never "just Rails".
It's the whole cycle.
Rack: the invisible piece of Ruby Web
If you've worked with Rails for years and never studied Rack…
…you probably haven't really understood Rails yet.
Rack is the standard interface between:
- Ruby web servers
- Ruby frameworks
It's the equivalent of:
- WSGI in Python
- middleware pipeline in Express
- the HTTP interface of the Ruby ecosystem
Puma basically does this:
env = { ... }
Rails.application.call(env)
That's it.
Rails receives a giant hash called env.
And responds in Rack format:
[
status, # 200, 404, 500...
headers, # { "Content-Type" => "text/html" }
body # ["<html>...</html>"]
]
Literally that.
All of Rails' "magic" exists on top of this absurdly simple abstraction.
Middleware: where half of Rails actually happens
This is one of the most underestimated parts of the stack.
Before reaching the controller…
…the request goes through dozens of middlewares.
Examples:
- cookies
- session
- authentication
- CSRF
- compression
- logs
- static files
All middleware.
Simplified flow:
Request
↓
Rack::Sendfile
↓
Cookies
↓
Session
↓
Warden/Devise
↓
CSRF
↓
Rails App
And this is VERY important.
Because:
- middleware can short-circuit a request
- middleware can respond directly
- middleware can modify the response
- middleware can catch exceptions
Your controller doesn't always run.
A lot of people never realize this.
Routing doesn't call controllers "directly"
Another abstraction that confuses beginners.
When you define:
get "/users/:id"
Rails doesn't "execute the method".
It:
- matches the route
- resolves controller/action
- creates an executable endpoint
- builds the request context
Routing is much closer to a dispatcher than to a simple URL table.
Finally: the controller
Now we reach the part everyone knows.
Rails:
- instantiates the controller
- runs callbacks
- runs the action
Example:
before_action :authenticate_user!
def show
@user = User.find(params[:id])
end
But here's another illusion.
That User.find looks innocent.
But under the hood:
- grabs a connection from the pool
- generates SQL
- talks to Postgres
- waits on IO
- deserializes the result
- instantiates Ruby objects
One line of ActiveRecord can cross:
- network
- database
- serialization
- memory
- garbage collector
And then comes the classic phrase:
"Rails doesn't scale."
No.
Your code might not scale.
Rendering is WAY more expensive than it looks
After the action…
…Rails still has to generate the response.
If it's HTML:
- find the template
- process ERB
- apply the layout
- render partials
- assemble the final HTML
And this can get absurdly expensive.
Especially when you:
- render partials in a loop
- create too many helpers
- build a huge component tree
- run heavy logic in the view
Rails renders MANY Ruby objects.
And Ruby objects cost memory.
The response climbs back up the stack
After rendering:
- the response goes back through middlewares
- cookies get serialized
- session gets saved
- gzip may be applied
- logs are written
- metrics are collected
Tools like:
- New Relic
- Datadog
- Skylight
- Sentry
live by intercepting this cycle.
Modern observability totally depends on this pipeline.
The big beginner mistake
The mistake is thinking a web framework is:
Request → Controller → Response
It isn't.
In real life there's:
kernel → socket → scheduler → thread
↓
middleware → connection pool → IO
↓
serialization → cache → GC → database
↓
render pipeline → response
And Rails abstracts all of this VERY well.
So well…
…most people never actually learn it.
The big shift
The day you understand the request lifecycle…
…you stop "using Rails".
And you start understanding:
- web applications
- concurrency
- throughput
- observability
- latency
- architecture
- real trade-offs
And then finally:
- profiling makes sense
- logs make sense
- APM makes sense
- pgBadger makes sense
- N+1 makes sense
- connection pools make sense
Because now you can see the whole request.
Not just the controller.
Conclusion
Rails was never magic.
The community just romanticized abstractions.
Underneath:
- it's still TCP
- it's still IO
- it's still SQL
- it's still memory
- it's still concurrency
- it's still Linux
- it's still systems engineering
And honestly?
The more senior you get…
…the less "magical" Rails feels.
And the more elegant it becomes.
