My Ruby Regrets

by Jeff Langr on December 8, 2010

My Ruby Regrets

This guest post is by Jeff Langr, who has developed software for thirty years, mastering many other languages (including Smalltalk, C++, Java, and currently C#), but just not Ruby and Python… yet. (Ever?) He owns the consulting and training company Langr Software Solutions, and codes full-time as an employee of GeoLearning. Jeff is the author of close to a hundred articles on software development and three books, including Agile Java and the very-soon-to-be-published Pragmatic Programmers “book,” Agile in a Flash.

Jeff Langr Ruby guru? Hardly. Even though I first “learned” Ruby about nine years ago, perpetual Ruby newbie is a far more correct term. In those nine years, I’ve coded here and there on a number of throwaway scripts. For each separate effort, I would get past novice struggles to the point where I felt reasonably comfortable with the language. But just as I started to enjoy high levels of productivity, the job was done and it was back to “enterprisey” Java coding. Months later, sometimes more than a dozen, I’d work another Ruby script. The cycle would start again at a slightly higher proficiency level than the last time I started. My feeble brain would struggle to recall any remnants of my Ruby memory. It’s impossible to become a guru this way!

I’m once again working on a side effort in Ruby, pairing with a few other developers to build a testing framework. It’s intended for inbetweeners (my newly coined term for QA people who are willing to get a little technical) to easily put together Watir-based tests. As usual, it’s a small effort, a few days at most. As usual, we (me and my pair partners) decided to just “hack at it.” Never mind adhering to good OO design concepts, and never mind test-driving the code. Why?

  • it’s a small effort that should stay small over the long haul.
  • we’re (re)learning Ruby, which means we’re experimenting in irb and pasting over code that appears to work.
  • it’s a test tool itself. Do we really need to write tests for a test tool?

I’d also paired to develop a couple comparably scoped (that is, small) Python scripts in the earlier few months. For similar reasons, we eschewed test-driven development (TDD) and good OO design on those efforts too.

Fortunately, our constant companion Humility is one of the best teachers. It doesn’t take long to generate legacy code (code without tests) to the point of regret. In the case of my three most recent scripting efforts, the point of regret was somewhere after a half day of code cranking. What happened? Looking more closely at our three arguments for hacking it out, we can easily find flaws.

We quickly slammed out a couple hundred lines of Ruby code. Small, yes, but we quickly found ourselves often unsure about what code was where, and we knew that we had a good amount of unnecessary duplication. But our only verification mechanism was to manually run the test framework against the handful of scripts that it drove–a cycle that took about 90 seconds. We couldn’t make the rapid, ten-second changes that we wanted to–no one wants to wait more than a minute to verify every two-line code change.

As far as the learning Ruby part, we know that there’s a Ruby way to express code, but our novice Ruby brains tend to code it a little more familiarly first. Without unit tests, we’ve been a lot slower to transform the ugly procedural-isms into tight Ruby-way constructs. Unit tests are great for letting you slap together a method’s implementation, and then safely play with improving its expressiveness.

From the design standpoint, it’s a little more work to create proper classes, but inevitably we found that the lack of good design just made for crappier code and more duplication. We also found that mixins, while very beneficial, can create some interesting challenges and confusion if you’re not careful with them. Moving to a more OO solution simplified our codebase and gave us more flexibility.

We quickly wished we had built more tests. As we got more frustrated with dumb mistakes that took a while to pin down, we started building some TAD (test-after development) tests. At least these tests let us put a stake in the ground, but it’s unlikely that we’ll have the time to go back and completely cover the code. Had we started with TDD, we would have avoided getting bogged down in the defects. We also would have been able to keep the code base small, and exhibiting minimal duplication and confusion.

I didn’t do things the right way the past three times I’ve built small scripts. But next time I code in Ruby–no doubt enough months away that I’ll have forgotten much of what I re-learned–I hope my feeble brain won’t have forgotten, once again, this important lesson. Don’t wait to test-drive, and don’t hack just because you can.

Feel free to ask questions and give feedback in the comments section of this post. Thanks and Good Luck!

Technorati Tags: ,

Posted by Jeff Langr

{ 7 comments… read them below or add one }

Glenn Goodrich December 8, 2010 at 7:53 am

I am in exactly the same boat, except I go back to .NET and Flex. Been a Ruby newbie for almost 6 years now. Sigh.

Reply

Clay December 8, 2010 at 7:32 pm

Me, too. What’s the best / easiest way to get started with TDD in Ruby?

Reply

Glenn Goodrich December 9, 2010 at 7:04 am

Clay,

I’d start with The RSpec Book (fresh off the press) at http://www.pragprog.com

I am in the middle of it now, and it’s very good.

Hope this helps,
Glenn

Reply

hgs December 9, 2010 at 4:32 pm

So what about Nick Sieger’s “War on Perfect”
http://blog.nicksieger.com/articles/2010/11/22/war-on-perfect
then? There must be forces that say a “Scrapheap Challenge” approach is acceptable under certain circumstances: “welding”, “angle grinding” and so forth with no “finite element models” to back up the design, just get the thing working well enough to win the race. Clearly that is not a universal approach, and you have to be pretty good to make it succeed when it is appropriate, but this never seems to be discussed in software construction. So what does it take to make that sort of thing succeed?

Reply

Jeff L. December 11, 2010 at 2:34 am

Most of us who do TDD aren’t so purist as to view perfection as a goal (although some of these OCD folks indeed exist).

The TDD rule of thumb: If you spent five-to-ten minutes putting code in place and getting it to work, you should spend about another five-to-ten minutes doing some level of cleanup on the junk you just put in. That’s not striving for perfection, it’s striving to prevent entropy from taking over.

I do occasionally write true throwaway scripts–but they are small and very identifiable as such. (Lately these are typically database-oriented things: crunch through some data subset and apply some complex fixes that would be a royal pain to do in SQL.) Even with these, I find that I often produce by-products, such as utility methods, that are useful in other contexts. Well, they would be if I were to take the time to do them properly, i.e. test, organize, and identify them to the team somehow.

Reply

hgs December 14, 2010 at 8:13 pm

I try to do that, but I’m being told in many cases that I’m perfectionist, and am trying to push back against that in myself. All the “worse is better” philosophy seems difficult to practice. :-) Apart from time, are there any other heuristics to guide non-perfectionism?
For people who don’t have the problem, such a question probably is meaningless, but….

Reply

Jeff L. December 15, 2010 at 5:20 am

Funny. Good question: How do you know when you’ve gone too far?

I think it’s almost impossible to go to far, and yet anything extra we do is perceived as “too far” by someone. Did we have these debates in the 1950s about using subroutines? I’m sure we did. Why not just lump everything into main() and be done with it?

Time to market is valued highly by the business, and yet we have Deming telling us that quality pays for itself. Sadly, we really never try to assess the true cost of “worse is better”–has anyone ever added up the cost of code & fix + defects in production + losing customers due to crappy software + increased time to add new features because of difficult/redundant code? Seems obvious to me that there are rampant “hidden” costs of slapping together a solution.

I use Beck’s simple design rule as one heuristic. Eliminating duplication and making code expressive is great, but if you don’t need a new method or class, you don’t need it.

You can always clean up the code to the point no one notices things took any longer.

Reply

Leave a Comment

{ 34 trackbacks }

Previous post:

Next post: