Ruby on Rails development differs fundamentally from Java development. In this article, Bruce Tate describes the key differences he discovered when using Rails to develop complex, scalable Web sites from scratch.
Developers on rails often look at their fellow coffee drinkers as remnants of the past. And Java devotees often describe Ruby on Rails as a toy that has no place in any serious software development project. As a consultant who has worked extensively with both technologies, I know that the truth lies somewhere in the middle. As this "Crossing Borders" series comes to a close, I would like to make one more comparison. Instead of looking at a particular language or technology, I will focus on my current project and compare it to Java projects I have worked on in the past. (I’ll take this opportunity to refer you to previous articles in the "Crossing Borders" series, which describe the topics discussed here in more detail.) This article should first of all give you an idea of the drawbacks and potential benefits of using Rails to develop database-driven Web applications.
The Business Challenge
Neither Ruby on Rails nor Java is the only right solution to any problem. To increase the likelihood of success, you need to take a long and thorough look at your business problem, understand your environment, and know your team. Only then can you choose a good language to solve your problem.
Last year, Arvando Systems hired me as a team leader to develop ChangingThePresent.org. , a new product that would help bring donors and nonprofits together. Like many Internet companies, we sell clear products that our customers buy. But unlike other companies, the "products" are opportunities(or gifts), such as paying a cancer researcher an hour for $50, helping a blind person for $30, or protecting an acre of century-old forest for a month for $20.
We had two main challenges : an ambitious plan and long term complexity (complexity throughout the development of the project).
We started development in September and we had to finish development by November in order to get a good profit over the Christmas vacations. (Actually, we were delayed by two weeks).We knew that a Java-based solution would take 6 to 18 months. Productivity was very important. The Java solution did not meet this requirement.
Looking at the challenges thrown at us, we knew we could attract traffic on the order of a million hits a day. We had to handle hundreds of thousands of hits a day to be successful, so scalability was important. This pointed to the use of Java.
Finally, we knew that even after the site was up and running, things were just getting started. We wanted to start with only 3% of all our plans. So the technology we used had to scale in terms of both complexity and load.
I believed that Ruby – with its capabilities as a high-level language – and Rails – with reduced configuration needs and a simpler, more integrated programming model – could scale better in terms of complexity.
But time and scalability worked against us, the other factors worked for us. We had a completely clean state: we could choose any technology, any team we wanted. We could define the project, the culture, and the whole set of technologies. Basically, we had complete freedom of choice.
Java is an excellent general-purpose language. It is always focused on new features, such as embedded systems and mobile devices. Java also succeeds in combining different requirements. It is high-performance, popular and well supported in the market. But as you’ve seen in this series, Java is not necessarily the best choice for developing database-driven Web applications from scratch (see article "Web development strategies in dynamically typed languages" ).
In contrast, Ruby on Rails is a new framework. Not many people have used Rails for high-traffic sites, and there is almost no experience with Rails layered development. But, Rails is an extremely productive framework for database-driven web applications. Finally, our aggressive schedule led us to choose Ruby on Rails despite the lack of experience with Rails for long term projects and large scale deployments.
Once we made the decision, we quickly recruited a talented developer for the project. We also found that our productivity was fantastic-even more than we expected. We had some stability issues at first, so we redoubled our testing efforts (see articles "Testing in integrated frameworks, Part 1." and part 2 ). Since doubling, we have increased our stability unimaginably.
Every framework developer works with a set of assumptions that create the philosophy of the framework. Learn how to live with the limitations of this philosophy and you are a happy programmer. Fight them, and you’ll feel crushed. Rails and Java have very different philosophies.
Rails is an integrated framework that makes heavy use of the dynamic nature of Ruby (see article "What’s the secret sauce in Ruby on Rails?" ). Rails developers reinforce framework properties to increase productivity, rather than introducing new tools to support development tools, and often take a simplistic view of the web architecture, as you’ve seen in previous articles in this series. Java developers must often piece together pieces of their environment, choosing independently the object storage system (persistence), the web system, and integrating them. They often rely on development tools to simplify key tasks. The Web architecture appears to be more complex from a development standpoint.
Java aims to solve one small problem, such as persistence or organization of views, while Rails is an integrated environment.
The advantage for Rails developers is that they don’t have to solve the problem of integrating many inherently different frameworks.
Most Hibernate developers fall into the trap of prematurely breaking the link to Java web frameworks. Rails view frameworks are built from the ground up with the expectation of integrating with ActiveRecord, the Rails object storage framework. You’ll experience the same experience with Rails frameworks for Web services, configurations, and plugins. Java supports a lot of fundamentally different frameworks, with different approaches to integrating all those things.
The virtue of Java developers is choice. If you need to build a framework from scratch, you might consider a SQL-based database integration solution such as iBATIS or one of the many JDBC-based Java wrapper frameworks. If, conversely, you are starting with your own database schema, you might consider ORM frameworks such as Hibernate. If you’re using Rails you have the only basic choice, ActiveRecord. This means that the Java framework, while providing more choices, sometimes provides a better solution for integrating projects. But since we were developing the project from scratch, the choice was not a big problem.
The next major component of the Rails philosophy is a dynamic programming language (see article "Typing strategies beyond the Java model." ). Java tools seek to leverage the additional information provided by the Java typing model. The tools can detect bugs and effectively restructure or refactor code. Rails also effectively combines the advantages of a programming language. Ruby is an ideal language for creating domain-specific languages (DSL) (see the article "Domain-specific languages in Active Record and Java programming" ). Rails strongly enforces the use of DSL for everything from creating relationships between model objects to specifying arbitrary components such as finite automata and server-loaded images. Dynamic languages are often more self-explanatory, so Rails projects are much more concise than their Java counterparts, allowing users to write both code and configuration more clearly. At ChangingThePresent.org, we’ve noticed that our top programmers can be more productive and we don’t need to hire programmers with more experience. I’m happy with that.
The typical Java programmer is religiously committed to his IDE and with good reason. The IDE provides him with code prompts (code completion),
eliminates typical bugs and provides incremental compilation to speed up the coding, compilation, placement, and testing cycle. In recent years, development environments have begun to use even more of the information provided by the compile cycle and static typing. IDEs now edit abstract syntax trees instead of, or in addition to, the text representation of the code. This strategy makes it possible to create powerful refactoring tools which are much more difficult to obtain using the same methods for dynamically typed languages.
Static typing provides good development tools, but it also has disadvantages. Static typing requires a compiler, and the compile step can absolutely ruin productivity. Using Rails, I can change a line of code and reload the browser, immediately seeing the result of the changes. In contrast to Java programmers, most Ruby programmers only use a good text editor. TextMate, the most popular editor for Ruby on Rails, highlights syntax, supplements code, and has good template support for commonly used constructs. And the editor is more than adequate for writing the simple Ruby scripts that underlie Rails. Instead of a full-fledged debugger, I use a breakpointer script that can stop the specified application and send me to a Ruby interpreter where I can call methods, view variable values, and even modify code before I continue execution.
A typical Java architecture for a Web application has a domain objects layer, a data access objects layer, a front end layer providing business layer APIs, a controller layer and a view layer. This architecture is slightly more complex than the classical model-view-controller architecture that was first used in Smalltalk. In contrast, RoR uses a single model layer, using the ActiveRecord pattern, the controller layer, and the view layer. We love the Rails approach. It’s clearer and leaves less room for additional complexity and bugs.
Arrangements (agreements) instead of configuration
Java frameworks often use XML configurations, while Rails mostly uses arrangements to avoid configuration wherever possible. Where the programmer has to configure something, Rails relies on Ruby, often in the form of DSL for configuration. For development from scratch, I find that arrangements instead of configurations make sense. This strategy saves me a lot of lines of code and simplifies the code I have to write. We estimate that we use 1/10 of the configurations that we would have to do in Java. We sometimes lose flexibility, but not so much that this disadvantage trumps acquisition.
To summarize, the philosophy of the rails framework is great for the task of creating thethepresent.org. The integrated stack allows me to get more functionality from the framework without having to do unnecessary integration myself. The philosophy of arrangements instead of configuration saves me time. The dynamic language gives my ex-users more power and flexibility, while allowing me to express more advanced ideas with less code. The framework aligns with the abilities of my team as well as with the business problem we are solving.
The most popular storage frameworks for Java and Ruby illustrate the difference between the two much better than all the other features. Java developers typically use Hibernate. With Hibernate, you take an existing model and an existing schema and define a correspondence between them using annotations or XML. Hibernate classes are POJOs, where each object is spawned by one common base class. Most configuration is done explicitly using annotations, XML, or a combination of both.
ActiveRecord, on the other hand, is a shell framework, which means that each class wraps around an existing class (see "Exploring Active Record" ). ActiveRecord automatically adds features to model objects based on the contents of the associated table, such as the attributes of each column in the table. All classes inherit from a common base class. ActiveRecord is mostly configurable through common conventions. For example :
- ActiveRecord finds a table based on the class name, which is specified in the plural
- The primary key name is id.
- The order in which the lists are sorted is determined by a field called position.
ORM is best when you have an out-of-the-box schema, perhaps already developed with an object model in mind. But when you develop a database schema explicitly for your application, you often don’t need an ORM framework. We see ActiveRecord as a terrific advantage for us. We can use a relational database, using SQL as needed, or stay above SQL when it’s convenient.
Rails migrations allow us to specify the differences between two versions of a schema and the data they contain in the code (see "Rails migrations." ). Each migration is named and numbered. I can go from version to version of the schema at any point in time.
Migrations have some special advantages, allowing us to :
- restore an older version of the schema when we remove the bad code
- to specify a schema in code instead of SQL, which is more convenient for us
- be independent of the database in most cases
But migrations have limitations. If two developers create migrations at the same time, the numbers can get mixed up and we have to intervene manually. We minimize these problems with effective communication : team members talk when they build a new model requiring migration. But this model usually depends on a small number of developers or affects a small part of the migration.
ActiveRecord has other shortcomings, some of them done intentionally. The founders of Rails believe that constraints and compositions are set in the application, not in the database, and this causes a side effect. ActiveRecord doesn’t make good use of views : a creation process that clones the schema, copies the test data, and runs the tests doesn’t copy them correctly.
ActiveRecord also has difficulties in some cases with referential integrity because some association types can use more than one database table. Intensive loading of complex models is very complex and often requires SQL when multiple rows must be combined. Inheritance is limited : with ActiveRecord I am forced to use an association strategy (mapping) called singe table inheritance which is not always optimal.
All object storage strategies are a tangle of tradeoffs. I believe ActveRecord strikes an effective set of tradeoffs, though it sins in terms of simplicity. So ActiveRecord plus migration is a big plus for us. We can build our solution quickly, and we have enough access to SQL to boost performance when we need to. But it would be healthy to use Rails with projects that have out-of-the-box schemas, which ActiveRecord doesn’t always win with. Alternative storage models are evolving, including RBatis, a port of the iBATIS Java framework (see resources). But it’s too early to tell how effective RBatis will be.
For my team and my situation, Ruby on rails has been wildly effective. I don’t know how well the project is scaling yet, since we’ve only been live for three months. We’re just now getting a solid increase in traffic. But we are aware of the productivity.
I know that the team has gone down a path that requires a smaller budget as opposed to competing shops offering Java solutions. I am also satisfied with our productivity.
About the author
Bruce Tate is a father, mountain biker and kayaker from Austin, Texas. Director of Technology(CTO) at WellGood LLC and chief architect of ChangingThePresent.org. He is also the author of three best-selling books on Java, including Jolt winner Better, Faster, Lighter Java. He recently published From Java to Ruby on Rails: Up and Running. He spent 13 years at IBM and later formed RapidRed, a consulting firm specializing in lightweight development strategies and architectures based on Ruby and Ruby on Rails.