Well, thatâs the saying. One thing is for sure. You canât expect to get what you donât pay for.
I got an IM from a buddy this afternoon looking for a second opinion on a Rails application. Someone had paid for 100 hours of work (I donât know at what rate), for a Rails project which wasnât progressing to his liking. Heâd asked my buddy to give an opinion of what had been done, and I was asked to have a look as well.
I only spent about a half hour or so looking at the code, since I was doing this as a courtesy. I spend quite a bit of pro-bono time helping others by writing here and contributing on public fora, but Iâve got my limits.
The executive summary: I wasnât impressed.
This was a completely blind sampling. I have no idea who the consultant was who wrote this app, nor even the identity of the client who had funded it.
I noticed that there was both a spec and a stories directory in the project a good sign. Looking closer though the specs all seemed to have been generated automatically by the restful_authentication plugin, same thing for the stories.
And the tests were the standard useless stub tests generated by the Rails model/controller generators.
There was some very suspect code in some of the controllers I looked at, one was creating a model in the new action. Normally the new action instantiates an unsaved model instance for use by an edit/creation form, leaving the actual creation of the model in the database to the create action after the form is posted.
The Application controller had some strange looking code, which looked like it might be boilerplate from somewhere. When I googled for a snippet from the code, it looks like it came from somebodyâs rails 2.3 template for generating a new rails app using shoulda.
So we have an app which canât seem to decide which testing framework to use and then not really use any of them to any effect.
Now I know that shoulda, rspec and test::unit can be used in combination by a sophisticated developer, but I donât think thatâs what was going on here.
I poked around in a few models. I found one which really flummoxed me. The model purported to be representing a currency exchange rate for a particular currency on a particular date. The model had a class method average which took a currency and a date range as arguments. Now from the name, Iâd expect that this would maybe calculate the average rate for instances for that currency between the first and last dates of the range. Lacking any tests/specs Iâll assume thatâs what it would do.
What did it do?
def self.average(currency, date_range)
first = find(:first, :conditions => ["currency = ? AND issued_on = ?", currency, date_range[0].to_date])
first ||= find(:first, :conditions => ["currency = ?", currency] , :order => "issued_on"
last = find(:first, :conditions => ["currency = ? AND issued_on = ?", currency, date_range[1].to_date])
first ||= find(:first, :conditions => ["currency = ?", currency] , :order => "issued_on ASC"
median(first.rate, last.rate)
end
protected
def median(a, b)
(a + b) / 2
end
See anything strange here? Actually I see several.
- To find the first it looks for an instance for the currency which happened exactly on the starting date. If it doesnât find one then it finds the first instance for that currency in the database. I would have expected to find the first instance on or after the starting date, which could have been done with a single find call with the right condition.
- Then to find the last it does what looks like a simple variant of the code to find the first, but is it a variant, instead of finding the last instance for the currency in the database rather than the first itâs going to again find the first. He wanted :order => âissued_on DESCâ.
- What kind of average is being computed here anyway? Average is a generic term, normally we take it to denote the mean, which is the summation of a series of values divided by the number of elements in the series. Weâre not doing that here, since we look at only two elements. Now the method being called is median, which is another type of average. You get the medium by sorting the series and picking either the middle element if the series has an odd number of elements, or the mean of the two middle elements. But thatâs certainly not what this median method is doing.
Having specs or tests would have helped this guy both think through what he was trying to do, and to realize that it wasnât working.
So to me it looks like the client had paid for some guy generating a rails app from some templates and plugins and then floundering around with no real idea of how to understand, let alone achieve, the goals of the application.
Like I said in the beginning, itâs not really that you get what you pay for, more like you sometimes get what you pay for. Since I donât know the rate the client was charged, I donât know whether he got what he paid for by hiring someone deservedly cheap, or just didnât get what he paid for.
Itâs always a shame when customers donât get what they pay for, but in the current economy, itâs a real shame when a client burns money hiring someone on the cheap or without real credentials instead of paying someone who might at first look more expensive but would be a bargain in comparison in terms of value for money.