Ruby on Rails (aka Rails) is a Ruby framework for database-backed Internet applications. I've now used Rails for two different applications and been involved with two others as an associate. For an upcoming book called Java to Ruby (see Resources), I've interviewed many Rails developers (people who have both succeeded and failed with the framework), the framework's founder, and the lead author of the flagship Rails book Agile Web Development with Rails (see Resources). I'm starting to understand why the Ruby on Rails framework is so successful.
Debates about Rails in the Java community have been intense and show no sign of dying down any time soon. Rails proponents boast of incredible productivity, with some claims of 10 to 1 over Java development. As a Java programmer, your knee-jerk response is to dismiss any wild productivity claims because you've likely heard them before and been disappointed. Java advocates increasingly insist that Ruby on Rails is a toy that can't scale, produces bad code, and won't work beyond the simplest applications. But as Rails praise keeps popping up -- often from credible sources -- a more prudent course might be to understand what Rails does well and to bring those ideas back to the Java platform. In this article, I'll explore the core features -- the secret sauce -- that are the essence of Rails's great productivity.
The Ruby on Rails framework is not a typical application-development framework by any stretch of the imagination. Rails founder David Heinemeier Hansson often calls the framework opinionated software, and he enjoys breaking long-standing conventions. David has made strong philosophical decisions and strictly follows them throughout the framework. Some of the core opinions pervasive within Rails are:
Seamless integration: Rails makes brilliant use of the best features of the Ruby language. It extends Ruby in ways that often make it hard to tell where Ruby ends and Rails begins. You also see nice integration between Active Record (the Rails persistence engine) and the model-view-controller (MVC) framework. For example, you can write three lines of code, create a table, and then immediately generate a user interface for that model.
- Convention over configuration: To keep perfect flexibility, Java frameworks maintain massive, pervasive configuration files. Rails rejects this strategy. It assumes a common project directory structure and simple, common naming conventions for methods, classes, tables, and columns to infer much of what's configured in Java applications. As a result, Rails applications need a fraction of the configuration code of Java counterparts, often by a factor of 10 or more.
- Low repetition: Don't Repeat Yourself, or DRY, is a common buzzword within the Rails community. Rails framework committers seek to abstract out repetitive tasks with methods that often look like extensions of the Ruby language. As you saw in the third article in this series, Rails's metaprogramming strategy makes each line of code work harder.
- Immediate feedback: With Rails, most of what you do can give you immediate feedback. You write a line of code and save, and your change is active when you load your next Web page. Migrations can instantly show you changes after updates to your database.
The argument against wild productivity claims usually goes something like this: If I've got a decent hammer, I'd be hard-pressed to find another hammer to make me twice as productive, let alone 5 to 10 times more productive, because hammers have evolved over thousands of years. But people who compare Ruby on Rails with a mix of general-purpose Java frameworks are missing the point. You can be 10 times more productive for some problems by radically changing the nature of the tool. Professional framers now use nail guns that can drive in dozens of nails in the time it takes to hammer in one nail. Like nail guns, Rails is specialized. It's a framework written with a laser focus on a single niche: new database-backed Web applications.
I'd guess that as many as half of the applications built today are database-backed, Web-enabled applications. So Rails is definitely a niche product, but its niche is large and important. Specializing in this space gives Rails huge advantages that account for the big splash that it has made. By focusing on projects in this niche, Rails designers can take some shortcuts that other frameworks cannot or should not take. This specialization often sacrifices flexibility for simplicity.
New, database-backed applications suggest a wrapping approach over a mapping approach. As you saw in the first article in this series, Rails tools assume conventions in the data model. Rails applications need a fraction of the model code found in Java applications. If you're creating your schema especially for your Rails applications, this philosophy works well. If you're trying to shoehorn a legacy schema into Rails, things will go less smoothly.
Web-based applications allow a similar set of optimizations. When you know an application is Web-based, you know the general structure of the application and the major components that it's likely to need. The following features are enhanced in Rails because the Rails focus is on Web-based applications:
Model-view-controller: The Rails MVC framework -- called Action Pack -- is customized for Web-based access and implements a well-known design strategy called Model 2 (see Resources). The Rails version has optimized integration between the controller and view that minimizes configuration and automatically makes controller instance variables available to the view.
Project directory structure: Rails applications all have the same project structure, with directories to handle application code, database configuration, public static files, and scripts for managing Web servers and Web-based functional testing.
Architecture: The Rails framework simplifies architecture by providing out-of-the box scripts that generate application components that all adhere to common architectural goals, such as page-level and fragment-level caching; two-tier design; and environments for test, development, and production.
- Tooling: The Rails tooling is specialized for the Web. The logging support, breakpointer, profiler, and testing frameworks are all tailored for Web-based applications and enabled for two-tier operation.
But nail guns will never replace hammers, and we'd be foolish to expect complete replacement. Hammers will always do things that nail guns can't. Rails will never be a tool for enterprise integration, object-relational mapping, or full-stack Web services. The best you can hope for from Rails is a specialized tool that fills its niche well.
As you start to dig under the covers, you begin to understand how radically different the Rails developer experience can be. A rapid feedback loop, interactive consoles at every turn, and convention over configuration all enhance the developer experience with dimensions not frequently available in Java frameworks.
One of the most important factors in developer productivity is the overall feedback loop. The feedback loop is the amount of time between making a change in code and seeing the results in the execution of your application on the screen. In Rails, you get instantaneous feedback as you code. That feature is most notable when you make changes to your Ruby code. You can immediately load a browser page to see the results of your changes. Because I don't need to compile or deploy during development, I tend to make only tiny programming changes before I reload my browser or execute my test cases. Just about every Java developer who begins to use Rails codes in smaller chunks.
You might expect a rapid feedback loop that's friendly to the developer experience to be hostile to production applications. After all, the frequent class reloads that enable the rapid feedback loop should slow production applications to a crawl. But Rails works around this problem by giving you different environments for deployment and development. The development environment forces frequent class reloads at the expense of application performance, and the production environment reduces reloads to a bare minimum, providing a fast end-user experience at the expense of a rapid feedback loop for the developer.
The interactive experience of Ruby also contributes to Rails. You might expect debugging a Rails application without a full IDE to be a painful experience. It's not. Rails provides two features that simplify debugging. One of them is the breakpointer, which lets you add the
breakpoint keyword to your source code.
To see the breakpointer in action, create a simple Rails application, generate a controller, start the server, and start a breakpointer. Make sure you have access to the breakpointer window, because you'll use it when Ruby encounters the breakpoint. Using Windows, the sequence looks like this:
>rails sample >cd sample >ruby script/generate controller samples >start ruby script\server >start ruby script\breakpointer
If you're running in UNIX® or Mac OS X, just make sure that your server starts in a separate process.
Type or paste the following code into the app/controllers/samples_controller.rb file:
class SamplesController < ApplicationController def index breakpoint @session[:message] = "hi, mom" render_text "Showing index" end def show render_text @session[:message] end end
Test the code by loading the pages localhost:3000/samples and localhost:3000/samples/show.
When Rails executes the breakpoint, the application hangs. The breakpointer window opens a Ruby interpreter with the environment having the current state of the controller. You can then issue Ruby commands to query the state of your session, execute methods, and query variables:
> puts @session[:message] -> hi, mom
This nice touch doesn't give you a full debugger, but you do have some capabilities that Java debuggers don't give you, including access to a full interpreter and the ability to execute methods on your application.
The second feature that simplifies debugging is the Active Record console. In the first article in this series, you saw that Rails also comes with a script that lets you work with your persistent objects in an interactive Ruby interpreter window. I've often wanted such a capability within my Java applications. You can code a persistent model, make changes to the database through the model, and then run some database queries to see their impact on the system. It would be nice to be able to query Hibernate objects in a similar setting.
Convention over configuration also leads to "aha!" moments for new Rails developers because the controller and model code is so concise. You saw in the first article that by leaning on the Rails environment, you can get some pretty advanced behavior out of some very thin classes -- by adopting Rails naming conventions and letting Rails infer your application's connection points instead of configuring them directly. To review, a
Person object with many attributes and a one-to-many relationship with a department might look like this:
class Person < Active Record::Base belongs_to :department end
No configuration is necessary because Rails infers the name of the table (
people), the name of the object identifier and primary key (
id), the name of the related table (
departments), the name of the foreign key (
department_id), and the name of the foreign class (
department.rb) based on naming conventions. The code remains simple, light, and very easy on the eyes, whether you're writing, reading, or maintaining it. Intentions are immediately clear.
I don't recommend that we build a better Rails in the Java language. Instead, Java developers should learn some lessons from the Rails framework and seek to build or enhance Java frameworks to do all of the following:
- Enable hot deploy where it will shorten the development feedback loop or support frameworks that enable hot deploy. This priority should be much higher on the Java side than it is now.
- Use less XML and more convention. Conventions don't rule out configuration because you can use conventions to specify meaningful defaults and configuration to override convention. Using this approach, as Rails does, you get the best of both worlds: concise code with less repetition without sacrificing flexibility.
- Work to incorporate more scripting languages, including BeanShell (see Resources), for exploring Java classes during the debugging process.
- Use the right tool for the job. Don't necessarily reach for Hibernate just because you need persistence or reach for Struts just because you need a Web application.
By incorporating the best features of other programming languages, you might not reproduce Rails, but you definitely can improve the Java experience.
Beyond Java (O'Reilly, 2005): The author's book about the Java language's rise and plateau and the technologies that could challenge the Java platform in some niches.
Java To Ruby: Things Your Manager Should Know (Pragmatic Bookshelf, 2006): The author's book about when and where it makes sense to make a switch from Java programming to Ruby on Rails, and how to make it.
Agile Web Development with Rails (Pragmatic Bookshelf, 2005): A popular book on Rails programming.
"Rolling with Ruby on Rails" and Learn all about Ruby on Rails: Learn more about Ruby and Rails, including installation procedures.
Active Record: Active Record is the persistence framework for the Ruby on Rails framework.
"Ruby on Rails and J2EE: Is there room for both?" (Aaron Rustad, developerWorks, July 2005): This article compares and contrasts some of the key architectural features of Rails and traditional J2EE frameworks.
"Understanding Java ServerPages Model 2 architecture": Many Java and non-Java frameworks use an architecture called Model 2.
"The Builders of Basecamp": Ruby on Rails was born from Basecamp.
BeanShell: BeanShell is a small, free, embeddable Java source interpreter with object scripting language features, written in the Java language.
Get products and technologies
Ruby on Rails: Download the open source Ruby on Rails Web framework.
Ruby: Get Ruby from the project Web site.
Bruce Tate is a father, mountain biker, and kayaker in Austin, Texas. He's the author of three best-selling Java books, including the Jolt winner Better, Faster, Lighter Java. He recently released Beyond Java.. He spent 13 years at IBM and is now the founder of the RapidRed consultancy, where he specializes in lightweight development strategies and architectures based on Java technology and Ruby on Rails.