Rails developers often see their Java counterparts as stodgy, laboring relics of the past. And Java visionaries often paint Ruby on Rails as a toy with no place in any serious software development. As a consultant who has worked extensively with both approaches, I know that the truth lies somewhere in between. As this Crossing borders series closes, I'd like to do one more comparison. Rather than look at a particular technology or language, I'll focus on my current project and compare it with the Java projects I've worked on in the past. (Along the way, I'll point you to previous Crossing borders installments that dig more deeply into relevant topics.) This first-hand account should give you an appreciation of the trade-offs and potential net gains involved in using Rails for green-field development of database-backed Web applications.
Neither Ruby on Rails nor any Java framework is the right solution to every problem. To have the best possible chance for success, you need to take a long, hard look at your business problem, understand the surrounding assumptions, and know your team. Only then can you choose a good language to solve your problem.
Last year, Arvato Systems hired me to lead a team that would build ChangingThePresent.org, a new marketplace that would bring nonprofits and donors together. Like many Internet companies, we would present tangible products that our customers could then buy. Unlike other companies, the "products" represent giving opportunities, such as an hour of a cancer researcher's time for $50, helping a blind person see for $30, or preserving an acre of the rain forest for a month for $20. We had two major challenges: an ambitious schedule and long-term complexity.
We were to start development in September, and we would need a compelling site up by November to have a real shot at getting traffic from the Christmas season. (We eventually missed the November target by two weeks.) From other bids, we knew that a Java-based solution would likely take 6 to 18 months to complete. So productivity was a huge concern, pointing away from a Java deployment.
From looking at our competition and projections, we knew we'd be able to drive traffic to the site on the order of millions of hits per day. We'd need to drive hundreds of thousands of daily hits to be successful, so scalability was also a concern. This concern pointed to a Java deployment.
Finally, we knew that even after the original site was launched, we would only be just beginning. We would launch with only three percent of our total plans. So the technology we chose would need to scale in terms of complexity as well as load. I believed that the Ruby language -- with its support for higher-level languages and features such as open classes -- and the Rails framework -- with reduced configuration requirements and a much simpler, integrated programming model -- would scale better in terms of complexity.
Though the time and scalability challenges worked against us, other factors worked in our favor. We had a completely blank slate: we could choose any technology, and any team, that we wanted. We could define the project, culture, and full technology stack. We had complete freedom to choose.
The Java language is an excellent general-purpose language. It is always pushing into new frontiers, such as embedded systems and mobile devices. The Java language also excels in the integration of broad concerns. It is high-performing, popular, and well supported in the marketplace. But as you've seen in this series, the Java language is not necessarily the best choice for green-field development of database-backed Web applications (see the article "Web development strategies in dynamically typed languages").
By contrast, the Ruby on Rails framework is new. Not many people have used Rails for high-traffic sites, and experience with multiyear development projects is almost nonexistent. Still, Rails is an extremely productive framework for database-backed Web applications. In the end, our aggressive schedules led us to choose Ruby on Rails, though we were concerned about the dearth of experience for Rails in long-term projects or high-volume deployments.
Once we'd made the decision, we found it easy to recruit talent for the project. We also found that our early productivity was fantastic -- even greater than we'd expected. We did have some early problems with stability, so we redoubled our testing efforts (see the articles "Testing in integrated frameworks, Part 1" and "Testing in integrated frameworks, Part 2"). We've since improved our stability tremendously.
Each framework designer works with a set of assumptions that make up that framework's overriding philosophy. Learn to live within that philosophy's constraints, and you're a happy programmer. Fight them, and you'll be smashing your equipment in frustration. Rails and Java frameworks have vastly different philosophies.
Rails is an integrated framework that heavily leverages the dynamic nature of the Ruby language (see the article "What's the secret sauce in Ruby on Rails?"). Rails developers emphasize features of the framework for productivity rather than tools and usually take a simplistic view of Web architecture, as you've seen in past articles of this series. Java designers must often craft together their environment piecemeal, choosing persistence, Web, and integration tiers independently. They often rely heavily on tools to simplify core tasks. Web architectures tend to be more complex by design.
Where Java frameworks tend to solve one small problem such as persistence or view organization, Rails is an integrated environment. The upside for Rails developers is that they don't need to solve problems of integrating many different frameworks. Most Hibernate developers have fallen into the trap of prematurely closing connections with Java Web frameworks. The Rails view frameworks are built from the ground up to integrate with ActiveRecord, the Rails persistence framework. You'll find similar experiences when you look at the Rails frameworks for Web services, configuration, and plug-ins. Java programming supports vastly different frameworks, with different integration strategies for all of these things.
The upside for Java developers is choice. If you need to build a framework from scratch, you might considering using a SQL-based solution for database integration such as iBATIS or one of the many JDBC-based wrapping frameworks in Java programming. If, instead, you were starting with a legacy schema, you might decide to use an object-relational mapping framework like Hibernate. If you're using Rails, by contrast, you have one major choice: ActiveRecord. That means that Java frameworks, providing greater choice, sometimes make better solutions for integrated development projects. But because we were developing a green-field project, choice was not as much of an issue.
The next major part of the Rails philosophy is the dynamic programming language (see the article "Typing strategies beyond the Java model"). Java tools tend to make effective use of the extra information provided by the Java typing model. Tools can recognize errors and effectively restructure, or refactor, code. Rails also effectively leverages the advantages of the programming language. Ruby is an ideal language for building domain specific languages (DSLs) (see the article "Domain-specific languages in Active Record and Java programming"). Rails heavily engages DSLs to do everything from building relationships between model objects to specifying custom components such as state machines or uploadable images. Dynamic languages are often more concise, so Rails projects are much more terse than their Java counterparts, letting users express both code and configuration more concisely. At ChangingThePresent.org, we've noticed that our top programmers can be more productive, but we do need to recruit developers with more experience. I'm happy with that compromise.
The typical Java programmer is religiously passionate about his IDE, and with good reason. The IDE gives him syntax completion, fixes trivial errors, and provides incremental compilation for a quicker cycle between coding, compiling, deploying, and testing. In recent years, development environments began taking even more advantage of the information provided by the compile cycle and static typing. IDEs now edit the abstract syntax tree (AST) instead of, or in addition to, a text representation of the code. This strategy allows powerful code refactoring tools, which are much more difficult to achieve using the same methods for statically typed languages.
Static typing does allow better tooling, but there's a downside, too. Enforcing static typing usually requires a compiler, and that compile step can absolutely crush productivity. With Rails, I can change a line of code and reload the browser, immediately seeing the results of the change. In contrast to Java developers, most Ruby developers use only a good editor. TextMate, the most popular Ruby on Rails editor, provides syntax highlighting, some code completion, and good template support for frequently used constructs. And the editor is more than enough when you throw in all of the simple Ruby-based scripts that serve as the basic toolkit for Rails. Rather than a full debugger, I can use the breakpointer script, which can stop a given application and place me in a Ruby interpreter where I can invoke methods, inspect variable values, and even modify code before resuming execution.
The typical Java architecture on the Web side has a layer for domain objects, data access objects, a facade layer that provides a business-level API, a controller layer, and a view layer. This architecture is a little more complicated than the classic model-view-controller architecture, which was first pioneered using the Smalltalk language. By contrast, Ruby on Rails uses a single model layer using the ActiveRecord design pattern, a controller layer, and a view layer. We like the Rails approach, hands down. It's much more concise and leaves less room for additional complexity or errors.
Java frameworks usually use XML configuration liberally, while Rails primarily uses conventions to avoid configuration where possible. Where the programmer must specify configuration, Rails usually relies on Ruby, often in the form of a DSL, to provide configuration. For green-field development, I find convention over configuration makes sense. The strategy saves me many lines of code and simplifies the code that I do need to write. We estimate that we have about one tenth of the configuration that would be specified in a typical Java application. We sometimes lose a little flexibility, but not enough to offset the tremendous savings.
All in all, the philosophies of the Rails framework fit the problem I needed to solve for ChangingThePresent.org. The integrated stack lets me get more functionality out of the framework without needing to do excessive integration myself. The convention-over-configuration philosophy saves me time as I try to configure the site. The dynamic language gives my seasoned developers more power and flexibility, while also letting them express more powerful ideas with fewer lines of code. The framework fits my team's abilities as well as the business problem we're solving.
The most popular persistence frameworks for the Java and Ruby languages illustrate the differences between the Java and Ruby experiences better than any other feature. Java developers usually reach for Hibernate, an object-relational mapping framework. With Hibernate, you take an existing model and an existing schema and express a map between them using annotations or XML. Hibernate classes are plain old Java objects (POJOs), with each object derived from a common base class. Most configuration is explicit, using annotations, XML, or some combination of both.
ActiveRecord, instead, is a wrapping framework, meaning each class is a wrapper around an existing class (see the article "Exploring Active Record"). ActiveRecord automatically adds features to model objects based on the contents of the associated table, such as an attribute for each column in the table. All classes inherit from a common base class. ActiveRecord primarily infers configuration from common conventions. For example:
- ActiveRecord infers the table's name from the plural form of the class's name.
- The name of the primary key is
- The sort order for lists is determined from a field called
Object-relational mapping is the best solution when you have a legacy schema, perhaps not defined with an object model in mind. But when you can design the database schema explicitly for your application, you often don't need a mapping framework. We see ActiveRecord as a tremendous advantage for us. We can embrace the relational database, dropping down into SQL as needed and staying above it when convenient.
Rails migrations let us express the differences between two versions of our schema, and the data they contain, in code (see the article "Rails migrations"). Each migration is named and numbered. I can move up or back to any version of the schema at any time. Migrations have some definite advantages, letting us:
- Restore an older version of the schema when we push out bad code.
- Express the schema in code instead of SQL, which is more convenient for us.
- Be database independent, for the most part.
But migrations have limitations too. If two developers create migrations at the same time, the numbers get confused, and we must intervene manually. We minimize these problems with effective communication: Team members announce when they build a new model requiring a migration. But this model depends on a small team of developers or a slow pace of migrations.
ActiveRecord does have other limitations, some of them intentional. The Rails founder believes that database constraints and composition belong in the application instead of the database, and this causes side effects. ActiveRecord doesn't use views very well: the build process that clones the schema, copies the test data, and runs tests does not copy them correctly. ActiveRecord also has trouble in some cases with referential integrity constraints because some types of associations might connect to more than one database table. Eager loading across complex models is complex, usually requiring SQL when many rows must be joined. Inheritance is limited: with ActiveRecord, I'm forced to use a mapping strategy called single table inheritance (see Resources), which is not always optimal.
All persistence strategies are rife with compromises. I believe that ActiveRecord strikes an effective set of compromises, usually erring on the side of simplicity. All in all, ActiveRecord plus migrations is a net positive for us. We can build our solutions quickly, and we have enough access to SQL to improve performance when needed. But it would be nice to use Rails on projects having legacy schemas, when ActiveRecord is not always up to the challenge. Alternative persistence models are emerging, including RBatis, a port of the iBATIS Java framework (see Resources). It's too early to tell how effective RBatis will be.
For my team and my set of circumstances, Ruby on Rails has turned out to be wildly effective. I don't yet know how the project will ultimately scale because we've been live for only three months as I write this article. We're only now starting to drive traffic. But we do know about productivity. I know that the team came in way under the budgets of competing shops, which usually bid Java solutions. I am also confident in our productivity.
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.
Changing The Present: The nonprofit marketplace, built in Ruby on Rails, that provided the example for this article.
- "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.
- "Rolling with Ruby on Rails" and Learn all about Ruby on Rails: Learn more about Ruby and Rails, including installation procedures.
ActiveRecord: Active Record is the persistence framework for the Ruby on Rails framework.
Single table inheritance: Single table inheritance maps all fields of all classes in an inheritance structure into a single table.
- "Improve persistence with Apache iBATIS and Derby" (Daniel Wintschel, developerWorks, January 2006): Learn all about iBATIS, one of Java's best wrapping frameworks, in this three-part tutorial series.
RBatis: iBatis for Ruby (RBatis) is a port of Apache's iBATIS library to Ruby and Ruby on Rails.
- "The Spring series, Part 2: When Hibernate meets Spring" (Naveen Balani, developerWorks, August 2005): This article teaches the best combination for using Hibernate -- Spring with Hibernate.
The Java technology zone: Hundreds of articles about every aspect of Java programming.
Get products and technologies
Ruby on Rails: Download the open source Ruby on Rails Web framework.
Bruce Tate is a father, mountain biker, and kayaker in Austin, Texas. The CTO of WellGood LLC and the chief architect behind ChangingThePresent.org, he's also the author of three best-selling Java books, including the Jolt winner Better, Faster, Lighter Java. He recently released From Java to Ruby and Rails: Up and Running. He spent 13 years at IBM and later formed the RapidRed consultancy, where he specialized in lightweight development strategies and architectures based on Ruby, and in the Ruby on Rails framework.