DHH vs. Palkan: The Rails Philosophy Cage Match Nobody Asked For


There are two wolves inside every Rails developer. One wolf wants to throw everything into a model and call it a day. The other wolf wants a service object for making toast. These wolves have names: DHH and Vladimir Dementyev (palkan).

Both of them are brilliant. Both of them love Rails. They just happen to disagree on the fundamental question of how much architecture a grown-up application actually needs.

The Majestic Monolith vs. The Layered Cake

DHH’s position is simple and persuasive: Rails gives you everything. Models, controllers, views, concerns, jobs. That’s the menu. You don’t need a service layer. You don’t need interactors. You don’t need a ProcessOrderAndMaybeSendAnEmailService. You need a fat model and the courage to let it be fat.

His argument from the Rails Doctrine boils down to: convention over configuration means the framework already made the good decisions for you. Stop inventing new drawers to put things in.

Palkan’s response, delivered calmly from the Evil Martians mothership, is essentially: “That works great until your app has 400 models and your User class is 3,000 lines long and someone on your team just quit.”

He advocates for layered architecture — extracting service objects, form objects, query objects, and presenters as your app grows. Not because Rails is wrong, but because a ten-person team working on a seven-year-old monolith has different needs than a solo founder shipping an MVP.

The Testing Divide

This is where it gets spicy.

DHH famously wrote TDD is Dead. His stance: write system tests, don’t mock everything, don’t chase coverage numbers. Integration tests that hit the real database are better than a thousand unit tests for objects that only exist to satisfy a testing philosophy.

Palkan, meanwhile, built TestProf — an entire toolkit for making your Rails test suite faster and more efficient. His position isn’t “write more tests.” It’s “the tests you’re writing are slow because you’re doing it wrong.” let_it_be instead of let. Factory cascades are killing you. Profile your tests like you profile your app.

Here’s the beautiful tension: DHH says stop overthinking your tests. Palkan says start profiling your tests. One says fewer tests, better sleep. The other says same tests, but 10x faster, also better sleep.

They’re both right. It depends on whether your test suite takes 2 minutes or 45 minutes.

Concerns: The Forever War

DHH loves concerns. He puts them in everything. A concern is just a module you mix into a model to organize behavior. What’s not to love?

# DHH reaches for this instinctively
module Commentable
  extend ActiveSupport::Concern

  included do
    has_many :comments, as: :commentable
  end

  def recent_comments
    comments.order(created_at: :desc).limit(10)
  end
end

Palkan doesn’t hate concerns. He just observes that when your User model includes 14 of them, you haven’t simplified anything — you’ve just distributed the chaos across multiple files. You still have a God Object. It just has better posture.

His alternative: pull that behavior into a standalone object with an explicit interface.

# Palkan might suggest something like this instead
class RecentComments
  def initialize(commentable)
    @commentable = commentable
  end

  def call(limit: 10)
    @commentable.comments.order(created_at: :desc).limit(limit)
  end
end

Is this more code? Yes. Is it easier to test in isolation? Also yes. Is it worth it for a side project? Absolutely not. Is it worth it when six developers are stepping on each other inside User? Probably.

The Punchline

The real joke is that DHH and Palkan agree on about 90% of things. They both think Rails is great. They both think most apps are over-engineered. They both think you should ship working software instead of debating architecture on Twitter.

The 10% they disagree on is when your app outgrows the defaults. DHH says the defaults scale further than you think. Palkan says they don’t scale as far as you hope.

The practical answer, which satisfies nobody at a conference talk:

  • Small team, young app? Follow DHH. Fat models, concerns, system tests, ship it.
  • Big team, legacy app, slow tests? Follow palkan. Extract objects, profile your suite, add structure where it hurts.
  • Solo developer at 2 AM? Follow neither. Just get it working and go to bed.

The beauty of Rails is that it’s flexible enough for both philosophies. The beauty of this debate is that it forces you to think about why you’re reaching for an abstraction instead of just cargo-culting one in because a blog post told you to.

Including this one.