Explore how DHH and Ruby on Rails popularized convention over configuration, speeding up web apps, reducing boilerplate, and enabling faster product iteration.

Before Rails, building a web app often started with a long “setup tax.” You picked (or built) a folder structure, decided how URLs should map to code, wired up database connections by hand, and wrote the same glue code repeatedly. None of that shipped a feature—but it still consumed days.
A second drag was decision fatigue. Even small choices—file naming, where to put business logic, how to organize tests—had to be renegotiated again and again. Multiply that by a team and a growing codebase, and speed gets lost in meetings, documentation, and inconsistent patterns.
Rails popularized a simple promise: if you follow the common way of doing things, you shouldn’t have to configure everything. That’s “convention over configuration” in plain language.
Instead of asking you to specify every setting, Rails assumes sensible defaults:
When the framework already “knows” what you mean, you write less boilerplate and get to working screens sooner.
Speed wasn’t just about writing fewer lines of code. Conventions changed how quickly you could iterate:
This article focuses on that practical impact—how Rails conventions shorten the path from idea to running feature—without turning the story into hero worship. The point isn’t that one person or one framework is “magic,” but that good defaults remove friction from building products.
David Heinemeier Hansson—usually shortened to DHH—is the creator of Ruby on Rails. He built Rails while working at 37signals (now Basecamp) and published it as open source in 2004. That timeline matters because Rails wasn’t designed in a vacuum: it was shaped by the day-to-day pressure of shipping a real product.
Rails began as an internal framework used to build Basecamp. Instead of starting with a grand theory of how web frameworks should work, DHH pulled out the parts that were repeatedly useful: routing requests, organizing code, talking to a database, rendering HTML, and handling common web patterns.
Because it came from production needs, Rails focused on removing friction from routine tasks. It wasn’t trying to be everything for everyone—it was trying to make the common case fast.
Rails is often described as “opinionated.” In plain terms, that means Rails makes decisions for you—especially about structure and defaults—so you don’t have to.
For example, it nudges teams toward:
Those opinions reduce the number of choices you need to make before you can build something useful. Fewer early decisions usually means faster first versions and quicker iteration.
Rails didn’t just ship code; it created a shared way of talking about web apps. When thousands of teams follow the same conventions, you get a common vocabulary (“models,” “migrations,” “scaffolds,” “RESTful routes”) and transferable skills. That lowers onboarding time, makes help easier to find, and turns “how do we do this?” into “Rails already has a standard for that.”
Rails popularized a straightforward idea: for the common case, the framework should guess correctly so you don’t have to spell everything out. You get sensible defaults for how code is organized, how components connect, and how data maps to the database. You only configure what’s unusual.
“Convention over configuration” means Rails assumes you’re building a fairly typical web app—users, pages, forms, database tables—and it provides a standard way to do each of those things. If you follow the conventions, pieces “just line up” with minimal setup.
That’s different from configuration-heavy approaches where your first steps are often creating and maintaining a web of settings: extra files, manifests, or endless flags describing what your app already implies. Conceptually, you spend time telling the framework what you want before you can start building.
Rails uses predictable naming and placement to connect parts automatically:
Article, Rails expects a database table called articles.ArticlesController maps to URLs and actions related to articles.app/models/article.rb and app/controllers/articles_controller.rb.Because Rails knows where to look and what to call things, you avoid repetitive wiring. You write the feature, not the glue.
The cost is less freedom up front: if you want a custom structure or unconventional naming, you may need extra configuration (and you’re swimming against expectations). The benefit is speed and consistency—especially when multiple people work on the same codebase and rely on shared patterns.
Rails popularized MVC for a broad audience not by inventing it, but by making it feel obvious. MVC is easiest to understand when you think of it as three responsibilities:
The speed boost comes from Rails conventions that connect these layers automatically. If you create a PostsController, Rails expects it in app/controllers/posts_controller.rb. A Post model lives in app/models/post.rb. Views for that controller naturally land in app/views/posts/.
Because names and locations are predictable, Rails can infer a lot: routes map to controller actions, controller actions render matching view templates by default, and models map to database tables with conventional naming. You can override behavior—but you don’t have to negotiate every decision up front.
When every Rails app is organized similarly, onboarding gets faster. Teammates know where to look for a validation, where a template should live, and how a feature is likely shaped. That reduces “where is this code?” time and increases “ship the change” time.
A common guideline is fat model, skinny controller: keep controllers simple and push reusable rules into models. It helps avoid copy-pasted logic across endpoints.
The limit: not all business workflows belong in a single Active Record model. As apps grow, teams often introduce service objects or form objects to keep models from becoming dumping grounds while still keeping controllers tidy.
Rails scaffolding is a shortcut for creating a working baseline of a feature—fast. With a single command, Rails can generate a model, database migration, controller actions, routes, and basic views for Create/Read/Update/Delete (CRUD). The result isn’t a slide deck or a mockup; it’s a running slice of the app you can click through.
A scaffold wires together the “boring but necessary” parts so you can prove the idea quickly:
This matters because product iteration often gets stuck on setup work. Scaffolding helps you bypass that and start learning from something real.
Scaffolding is best seen as a prototype generator. The default views are plain, the UX is minimal, and the code reflects generic assumptions. That’s a feature, not a flaw: it nudges you to treat scaffolds as a starting point, not “the design.”
A common healthy workflow is:
Generated code still needs review. You’ll want to add tests, tighten authorization, and improve error handling. And because scaffolded pages are utilitarian, plan time for real UX work—copy, layout, accessibility, and edge cases. Scaffolding accelerates the first draft; it doesn’t replace engineering judgment.
Rails didn’t just introduce conventions in theory—it wired them into day-to-day work through generators, migrations, and naming rules that reinforce each other. That cohesion is a big reason teams can iterate quickly without the codebase turning into a pile of one-off decisions.
A Rails generator doesn’t merely “create files.” It creates expected files in expected places with expected names—models in app/models, controllers in app/controllers, tests in the right folder, and, crucially, a migration that updates the database structure.
Because Rails leans on naming (like User mapping to a users table), the generated pieces tend to connect with minimal extra wiring. Less time is spent deciding where something goes or what to call it, and more time is spent shaping the feature.
Migrations treat the database schema as something that evolves alongside the application. Instead of “the database is done, now we code,” Rails encourages a steady rhythm: build a feature, adjust the schema, learn from real usage, then refine.
Each migration is a small, timestamped step that can be reviewed, tracked in version control, and replayed across environments. That makes iterative product changes—adding fields, tweaking constraints, introducing new tables—much less risky over time.
Say you want to add a role to users:
rails g migration AddRoleToUsers role:stringrails db:migrateUser.That’s a tight loop: the schema change and the application change move together, so you don’t end up with “mystery columns” or code that assumes data that isn’t there.
Speed only stays sustainable if migrations are kept clean: avoid editing old migrations after they’ve shipped, write reversible changes when possible, and treat schema changes like production code—with reviews and careful naming. Rails makes iteration easy; teams keep it safe by staying consistent.
“Don’t repeat yourself” (DRY) is the simple idea that your app should have one clear source of truth for each piece of knowledge. In a web app, repetition often sneaks in when the same concept is spelled out in multiple places—routes, controller logic, view templates, and even database queries.
Imagine you’re building a basic blog with Post records. Without DRY habits, you might copy the same “find the post by ID” code into show, edit, update, and destroy. Rails nudges you toward a single shared method instead:
before_action :set_post, only: %i[show edit update destroy]
def set_post
@post = Post.find(params[:id])
end
That’s DRY in action: one change (say, switching to Post.friendly.find) updates every action.
Rails conventions make DRY easier because different layers “agree” on naming and structure. When you use RESTful routes (resources :posts), Rails expects a PostsController with standard actions, and it looks for views in predictable paths like app/views/posts/show.html.erb.
Because those pieces line up, you write less glue code. A link helper like link_to @post.title, @post works because Rails can infer the correct route from the model instance. Partial naming conventions (render @posts) can automatically pick posts/_post for each item.
Pushing DRY too far can hurt readability: tiny abstractions, metaprogramming, or “one method that handles everything” may save lines but cost understanding. A little repetition is sometimes the clearest option—especially in views and business logic. The goal is maintainability, not minimal character count.
Rails is famous for optimizing “the happy path”: the most common way teams build and ship a typical database-backed web app. It assumes you’ll have users, forms, validations, CRUD screens, routes, emails, background jobs, and a relational database—and it makes those flows smooth and predictable.
Happy path development means you spend most of your time doing the “normal” thing, without wrestling the framework. When you name a model Order, Rails expects an orders table, knows where the file lives, and can infer how controllers, views, and routes should line up. You’re not proving every choice; you’re following a well-worn trail.
New projects have an endless list of early decisions: folder structure, naming, configuration style, testing setup, how to handle forms, where to put business logic. Rails deliberately answers many of those questions up front.
That matters because decision fatigue is real: the more small choices you make, the slower you move—and the harder it is for teammates to predict what you did. Rails defaults create a “good enough” starting point, so you can begin building features immediately and only customize when the need is clear.
Product iteration is about running more (and better) experiments: shipping a small change, watching what users do, and adjusting quickly. Rails supports that rhythm by making it easy to:
Shorter build times lead to shorter feedback loops—and that’s where speed turns into learning.
Rails defaults can feel restrictive when your problem is unusual: highly specialized domains, extreme scale requirements, strict regulatory constraints, or unconventional data storage and workflows. In those cases, you may spend more time bending conventions than benefiting from them. The key is recognizing when defaults are helping—and when you should intentionally step off the trail.
Rails didn’t just speed up individual developers—it sped up teams. The “Rails way” is really a set of shared expectations: where files live, how classes are named, how requests flow through controllers to views, and how data is modeled. When most projects follow the same patterns, teammates spend less time decoding structure and more time shipping features.
Conventions show up in small, repeated decisions:
app/models, controllers in app/controllers, views in app/viewsPostsController manages Post)index, show, create, etc.)None of these is magical alone. Together, they reduce the number of “How do we do this here?” conversations.
When a new developer joins, Rails conventions act like signage in a building: you can find what you need without asking for a guided tour. That cuts onboarding time and lowers the risk of knowledge being trapped in one person’s head.
Conventions also improve code reviews. Reviewers can focus on product logic, edge cases, and performance instead of debating folder structures or inventing new patterns. When there’s a default, the burden of proof shifts: you only argue when you’re deviating for a good reason.
The flip side is that teams can follow conventions out of habit. It’s healthy to justify exceptions—especially for unusual domains, scaling constraints, or security requirements—while still using Rails defaults as the starting point.
Rails earned its “batteries included” reputation by treating a web app as a whole product, not a puzzle of disconnected parts. Instead of asking you to assemble a stack for routing, templating, background work, email, file uploads, security defaults, and testing, Rails ships with a coherent set of tools designed to work together from day one.
Most web products hit the same milestones early: user accounts, forms, validations, database changes, sending emails, handling errors, and deploying reliably. Rails leans into those repeatable needs with built-in patterns and sensible defaults. That means teams spend less time debating which library to pick or how to wire it up, and more time shaping features and polishing the user experience.
When the “standard” path is already paved, shipping becomes a matter of filling in application-specific details—models, rules, and UI—rather than inventing architecture for every new project.
Speed isn’t only about having tools; it’s about how well they fit together. In a mix-and-match setup, a surprising amount of effort goes into translation layers: adapting one library’s configuration format to another’s expectations, reconciling competing conventions, or duplicating concerns like logging, instrumentation, and error handling.
Rails reduces that friction by integrating its components around shared conventions. Data validation, database persistence, and rendering views follow consistent rules. Errors surface in predictable ways. Configuration tends to live in familiar places. The result is less “glue code” and fewer one-off decisions that slow down delivery and complicate maintenance.
The flip side of tight integration is that upgrades can have wider blast radius. When Rails changes defaults or deprecates an approach, multiple parts of an app may need attention at once. Teams often accept this cost because the day-to-day gains in delivery speed and coherence outweigh occasional upgrade projects—but it’s a real factor to plan for.
Rails conventions are a speed multiplier when you stay close to them. But the same conventions can slow you down when your app starts bending the framework into shapes it wasn’t designed to make effortless.
A few practical “smoke signals” usually show up early:
When this happens, the time you saved via convention often gets paid back with interest in onboarding, debugging, and code review.
Rails can scale, but it doesn’t magically remove performance work. Convention-friendly code can still become slow if you’re not watching queries, caching, background jobs, and object allocations.
Where conventions can hurt is when you assume defaults are “always optimal.” For example, naive Active Record usage can create N+1 queries, and default caching decisions may be too generic for your hottest endpoints. Scaling typically means measuring, then adjusting deliberately.
Rails helps you ship and learn quickly—but quick changes can accumulate inconsistencies: model bloat, callback chains, or business logic drifting into controllers. Conventions reduce friction; they don’t automatically enforce clean boundaries.
Customize deliberately:
The goal is to earn flexibility without turning “convention over configuration” into “configuration everywhere.”
Rails accelerated teams by standardizing structure: where things go, what they’re called, and how the pieces connect. A similar speed dynamic is showing up today with vibe-coding platforms like Koder.ai, where the “default” is less about folder layout and more about turning intent into a working application through chat.
Koder.ai focuses on the same outcome Rails optimized for: a shorter path from idea to a running feature. Instead of hand-wiring the first version, you describe what you want in a conversation, and the platform helps generate and iterate on a real app (web, backend, or mobile). You can then refine like you would after a Rails scaffold—adjusting behavior, permissions, and UX—while keeping the feedback loop tight.
The underlying lesson is consistent: teams move faster when the early, repeatable decisions are made once (by a framework or platform) and everyone can build on top of those defaults.
Rails is fastest when you treat its conventions as a default operating system for your product team—not a set of suggestions you debate on every ticket. The goal is to preserve momentum while still leaving room for intentional exceptions.
Start by leaning into Rails’ “expected” choices: conventional naming, standard folder structure, RESTful routes, and the built-in way of handling forms, validations, and background jobs.
As a simple habit, ask: “Can a new teammate predict where this code lives and how it behaves?” If the answer is yes, you’re probably staying close to convention—and future changes will be cheaper.
Follow conventions until there’s a measurable need not to. “Measurable” can be any of the following:
If you can’t point to one of those, prefer the Rails way. It keeps the system understandable and makes iteration smoother.
Every team eventually makes a few deliberate deviations—custom service objects, alternative form patterns, specific routing conventions, or a standard approach to querying.
Capture these in a short “team playbook” (a single page in your repo). Include:
This prevents exception creep and helps new hires ship confidently.
Conventions aren’t just a coding preference. Used well, they’re a product strategy tool: they reduce decision overhead, shorten feedback loops, and let your team spend more time learning from users than arguing about structure.