software solutions / project leadership / agile coaching and training

Ruby on Rails vs. .NET: Doing more with less

Posted on October 19, 2010 in .NET, Ruby

One thing that I quickly noticed about Ruby on Rails is that you can do a lot more with less code. This is one of the benefits of working in a dynamic language like Ruby that does not have as many restrictions as a static typed language. The end result is that you have smaller methods, more descriptive code, fewer chances to create bugs, and it becomes easier to get things done. Let me show you what I mean.

Let’s say that I have a database table that looks like this:

create table Users
(
    id int,
    name varchar(100),
    user_status_id int,
    created_at datetime,
    updated_at datetime
)

My Ruby class looks like this:

class User < ActiveRecord::Base
  belongs_to :user_status
  has_many :roles, :through => :user_roles
  validates_length_of :name, :maximum => 100
  named_scope :active, :conditions => ["user_status.id = ?", UserStatus::ACTIVE]
end

My class only has 6 lines, but it does an awful lot. Let’s go through it line by line:

class User < ActiveRecord::Base

Because I have a table in the database called “Users”, I now have the following methods on my User class:

  • id, id=, name, name=, user_status_id, user_status_id=, created_at, created_at=, updated_at, updated_at=
  • find, find_by_id, find_by_name, find_by_user_status_id, find_by_name_and_user_status_id, find_by_id_and_name_and_user_status_id, and any other combination of columns I might want to query on

Because I derive from ActiveRecord::Base, I inherit a lot of data access methods like save, delete, destroy, and many more.

All I’ve done so far is made my class declaration! Look at everything I’m getting for free.

belongs_to :user_status

My User object has a many-to-one relationship to the User_Status table (and it’s corresponding UserStatus object). I now have the “user_status” and “user_status=” methods on my object. The user status will be lazy loaded, so it won’t do any database queries to get the user status until I actually need it.

has_many :roles, :through => :user_roles

My User object has a many-to-many relationship with the roles table, and in the database there is a user_roles table that serves as a mapping table. I now have a “roles” method on my User class that will give me the list of roles, and I can add new roles to the collection without having to worry about creating anything for the mapping table.

validates_length_of :name, :maximum => 100

Name is required and cannot be longer than 100 characters.

named_scope :active, :conditions => ["user_status.id = ?", UserStatus::ACTIVE]

Now I can write “User.active”, which will return all active users. This is equivalent to User.find_by_user_status_id(UserStatus::ACTIVE), but now I’ve abstracted away that concept of an active user so that other classes don’t have to know how the internals work.

end

OK, well, every language has to have some ceremony.

I haven’t created any methods yet! Already my object has all of the data access methods that it needs, it will validate the object before it saves, it knows about related objects, and I abstracted away the concept of an active user.

Note that not only have I been able to do all of this with very few lines of code, the code that I did write was very simple and readable. Since the code is so simple, I’m not going to write unit tests for it either (the tests would basically be the same one line of code as the implementation code in most cases).

All of this means that I can do things much easier than before. In .NET, I would’ve had to create finder methods by hand. I would’ve had to add all of the properties that match up to my database columns by hand. I don’t know how I would duplicate named_scope in a .NET ORM, maybe it’s possible somehow. I’ve never found a way to do it. Maybe I would have to write a stored procedure.

To sum it up, all of the boring crap is done for me and I can pretty much immediately start writing code that provides business value, which is what we’re getting paid to do. No ADO.NET, no more mapping my database table to my entity object, no more modifying several files just to create the same kinds of methods that I have to create for every new entity object that I create.

All of this is helping me to achieve what I’ve always wanted in software development: the ability to write code that says what I want to do and does it without having to do lots of typing.

14 Comments »

  1. This is one of the things that appeals to me about object databases like db4o: just use POCOs, no O/R mapping necessary, all the CRUD is already done, and writing a Linq query is all you need for anything beyond simple CRUD.

    mgroves — October 19, 2010 @ 10:36 am

  2. …and check out all the responsibilities that class has! Validation/establishing object relationships/crud/querying/business logic…more?

    If you crammed all that into a .Net class (which arguably you could achieve by composing a class with some Linq object references) wouldn’t you be questioning the author’s sanity?

    Why do you get a free pass on SRP with Rails? (That is not rhetorical, it’s a serious question!)

    Steve Horn — October 19, 2010 @ 11:25 am

  3. As always, less code means less maintainability. You (or rails) violate the Single Responsibility Principle here: mixing your class behavior with database mapping and validation. Might be cool for a homepage, but a nightmare for a big project. Also “user belongs_to user_status” reads just *horrible*!!! Also, how can has code first?

    Looks like a poor version of Linq2Sql to me.. Yes it doesn’t autogenerate the find_by_… methods, but with Linq it’s about as simple to write a Linq query.

    @mgroves rails doesn’t have Linq as far as I know..

    ulu — October 19, 2010 @ 11:32 am

  4. I think maybe that the violation of SRP is okay ONLY if it’s on objects that act as DTOs between the data layer and the business layer. If you cram so many responsibilities into such a small space, then you need to translate it into something more SOLID as soon as possible. But then I have had these discussions (about my discomfort with active record) with Jon before.

    It’s why I don’t think I’ll ever be a Ruby fanboy, and it’s also why I think it’s “domain” is in building web presentation tiers, and not so much in the business tier.

    MIchael Meadows — October 19, 2010 @ 12:41 pm

  5. @ulu “As always, less code means less maintainability.” This is the exact opposite of my philosophy. Why would it be easier to maintain MORE code? Seems to me five, five line classes is alot easier to maintain than ten, ten line classes.

    Also, Once you are accustomed to reading the association declarations they are very easy to follow. However, the belongs_to :user_status is a little odd. Why would a user belong to a status, and not has_many :user_statuses? But this is probably besides the point of the post.

    Jimmy Trowbridge — October 19, 2010 @ 1:38 pm

  6. [...] Ruby on Rails vs. .NET: Doing more with less [...]

    Jon Kruger’s Blog » Ruby on Rails and the Single Responsibility Principle — October 19, 2010 @ 2:47 pm

  7. There was too much to respond to in a comment, so I just wrote up another post with my response to all of the comments here.

    http://jonkruger.com/blog/2010/10/19/ruby-on-rails-and-the-single-responsibility-principle/

    Jon Kruger — October 19, 2010 @ 2:47 pm

  8. Well, db4o is only for Java and .NET/Mono, but I brought it up because I think it reduces “ceremonial” code in a similar way.

    mgroves — October 19, 2010 @ 3:49 pm

  9. Micheal,

    The necessity of a distinction between the “data layer” and the “business layer” is somewhat of a moot point if your data layer is so lightweight that you don’t need to think about it.

    Principles are a means for experts to communicate rules to novices. They should be treated as guidlines, but it takes true prowess to be able to know when it makes sense to apply the principle or build new ones.

    I don’t think you need to be a Ruby fanboy to be able to realize that various languages have their advantages/disadvantages. Ruby is definitely for the business tier; the business just hasn’t realized the full advantage yet.

    Ben Wagaman — October 19, 2010 @ 10:09 pm

  10. Hate to mince words, but really that’s what this argument is hinging on.

    The word “principle” has a much stronger meaning than this conversation is giving it.

    Merriam Webster - “Principle”

    Perhaps a revision is in order? How about “SOLID guidelines”.

    Steve Horn — October 20, 2010 @ 12:14 pm

  11. SOLID guidelines… I like that! Too many principles and best practices are treated more like rules and laws than suggestions or ideas.

    Jon Kruger — October 20, 2010 @ 12:23 pm

  12. Ben,

    In the case that the data layer is simply a “pass-through”, then you have a point (with caveats). As long as you are in full control of the complexity of your persistence, then it’s reasonable to eliminate the distinction. If however, you’re not in control of the database, or if you rely on external services or external databases for your data, then the indirection will protect you from leaking that complexity into your business layer.

    MIchael Meadows — October 21, 2010 @ 10:34 am

  13. Michael, in the cases of external data sources, you can build in a proxy layer, but I wouldn’t use ActiveRecord for that. I have many times built in a transaction data model to handle the processing of web services. That seems to provide a robustness to log status and reprocess if necessary.

    Ben Wagaman — October 21, 2010 @ 7:28 pm

  14. Not quite as elegant as named_scope, but NH can do similar. See ayende’s post here: http://ayende.com/Blog/archive/2009/04/07/nhibernate-mapping-ltpropertygt.aspx

    Search for CountOfPosts. Integer being computed here, but could be a bit.

    Ryan Cromwell — November 16, 2010 @ 9:08 am

Leave a comment





SERVICES
SOFTWARE SOLUTIONS
I have over 10 years of software development experience on several different platforms (mostly Ruby and .NET). I recognize that software is expensive, so I'm always trying to find ways to speed up the software development process, but at the same time remembering that high quality is essential to building software that stands the test of time.
PROJECT LEADERSHIP
I have experience leading and architecting large Agile software projects and coordinating all aspects of a project's lifecycle. Whether you're looking for technical expertise or someone to lead all aspects of an Agile project, I have proven experience from multiple projects in different environments that can help make your project a success.
AGILE COACHING
I believe that Agile processes and tools should be applied with common sense. I've spent the last 6 years working on Agile projects as a consulant in many different environments, both in leadership roles and as a practitioner doing the work. I can help you find out how Agile can work best in your organization, not just apply a prescriptive process.
TEST DRIVEN DEVELOPMENT TRAINING
TDD Boot Camp is a hands-on, three day, comprehensive training course that will teach you all of the skills, tools, frameworks that you will need to use test-driven development to develop real world .NET applications. If you're not looking for something that intensive, check out the the half-day version.
Have any questions? Contact me for more information.
PRESENTATIONS
(presented with Paul Bahler and Kevin Chivington from IGS Energy)
From CodeMash 2011
An idea of how to make JavaScript testable, presented at Stir Trek 2011. The world of JavaScript frameworks has changed greatly since then, but I still agree with the concepts.
A description of how test-driven development works along with some hands-on examples.
From CodeMash 2010
From CodeMash 2010