After the last salvo of Java fire, I considered it best let the gun cool off for a while before giving it another round. But it's just so darn hard when my newsreader is flooded with misinformation, shallow intentions, and the smell of fear.
Let me start in good manners by offering up some self-deprecation. It has been a mistake to power-push scaffolding through movies and tutorials without following up with the story of how Rails is actually used by peoplebuildingreal-lifeapplications. I acknowledge that while it has brought plenty of wow-factor attention, it would leave the recipients — who only got that message — with an easy path to draw distorted conclusions.
The fact is that scaffolding plays a very small role for anything more than getting people interested, teaching them the first baby steps about doing CRUDs in Rails, and possibly, remotely for doing non-important admin stuff. It was hacked together in a single afternoon as a spur of the moment thing after I had remembered about naked objects and just wanted to take a quick stab at shallow auto-wiring. The main driver for scaffolding is a mere 100 lines of Ruby code!
Why code and HTML gel in Ruby and not in Java
Suffice it to say, I'm disappointed, but certainly not without blame, when I see the simplicity of scaffolding being used as a straw man to convict Rails. I'm equally disappointed, but at some level understanding, when Java programmers apply the misfortunes of their own environment to ramp up the indictments. Dave Geary has this bit:
It's nice to get you up and running, and great for seductive demos and articles, but you're going to override at least 100% of the views that ROR generates. And therein lies the rub... because views in ROR are a mixture of HTML and Ruby scriplets! We've been there before, of course, in the early days of JSP with HTML mixed with Java scriptlets. No thanks, I'll pass on that giant step backwards.
Let's examine the origins of one of that charge — the mixture of Ruby and HTML being a bad idea — and I'll explain why its misguided.
In Java with JSP, it's incredibly tempting to allow your view logic to deteriorate through responsibility creep (overloading them with business or mere complex logic) that leads to a rapid rot. Back in 2001, I worked on a J2EE system fraught with rot of this kind. I can perfectly understand the fear of such decay can instill in any developer.
There are many reasons that things turn from bad to worse with JSP. Allow me to list just three:
JSPs are normally auto-compiling: Unlike the controllers and models, you can make a change, then see it work. In other words, you can circumvent the drudgery of letting the Ant loose to recompile the half or all of the system and redeploying it in the container. Hence, JSPs serve as a constant and appealing temptation to do the bad thing to get the job done.
Extracting complex view logic is painful: With tag libraries, it's possible to be a good programmer and rid the views of complex logic by extracting code and replace it by tags. That is a noble and right path to follow. The problem is when the effort required to do the right thing is so intense that its basically a project in itself. Then its not something you're gently invited to do. It's a huge barrier and easy source of procrastination ("I'll extract later...") and guilt ("If only I had extracted sooner...").
Java is a terribly view logic language: I talked at length about this subject in The false promise of template languages and I doubt there's any disagreement here, so I'll leave it at that.
Let's contrast this to the situation with Ruby on Rails:
All layers are "auto-compiling": There's no compilation penalty for placing code in the controller or model in Rails. All changes are instant! This creates an environment of equal opportunity where code can be placed in the appropriate level right away. In fact, it is often less convenient to place code in the views than in a helper or in the model because you have to integrate it with the HTML instead of just making another method. Temptation to do bad has been replaced with convenience to do good.
Extracting view logic into helpers is easy: As I was riffing on in the rant about template languages, view logic is not a bad thing. It just needs to be managed through extraction of commonalities and complexity. Exactly how easy it is in Rails is hard to explain in words, so I made a little video to do it on its behalf (I'm just working with scaffolded code here, perpetrating the original crime, I know).
Ruby is an excellent view logic language: Ruby is incredibly succinct and imminently suited to do view logic where conditionals and loops are mixed and matched with HTML. Allow me to demonstrate with another example.
But it gets even better. You don't have to mix Ruby and HTML, if that doesn't cook your noodle! Rails provides a fully programmable alternative using Jim Weirich's excellent Builder class. It's very cool and I use it in all the places where I don't collaborate with designers (where HTML is a nice common language). An example is the Builder-template for the RSS feed in Ta-da List.
So with the addressing of Dave Geary's hasty conclusion that "But [Rails] seems to me that once both apps are up and running, ROR is no match for a Java-based web app six pack of JSF-Shale-Tiles-SiteMesh-Hibernate-Spring" purely on the basis of "implementing my own views in ROR looks mighty ugly to me", let's move onto to the even more depressing perception of Rails held by Trails creator Chris Nelson.
'This seems to totally preclude having a rich domain model'
Trails is a can of glue for binding existing Java frameworks like Spring, Tapestry, and Hibernate together into what Chris calls "...a domain driven development framework in the spirit of Ruby on Rails or Naked Objects". Seeing he even got the casing right on RoR, you should think that he spent an equal amount of time understanding the very spirit he's attempting to clone. He echoes the charges from Dave Geary's post on scaffolding and Ruby in HTML (apparently equally ignorant of the issues outlined above), which we won't bother with again, but also adds a third to the mix:
First, domain classes in Rails have to extend ActiveRecord::Base. At first I didn‘t get how big an issue this is, as I assumed Ruby had multiple inheritance or something similar. But having delved into a bit, I‘ve learned that it does not. This seems to totally preclude having a rich domain model. That‘s a compromise I wouldn‘t be willing to live with. It seems like maybe this could be easily done in a different way (with mixins maybe), so let‘s hope Rails makes an improvement here. Since Trails uses Hibernate to persist POJOS, that just isn‘t an issue here.
(My emphasis). Reading that gave me one of those woah experiences. Let me try to give my understanding of what the above means and then address it (then Chris can perhaps correct my understanding): Because Rails already used the first step of inheritance, you can't use inheritance for your own domain model. This is of course not true.
class Project < ActiveRecord::Base
belongs_to :portfolio
has_one :project_manager
has_many :milestones
has_and_belongs_to_many :categories
end
Hence, it addresses both of the short-comings of the original Active Record pattern as described by Martin Fowler while making it incredibly easy to create rich domain models without the overhead of XML sit-ups to describe the mapping.
True, Hibernate can certainly handle more edge cases and legacy database integration jobs, but to state that "This seems to totally preclude having a rich domain model" is one of the most ignorant statements I've ever read about Rails. And its baffling that it comes from a guy that claims to be interested enough in Rails to attempt to recreate the spirit in a Java package.
Jumping back to Geary for a moment, I'd like to ask him to reconsider just how big a Dave Thomas fan he really is (he states "I'm a big Dave Thomas fan"). You obviously don't have very much respect for Dave's sense of judgement, if you think he would be excited enough about Rails to talk about it in front of Amazon and at the Java No Fluff conferences, not to mention that he's write a whole book about Rails!, just because of a little scaffolding.
So please, both of you. Don't be a stereotype. Dislike Rails for all sorts of reasons based on more than a shallow look and a quick dismissal. Talk about how it has no major vendors behind it to make your enterprise feel secure, how static typing makes you all warm and fuzzy, what terrible life would be without IDEA, or how you need to integrate with crazy legacy schemas all day long that Active Record can't handle. There are plenty of enterprisy reasons to diss Rails. Using scaffolding or Ruby in HTML as straw men are not.
To everyone else spectating this with curiosity: Unless you've already been turned off by Rails with my inability to let any negative Rails mentioning on the web slide me by without a big fuzz (and I forgive you if you are), I recommend that you have a look at some Rails code that isn't about scaffolding. Tobias Lutke has some great Subversion repositories available online for the minimalistic weblog-engine Typo and for the book collaboration project Hieraki. And there are move available at documentation.rubyonrails.com.
Even better, download Rails. It's really easy to get started. We include just about everything in the box and there's always somewhere between 150 and 200 people available in #rubyonrails on Freenode to help you get started.
"In Java with JSP, it's incredibly tempting to allow your view logic to deteriorate through responsibility creep (overloading them with business or mere complex logic) that leads to a rapid rot."
It's even easier when the J2EE tutorial illustrates how to throw SQL right into the view:
I believe another reason for the separation of domain logic & view in Java is because of just how verbose JSP + HTML is. You can do the same thing much more succintly with Ruby, and therefore there is less of a need for the separation. However, if the scale of the project requires the separation, you can still go ahead and do it with helpers, and you only need to learn one language (Ruby!).
"In fact, it is often less inconvenient to place code in the views than in a helper or in the model because you have to integrate it with the HTML instead of just making another method."
David, I'll grant that auto-compiling is good and taglibs are too hard to set up, but I'm not buying the argument that Ruby is inherently better than Java as a view logic language. You don't really offer much of an argument here except your premise that Ruby is somehow inherently better than Java. As long as you don't use taglibs, extracting view logic code is no harder in Java than in Ruby.
As for the Trails guy's claims that you can't do a rich domain model in Rails, I don't really see what he's on about. You easily refuted the inheritance argument, and I don't really see what else there is. Active Record seems to strike a reasonable balance between functionality, intrusiveness and simplicity. Java people too often want a "generalized" solution, even when it's not needed. That said, if portability is a goal of an application then Hibernate can be a better fit (at the cost of writing HQL).
Just keep pushing the strengths of Rails!
Challenge by Zsolt on February 22, 9:04
Rails has an edge at the moment when it comes to rapid development of web applications. But Java is catching up. There is a drive for simplification. For example with the advent of Java 5 and Hibernate 3 you can annotate your model with persistence semantics, see http://www.hibernate.org/247.html
Also, tag files as specified in jsp 2.0 let you create the equivalent of helpers in Rails, although more verbose, see http://www.oracle.com/technology/pub/articles/cioroianu_tagfiles.html . On the other hand this verbosity comes from a strict definition that allows advanced tool support.
Now what is better? A concise syntax destined to be read by humans or a stricter syntax that enables tool support (IDEs, visual designers, etc). The jury is still out on this one.
David,
I am also a Java programmer trying out Rails, and I just finished writing a comparison of Struts and Rails (ActionPack) on my blog, my conclusion is somewhat different as Dave Geary & others, so don't despair, not all Java programmers share the same narrow-minded opinion.
Keep singing the praises David, you've converted a bunch of us already ;). The ironic thing about the 'rich' domain model question is that the whole *reason* I love rails is that I can build *real* OO apps easily. No 'POJO with 3 service layers and factories' crap.
I have the same misgiving about the ActiveRecord::Base being the base class of my domain model. I haven't looked at the source code yet but is there any reason why ActiveRecord::Base couldn't be a mixin?
It's as funny as it is tragic; Some people tend to display a tacky affection for jumping to conclusions without bothering to learn the first thing about the subject they're jumping at (or over, way way over).
David, don't ever stop writing rebuttals like these, I learn something new about Rails every time you do.
tim, if you look in the archives of this blog you may find a message from me asking your same question :)
But at some point I had this "ah-ha" moment (after a David's answer) .
The basic idea is that you won't have a class inheriting both from Rectangle and Dog, and neither you won't have a domain data object wich happens to be something else.
Anyway, try to come up with an usage for some counter example, I did not succeeded but maybe there are some :)
You are correct on my misunderstanding of Rails inheritance, so I updated my blog accordingly. Though the ActiveRecord::Base thing still makes me uncomfortable as it reminds me of early Java persistence frameworks that didn't work out very well, perhaps the same pattern works better in Ruby. Anyways, thought I ought to eat my humble pie on that one.
As as Java person by day I do see some of the points that people are making about the domain model that Rails uses. One of the things that I find annoying is that each of the domain classes has to inherit ActiveRecord::Base, which immediately ties your domain model to your persistence model/framework.
It also make abstracting domain class code into common super-classes tricky. For example when I looked at Rails about 2 months ago, I very quickly came unstuck when Rails assumed that when I wanted to do:
Image
that AbstractAsset had its own table rather than just being a superclass supplying the shared attributes and methods, or that Image was a subclass of AbstractAsset and the single table inheritance kicked in . While you could model it using a mixin, it felt decidedly non OO, and became a pain when needing to override some of the methods. Not sure if this has improved, but that's one example where the Rails model suffers slightly over the POJO model.
Saying that there are a lot of good ideas in both Rails and Ruby, that Java and other languages and frameworks need to look at and take on. Similarly there are a lot of ideas that Rails and Ruby need to examine and take on from outside. Hopefully rather than degenerating into a flame war, both camps can use the discussion and learn from the other to improve their products to the benefit of all.
Aled, when you have common functionality that you want to refactor out and share amongst many different model classes, you would typically use modules in Ruby, and mix them in. In other words, "AbstractAsset" (in your example) would be a module, and you would then use "include" to suck that into every model class that needs it.
I came from Java land, too, and I know it took me a little while to begin seeing how to use modules instead of inheritence, but once I got it, I saw how much more powerful it could be, especially when you're stuck with single inheritance.
Challenge by Kent on February 22, 19:28
Aled: I had the same question when I first saw this Rails thing. Why does Rails use ActiveRecord pattern instead of DomainModel pattern? It was so obvious that DomainModel is so
much more powerful. But after playing with Rails and creating a
pretty much complex Rails application, I have realized that ActiveRecord pattern combined with dynamicy and expressiveness of Ruby gives you the enormous power. You can benefit from services provided by ActiveRecord::Base class and it is exactly what you need in 99.9% of cases. Plus Rails has integrated UnitTesting framework from the beginning and your model classes are very easy testable.
For me Rails has these pluses:
--you can get a basic front to back app running in no time and then build it out. That's good agile development: get an app running quickly and iteratively develop it.
--It has ActiveRecord. I'm more of a front-end guy, so anything that makes connecting to the DB this simple is a plus for me.
--Conventions: I like that RoR uses conventions to get things going quickly. .
--Ruby. Ruby is a wonderful language. Enough said.
For me, Rails is lacking in the front end:
--ERb. Using a mechanism like ERb just isn't close to using a component object model such as Tapestry or WebObjects has. Once you've used one, it sucks to go back.
I need to get out, I am in Java model bean hell!!!!, Yea, once you start looking at what it takes to create an web application in java or C#(VB.Net). It really makes you want something else. Is Ruby the way to go. I have a question though, how do you convince others that Rails is something to consider when you have this big J2EE app or .NET application(scary, I actually work with both, mainly J2EE). I may just start showing people the application and not even mentioning the language or environment.
Challenge by Gregory Higley on February 25, 22:46
Before I start whining about ActiveRecord::Base, let me say first of all that I love Ruby and think Rails is headed in the right direction. I've played with it a lot and was impressed, but unfortunately it has some "features" that make it impossible for me to use for my everyday work.
The show-stopper for me seems to be the part everyone loves: ActiveRecord::Base. I guess it's because one of my hats has the acronym "DBA" printed on it. ActiveRecord::Base makes some really naive assumptions about database structure. It more-or-less requires an autoincremented primary key column, which I avoid whenever possible in favor of a natural primary key. (Yes, I'm a Joe Celko fan.) For instance, it seems absurd to me to require an autoincrement column on a list of country codes, when the natural primary key practically leaps out at you.
The other problem with ActiveRecord::Base is that it maps directly to a table. I don't necessarily want that. Sometimes I use performance-enhancing techniques like vertical partitioning, etc. ActiveRecord::Base doesn't map well onto such a thing.
I can recommend ActiveRecord::Base for only the most simplistic database access. But I think it has the potential to do a lot more if some of its infrastructure were changed. Here's a suggestion: In addition to mapping an ActiveRecord onto a specific table, add the option to allow it to be mapped onto any valid SQL query. All SQL databases return type information for the fields in a query, so finding out what the fields and their types are should be no more difficult that getting that information from a table. For the other CRUD operations, present overridable methods that allow the user to provide their own parameterized SQL. That would make ActiveRecord::Base much more usable for a database power user like myself.
(Yes, I'm already working on hacking ActiveRecord to make this possible.)
I love what I see in RoR but unless I'm starting on a project from scratch that doesn't have to work with any legacy db stuff, then I'll have to pass. For those fortunate enough to be starting from scratch and not having to worry about integrating much with other systems (like ERP, CRM, PLM, etc.) then RoR looks to be a great tool. And, just because it doesn't seem to be suited for the job of creating apps based on legacy dbs does not make RoR any less of a success IMO.
"For instance, it seems absurd to me to require an autoincrement column on a list of country codes, when the natural primary key practically leaps out at you."
Interesting. What did you do when Burma (BU) changed its name to Myanmar (MM), or Zaire (ZA) changed its name to The Democratic Republic of The Congo (CD)?
"Interesting. What did you do when Burma (BU) changed its name to Myanmar (MM), or Zaire (ZA) changed its name to The Democratic Republic of The Congo (CD)?"
Probably he just updated the table with the new abbreviations(?)
You can change the value of a primary key field in a record, you know(???)
At any rate, I totally agree with him on this point. I hate when people use auto-increment primary keys when there is a perfectly good candidate natural key.
I've seen some people try to use phone numbers, ISBNs, or social security numbers as their primary keys. On the surface, I think that these would appear to be acceptable natural candidate keys to many people. However, I don't think it's a good idea to make the application responsible for RI.
It's common practice not to choose mutable data for a primary key, especially if the dataset is nontrivially large. Until database engines support a purer relational model, or at the very least, an ON UPDATE CASCADE (which is slop in my opinion), the need to update primary keys results from of a lack of domain knowledge at design time. At some point, someone learns that phone numbers change, ISBNs are not unique, or that some people don't have social security numbers (or changed them for various reasons).
What usually follows from one of these events is an annoying sequence of table updates because you need to find all the fkey relationships. Even worse, if those relationships are not explicit (say, with MySQL or whatever) you've just turned a simple update operation into a full QA cycle.
Not all candidate keys are good primary keys, and the two terms are definitely separate. While you might be happy using a composite candidate key as a foreign key in a small table, I think you'll agree that it's wasteful to do so with several million records.
None of your examples contained "perfectly good candidate keys." They contained bad candidates that anyone would quickly recognize as being such.
A table is supposed to be a collection of data about a certain class of objects. When you introduce an identity key, you are introducing a piece of information that does not describe the data that you are storing and has no logical meaning outside of the database and its innerworkings.
From a very strict point of view, it is an impurity and should be avoided.
Another thought: not every table needs a primary key.
Of course, I realize that in many real-world cases you have to take the realistic path rather than the idealistic one.
I also realize that sometimes performance issues cause the need for a primary key on tables that don't have an acceptable one in their definition.
The situations that I speak of are just what I said. Situations where there *is* a perfectly good (immutable, truly unique) primary key.
I am sorry, but i don't get the opposition to using an id key. Even if there is a natural key around, the world is a-changing, and your database should deal with that by using internal ids.
Say, if the country code changes, you can update the table, but what about the 500 million lines of code and 300 other tables that refer to the country by its unique key? What about stored files on people's disk that refer to that key (granted a globally never- changing unique key is a hard thing to maintain in any case)?
So we have some arguments against using a natural key, which is all fine and well. But what i don't know and would like to see are arguments against the auto-increment id. "impurity" doesn't cut it for me - give me real examples. Thanks!
Challenge by orestis on April 17, 21:34
Okay, I've run into a case where you don't need a auto-inc primary key.
How about when two columns form a primary key, by referencing two other primary keys in other tables ? I don't think that's uncommon.
Challenge by corey lawson on May 17, 8:49
So we have some arguments against using a natural key, which is all fine and well. But what i don't know and would like to see are arguments against the auto-increment id. "impurity" doesn't cut it for me - give me real examples. Thanks!
Or, where I work, we use Lawson on top of Informix. Well, our company got bought out by another similar company, and they use Oracle Financials.
Oh, OF uses a 6-digit account number (char(6)), Lawson uses 5-digit (char(5)). And they're primary keys.
Yes, there are cases where semantic primary keys (i.e., country code, US state abbrev) seem to make some sort of intuitive sense, until you get a dataset that has ISO or FIPS codes for things that otherwise have "natural" PK fields. There's a good, practical reason why those non-semantic ID fields are in those sets...
And there are some new legal requirements like Sarb-Ox and HIPPA that really do seem to cry out for non-semantic key fields as well, because they facilitate the splitting of data appropriately, if required.
Besides, an INT in most RDBMS indexes a whole heck of a lot better than a CHAR(n), where N>4.
I like Celko too, but I tend to dislike semantic primary keys more and more. They just seem to cause too much trouble (i.e., cascading of updated PK values, developing PK generators, etc).