Tuesday, December 26, 2006

Attacking Ruby on Rails

[Note added 12/28/2006: Caution - this posting was a holidays fogged, sugar-high induced feeble attempt at satire and humor that apparently rubbed many people the wrong way. Don't take it too seriously......]

The Swiss Army Knife

The Swiss Army Knife is a cool tool. It slices. It dices. It has scissors. A long knife blade. A corkscrew. A short knife blade. A file. A saw. Pliers and a screwdriver. If I'm stuck out in the wilderness with just one tool, I want one of these, or a Leatherman, or some similar tool. But I don't use a Swiss Army knife to gut a deer or a fish - it's too big and not something I want coated in blood and entrails. I don't use its screwdriver or pliers when I'm working on a car - it's too small and doesn't fit in tight spots. I don't use its saw to cut wood for the fireplace.

Using the right tool for the right job is a pretty simple concept. So why do so many people forget this when discussing programming languages and software frameworks? They're just tools, and can be selected in the same way. Are people really so stupid as to think that Java, or .NET, or PHP, or Python, or PHP, or LISP, or (insert your favorite language here) is the best tool for every job? I don't think so. So what's really going on when otherwise smart people go ballistic and become zealous warriors for their favorite technology when the topic of Ruby or Rails comes up? Here are some characers I've encountered that shed some light on the subject:

The One Trick Pony

"I've created dozens of kick-ass web sites in PHP. I can do anything in PHP that you can do in Rails. There's more lines of code in PHP than in Rails. There's more PHP job postings than for Rails. No way am I learning anything new, dude. It would cut into my partying and napping time..."

"Ruby is just a degenerate form of LISP. Rails is just an over-hyped Lambda expression."

Python is demonstratably better than Ruby
We have infinite frameworks to choose from
Our superior intellects will make you weep
Take your upstart toy and be gone
The Wise Wizard

"Bow down to my Enterpriseyness! How dare you think you can apply your puny little framework to Serious Business Stuff! You foolish mortals know nothing of the dangers you will encounter. Inflexible DBA's! WSDL, BPEL and Enterprise Buses! Evil database API's! Message Queues! Enterprise Application Integration and Business Process Orchestration! Technology Stacks containing complexities which your feeble minds can't begin to comprehend! Now take your stupid little dog and go back home before someone gets hurt."

The Corporate Framework Guru:
Keep Your Filthy Problem Domains Away From Me

"We have a well-established seniority structure here. The new programmers work on customer-facing code: printed output, reports, user-interfaces. The intermediate programmers work on the business logic that the customers care about - they build classes and business rules that make the system work. The senior programmers work on the fun things that the customer doesn't care about - presentation layers, message processing, persistence engines, process orchestration rules engines. The smarter you are, the better you insulate yourself from boring customer problems. I don't want a fancy framework coming in here and messing up this my nice ivory tower..."

The Drone

"I got my Visual Basic certification just 10 years ago. 3 years ago the company made me go to a training class to learn VB.Net. Now I have a jerk of a new manager that wants me to use C#. My head hurts just thinking about it. No way am I going to experiment with Ruby or Rails. The company won't even let me install it on my machine at work, and I'm too busy to learn anything at home...."


"Open source - isn't that something only commies use? Who provides your support contract? What does Gartner have to say about it? I can't possibly tell the CEO this is a good idea! Blah! Blah blah! BLAH!!! Now get out of my office - I have lots of magazine reading to catch up on and a golfing --er I mean a technology conference trip I have to schedule."

The Chicken

"I'll prototype it in Rails, then build the real app in X."

(This is how I got sucked into using Rails full time. Prototyping is a dangerous gateway drug...)

The Best Tool for the Job

If I was thrown out into the wilderness and forced to pick one toolset without knowing what kind of problem would come along I'd want the .NET Swiss Army Knife. If I was integrating with a rich corporate infrastructure rife riddled with legacy applications, pre-established database schemas, message queues, with some WS- sauce thrown on top, I might want the Java Leatherman. (OK, I'm lying. Actually I'd want to quit, run away, and start my own company with a fresh slate, but that's a different story....)

But if I'm working on a new web-based application, backed by an application database, Rails is the best tool for me, whether the job is large or small.

Thursday, December 21, 2006

Rails Web Service Gotcha: Use Separate Server for Client Testing

Today's painful lesson: If you are developing a web service, and also writing the "client" code that will utilize the web service, don't develop and test both on the same server instance.

In our case, we are exposing some controller code (call it the "server" controller) via a web service, and have a separate controller (call it the "client" controller) that will invoke the service. In the production world, these controllers will be running on separate web servers.

During development, it seemed perfectly reasonable to test both controllers on a single machine. The client code successfully invokes the server code via the web service, but also times out in a bizarre fashion. This occurs under Mongrel and Webrick, and occurs whether the call is made via SOAP, XML-RPC, or REST.

The solution: When on a development machine, start up two Mongrel or Webrick instances on separate ports (say 3000 and 3001). Point your browser at the client controller on port 3000, and be sure the client controller talks to the server controller on port 3001. This requires a bit of environment-specific code, but solves the problem.

Thursday, December 14, 2006

More Ruby / Rails trend indicators

After learning of the TIOBE Programming Community index mentioned in this post a few days ago, I've kept my eyes open for other trend indicators showing trends related to Ruby and Rails. Here are a few more:
  • Google trends, showing a trend history for the search term "Ruby on Rails".
  • A book sale graph comparing trends between various languages.
  • Indeed.com shows a nice graph of the Trend of Ruby on Rails jobs postings.
  • Monster.com currently shows 135 postings for Rails jobs, 335 postings for Ruby jobs. By way of comparison, Delphi shows 329, Python shows 906, and C#, Java, PHP, and Perl all show 1000 or more.
  • Dice.com (filterd for the U.S.A.) shows 181 postings for Rails jobs, 286 for Ruby. By way of comparison, Delphi shows 171, Python shows 853, C# shows 6,045, .Net shows 8,347, Java shows 15,122, and J2EE shows 7,674.
Today, my own observation of shelf space allocated to any given programming language at the local mega bookstore showed the following:
  • 6 shelves of books on .NET languages (C#, ASP.NET, VB).
  • 3 shelves of books on Java and J2EE.
  • 1 shelf of PHP books.
  • 1/2 shelf of Ruby/Rails books (a nice increase from the 2-3 books available earlier this year).
  • 1/2 shelf of Python books.
These are all crude indicators, but in combination provide a good view of the emerging Ruby/Rails development community. I'll revisit these numbers in the future to monitor the trend.

Tuesday, December 12, 2006

The Software Complexity Triangle

On recent projects, while drinking the koolaid of delivering simpler, more usable software, I have noticed a Software Complexity Triangle similar to the well known Project Management Triangle. The points on the Software Complexity Triangle are Simplicity, Power, and Design:

On this diagram, Simplicity represents the ease of using a system, both for end users as well as for operational support or back-office staff. Power represents the overall capability and depth of functionality of the system. Design represents the expertise of your software development team, particularly the depth and breadth of their technical skills as well as their knowledge of the problem domain in which they are working.

It is possible to fix in place or optimize for one or two points of the triangle, but the third point must compensate accordingly and cannot be held constant:
  • Most development teams can create unpowerful software that is simple to use.
  • Most development teams can create powerful software that is difficult to use.
  • It takes a highly skilled team to create software that is both powerful and simple to use.
The last item is of key interest. As an example, on our primary project it is critical that our software be simple to use, yet the operations it must perform are unavoidably complex. Our design team has spent many long hours searching for the right balance between power and simplicity on various portions of the system. Often, the end result of our requirement and design discussions results in something like this:

Usually (after recovering for an evening or a weekend) we are able to grind through the issue and eventually create a good solution. But it takes considerable time and effort. Other implications when developing a simple+powerful system are:
  • Planning: Be prepared to spend a lot more time on requirements, designing, prototyping, and reviewing until the combination is right.
  • Hiring: Your team must contain a great depth of domain knowledge - it will not be sufficient to attack the problem with technical wizardry only. Working by rote against a typical listing of functional requirements, use cases, etc. will not deliver a solution of any great value. A true understanding of the real business problems and opportunities to which the system will be applied is critical.
  • Back Office software: "Back end" processing software may need a higher degree of sophistication than the typical mishmash of scripted processing routines.
  • User Interface: It takes an open mind and considerable time and attention to detail to look at your system with the eyes of a new user.
It many cases it is inappropriate to force simplicity and power into the same system (or portion of a system). A reduction in complexity, or less emphasis on simplicity, may be appropriate. Keep in mind the alternate paths this potentially puts you on:
  • Less simplicity results in more user training and documentation. This is not acceptable if your audience is the general public, but may be appropriate if user staff is relatively small.
  • Less simplicity may cause the need for a "Man Behind the Curtain" to make the system work. This might be OK for small systems, but typically does not scale well.
  • Less power: Be certain that your requirements for high end functionality are really necessary - it is often surprising and rewarding to examine requirements through the lens of ROI, schedule impact, and alternative solutions.

Friday, December 8, 2006

Ruby moves up to #11 on the TIOBE Programming Community Index

Ruby has moved to position number 11 on the TIOBE Programming Community Index. I'm not sure what all this means, as I have not studied the methods used to create the index, but it's pretty cool regardless. Ruby was in position #20 a year ago.

The next languages above Ruby on the list are #10 Javascript, #9 Delphi (an old favorite of mine), and #8 is C#. It will be interesting to see how the rankings go the next few months.

Thursday, December 7, 2006

Ruby on Rails Deprecations Part 3 of 3: ActionView

This final installment in posts about deprecated features of Ruby on Rails covers Views. See Part 1 for Controllers, and Part 2 for ActiveRecord.

Deprecated View features
  • content_for('name_of_content_block') - use yield :name_of_content_block
  • :human_size - use :number_to_human_size
  • link_image_to - use image_tag within a link_to method
  • :post as a link modifier - use :method => "post" instead
  • render_partial - use render :partial
  • render_partial_collection - use render :partial, :collection

Wednesday, December 6, 2006

Ruby on Rails Deprecations Part 2: ActiveRecord

My search of deprecations for ActiveRecord returned a mercifully short list. Here they are:

Deprecated Associations
  • :dependent => true - use :dependent => :destroy
  • :exclusively_dependent - use :dependent => :delete_all
  • push_with_attributes - if associations require attributes, use has_many :through
Deprecated Methods
  • count by conditions or joins - use count(column_name, options)
  • find_all - use find(:all, ...)
  • find_first - use find(:first, ...)
  • human_attribute_name - use .humanize
  • Object-level transactions

Tuesday, December 5, 2006

Ruby on Rails Deprecations Part 1: ActionController

Rails provides reasonable facilities for notifying you when deprecated code is encountered. However, I have been unable to find a centralized listing of deprecated features so I can avoid them prior to coding. So, I've examined the Rails 1.2 Release Candidate 1 code to put together such a list. This article contains a list of deprecated Controller items. Subsequent listings will cover Models and Views.

Items are listed alphabetically, followed (if applicable) by the appropriate replacement. If you are aware of omissions or see an error, leave a comment and I'll update the list.

Deprecated: General

  • Components are deprecated.
  • All dependency loaders in module Dependencies now belong to ActiveSupport instead of ActiveController (:depend_on, :dependencies_on, :model, :observer, :service)

Deprecated Controller Instance Variables

  • @cookies, @flash, @headers, @params, @request, @response, @session - use reader/writer methods

Deprecated Controller Methods

  • expire_matched_fragments - use expire_fragment
  • keep_flash - use flash.keep
  • parse_query_parameters - use parse_form_encoded_parameters
  • parse_request_parameters - use parse_form_encoded_parameters
  • redirect_to_path - use redirect_to(path)
  • redirect_to_url - use redirect_to(url)
  • render('#{options}') - use render :file => #{options}
  • url_for(:#{options}) - use url_for with a named route directly

Deprecated Assertions

  • assert_assigned_equal - use assert_equal(expected, @response.template.assigns[key.to_s])
  • assert_cookie_equal - use assert(@response.cookies.key?(key))
  • assert_flash_empty - use assert(!@response.has_flash_with_contents?)
  • assert_flash_equal - use assert_equal(expected, @response.flash[key])
  • assert_flash_exists - use assert(@response.has_flash?)
  • assert_flash_has - use assert(@response.has_flash_object?(key))
  • assert_flash_has_no - use assert(!@response.has_flash_object?(key))
  • assert_flash_not_empty - use assert(@response.has_flash_with_contents?)
  • assert_flash_not_exists - use assert(!@response.has_flash?)
  • assert_invalid_column_on_record - use assert(record.errors.invalid?(column))
  • assert_invalid_record - use assert(!assigns(key).valid?)
  • assert_no_cookie - use assert(!@response.cookies.key?(key))
  • assert_redirect - use assert_response(:redirect)
  • assert_redirect_url - use assert_equal(url, @response.redirect_url)
  • assert_redirect_url_match - use assert(@response.redirect_url_match?(pattern))
  • assert_rendered_file - use assert_template
  • assert_session_equal - use assert_equal(expected, @response[key])
  • assert_session_has - use use assert(@response.has_session_object?(key))
  • assert_session_has_no - use assert(!@response.has_session_object?(key))
  • assert_success - use assert_response(:success)
  • assert_template_equal - use assert_equal(expected, @response.template.assigns[key.to_s])
  • assert_template_has - use assert(@response.has_template_object?(key))
  • assert_template_has_no - use assert(!@response.has_template_object?(key))
  • assert_template_xpath_match - use assert_tag
  • assert_valid_record - use assert(assigns(key).valid?)
  • assert_valid_column_on_record - use assert(!record.errors.invalid?(column))

Harvest Outfitters: Rails Site Recently Depoyed

Harvest Outfitters is a company in which I have a business interest. The company supplies kerosene cooking and lighting appliances and accessories for emergency preparedness. The site was originally a J2EE system that has recently been rewritten in Rails. It is running on Mongrel - so far we are very satisfied with Mongrel as a server. Take a look....

Monday, December 4, 2006

Justify Your Choice of Ruby on Rails: Articles and Links

If you have a project of any significance for which you are using or considering using Ruby on Rails, you will have to justify your decision to several brutal audiences. Your customers, investors, those that may acquire your business, your coworkers, managers, team, etc. have a whole boatload of concerns about technologies that are not regarded as mainstream. If you're not using something widely regarded as safe (J2EE or .Net), you'd better be prepared to dance.

Performance and Scalability

The top two concerns are usually performance and scalability: "Ruby is slow" or "Interpreted languages are slow" seem to be the most common. Here are some excellent articles on the subject (the overall summary being: Rails provides a great advantage in getting your product to market, there are reasonable and inexpensive ways of addressing any performance bottlenecks, and scalability is not a problem):

It's Boring to Scale With Ruby on Rails
Scalability Examples: Hardware Requirements at Basecamp and Robot Co-Op (43Things)
Outsourcing the Performance-Intensive Functions
Making Things Faster

Large Systems Using Rails

There are a lot more than listed here, but these are some of the largest:

Amazon Uses Rails on Amazon Unspun
Basecamp has over 1 million users
43Things has over 700,000 users
Odeo is a Rails App
Blinksale is a Rails App
List of Other Rails Deployments

Philosophy Behind Rails

Many of the arguments against Rails are rooted in a misunderstanding of philosophy, purpose, and design decisions behind the framework. The following articles provide clarity in this area:

Ruby on Rails: An Interview with DHH
IndicThreads.com interview with DHH
The Reg sits down with DHH
Choose a Single Layer of Cleverness

Are You Afraid?

Fear-Driven Technology Choices
Is Rails Ready for Prime Time?
Are We Approaching a Tipping Point for Rails?
Will Rails Become Mainstream?
Does it Matter If It Does?
How to Introduce Ruby on Rails in Your Company
Paul Graham: Beating the Averages (It's about Lisp as a strategic advantage. Just read "Rails" where it says "Lisp" and you'll get the idea).
TIOBE Programming Community Index One indicator of the rate of Ruby popularity (as of this writing #12 and moving up fast).

Other Articles

Some of these articles are dated and are not up to date with respect to capabilities of the current version of Rails, but nevertheless provide good material.

Enterprise Rails
Evaluating Ruby
RoR in the Enterprise Toolbox
Rails / J2EE Comparison from IBM
Rails Bidding: Put Your Money Where Your Mouth Is
Rails Perspective from the guy at the bank

Know the Enemy

Get to know what the other side is saying. There is a lot of writing out there (some rational and well reasoned, some misinformed, and some from people that are almost certainly insane). Search for 'rails sucks' or 'ruby sucks' and you'll have hours of entertaining reading. You need to understand what Rails strengths are, what its weaknesses are, and what FUD is out there in order to be able to make (and defend) a wise decision.