Style Matters

Does style matter to you?

Here is the Martin 15-seconds test about whether style does matter to you or not.

Let’s take a look at this code:

class Blog_engine
def initialize
    @posts = []
end
def addPost post
    @posts <<post unless !post||@posts.include?(post)
  end
  def AllPostsTitles
    return @posts.collect{|post|post.title}
  end
  def allPosts_hash
    hall = []
    @posts.each {|p|
       hall << {:title =>p.title,:contents=>p.contents,comments:p.comments.map{|c|c.text}}
    }
    hall
  end
  def postByCategories
    by_cat={}
    @posts.each{|post|
      if by_cat[post.category] then by_cat[post.category]<<by_cat[post.category]=[] and by_cat[post.category]<<post end
    }
    return @posts
  end
end

Does this code make your eyes bleed? Why? It is perfectly correct Ruby code, and it does what it says. Still, I prefer to have it written like this:

class BlogEngine

  def initialize
    @posts = []
  end
                                                                              .
  def add_post(post)
    @posts << post unless post || @posts.include?(post)
  end
                                                                              .
  def all_posts_titles
    return @posts.map { |post| post.title }
  end
                                                                              .
  def all_posts_hash
    all_hash = []
    @posts.each do |post| 
       all_hash << { 
         title: post.title, 
         contents: post.contents, 
         comments: post.comments.map do |comment| 
           comment.text 
         end 
       }
    end
    return all_hash
  end
                                                                             .
  def post_by_categories
    post_by_categories = {}
    @posts.each do |post|
      (post_by_categories[post.category] ||= []) << post
    end
    return @posts
  end
end

If the difference between the two excerpts is important to you, then style probably matters to you too.

Style differences

Let’s dissect the differences a bit:

Naming

Ruby has opted for a mix of most used naming schemes: CamelCase for the classes and modules, snake_case for the methods, variable, and file names. While the convention is arbitrary (I use

CamelCase

CamelCase (Photo credit: Wikipedia)

CamelCase in Java and snake_case in Ruby without problems), having one standard helps a lot. The problem with the code above is not that it uses CamelCase – it’s the fact that it is mixing naming schemes for the same kind of elements. In other words, it is bad, without even starting the (heated) debate about which format is “best”.

Sticking to one standard inside a project is very important for readability – good style allows the reader to understand the nature of various tokens immediately, just by how they are written.

Spacing (lines)

Not spacing between methods really hurts readability, as it impedes the reader’s ability to “make blocks” in his/her mind, forcing him or her to go back to the “line per line” deciphering – One cannot “jump” to a method if one first need to find it.

Spacing (characters)

Code lines are not random groups of characters. As such, spaces – again – help to decipher the parts. Lose the lines – and the code becomes gibberish.

While spaces are actual “void” characters for the machine, they are not void for other people reading your code. For the human readers, they are a necessaty  part of it. Once more – code for the human – not the machine.

If-else

While it makes no real difference to the machine, using double negation in alternatives has the same effect as in text – it makes the point difficult to grasp.

Blocks

Ruby blocks can use either do… end or {} (their only difference lies in their precedence order). Both are fine, but mixing them makes reading difficult. The Ruby guide push toward do… end for multi lines blocks and {} for single, which is as good as any other rules – meaning it is clear and unambiguous.

Aliases

Ruby has lots of aliases – methods that exist under several names: map & collect, reduce and inject or Array length and size. Ruby provides this range make everyone – notably smalltalkers – comfortable, and each choice has its merits. Just try not mix them.

Hashes

Hashes have a simplified syntax since Ruby 1.9, as long as you use Symbols as keys (which is a good idea in a large number of cases). Nothing was really wrong with the old one, but mix these and you’ll see colons in your nightmares.

Other reasons

I hope to have made my point about the importance of style for readability. If not – or for variations – do not hesitate to look at other sources.

In any case, there are actually other good reasons to adopt a common style in a team.

Promote collective ownership

Nothing impedes collective ownership more than being able to immediately identify the author of any snippet of code – “this is Mark’s code”. Using the same conventions is a small but important step towards the “this code is our code” objective.

VCS and Diffs

Changes in style introduce false positive in the VCS change set, whether in the form of conflicts or just unnecessary differences. A large set of “style only” changes can certainly be ignored when reviewing… until I hide a real change in the middle. Good luck finding it.

How to manage style

  1. Pick one. The most important thing is to standardize around something, whatever it can be (well, try to not be too creative). The Ruby community seems to have standardized around Bozhidar Batsov’s “Ruby Style Guide”, or its slightly diverging GitHub variant.

  2. Stick to a standard. Most conventions are good because just because they are conventions, and that is fine.

  3. Use tools to check style. Discipline is difficult, so don’t depend on it – have your SublimeText (or whatever you are using to edit Ruby code) do it for you.

Finally

Style is what separates the good from the great.
– Bozhidar Batsov

Will style save your code? No, but it can help saving it.
– Martin

Enhanced by Zemanta




8 thoughts on “Style Matters

  1. Pingback: Why Code Style Matters « Startup News Fresh

    1. Martin Post author

      Hi Alain,
      This is something where I confess a divergence from the Ruby Style Guide orthodoxy. Even if the return is unnecessary in situations like this in Ruby, I still tend to write it down. It make somewhat easier to “scan” the method to find the (various) return point(s).

      I agree that this is a personal preference (may be linked to my Java past, may be not – I like and use most of the Ruby idioms).

      Thanks – and sorry for your eyes.

      Martin

      Reply
  2. Pingback: Code Rules are Lines, not Walls | 8th color

  3. MIchael Gee

    You add_post method only appends the provided post if it is false or nil. Perhaps the || in the condition should be &&?

    Other thoughts are best expressed in code:

    def all_posts_titles
    @posts.map &:title
    end
    .
    def all_posts_hash
    @posts.map do |post|
    {
    title: post.title,
    contents: post.contents,
    comments: post.comments.map(&:text)
    }
    end
    end
    .
    def post_by_categories
    @posts.group_by &:category
    end

    Reply
  4. Pingback: One Year of PullReview | 8th Color

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>