Writing Pretty Code — Ruby Style

Michael McKee
6 min readSep 17, 2018

--

Most people do not think of aesthetics when writing code. Sure, proper use of white space and indentation are common. But can we go deeper than that? Good code should first and foremost run efficiently while being readable by human beings that create, maintain, and debug that code. This is all fine and well, and is certainly important. But as someone who loves organization and uniformity, writing code to me is not just about best practice and implementation. It needs style, and a little bit of flavor, and most importantly, consistency.

There are certainly a ton of articles on this topic. This is just my attempt to express how I generally like to organize things on a visual level. I believe we can find the expression of art and design in anything in life. It is everywhere. And few things are more important to me than intentional organization and fastidiously implemented style. Ruby is great because it generally lets us do these things how we wish, and will usually ignore our indentation and use of white space. This allows us great flexibility in how we can indent, style, and use a bit of Ruby’s great syntactic sugar to help make our code not only run well, but look great in an editor while spending hours pouring over it.

Consider the following example:

An abomination of indentation

Even very new programmers with a couple of lines under their belt will know what is wrong here. It almost hurts to look at. But this is often the first step. Learning how to indent blocks properly is key to making readable, pretty code. We can start here by putting the proper spacing for each line. The Ruby convention is 2 spaces for each “nest” of code. Here is a fixed example:

Phew! Much better. We can now see what belongs to what. Indentation really brings out what is responsible for what, and allows us to intuitively know the scope of each line. But there is more we can do here! Under no circumstances will our attr_reader and attr_accessor not run the way they currently are. But I believe there is a prettier, more organized way we can display those declarations.

Not bad!

Breaking each attribute declaration on each line is much easier on the eyes. Now we have a list of attributes we are assigning to each accessor. There is one more step which I like to do, which aligns these columns evenly.

Now we have a nicely indented, line-broken attribute accessors. It is easy to tell which ones are only readable, and which ones have full access to both read and write.

Let’s take a look at the next part of this class. The initialize method. There is a lot going on here.

This is generally readable. However, with so many assignments going on, it is a little rough on the eyes. I think we can do much better here. Let’s fix this now.

Aligning all of these assignment operators on the same column makes for a really nice, consistent, and readable code block. It is easy to tell what is going on even faster than looking at it before, when it was just properly indented. We simply align each assignment operator on the column with the longest instance variable.

Let’s take a look at the entire InvoiceItem class now and see how it looks.

Much better!

We now have a class that is easy to read and understand on just a quick glance. Scope is easily identifiable, assignments in our initialize method are easy to pick out, and our attribute accessors are much easier to digest.

Let’s take a look at a few more opportunities for improvement elsewhere. When we are first learning how to write code, writing tests can be cumbersome due to a lack of tool knowledge. We often spend time writing out fake data. There is no harm in this, but sometimes it can create messy test files which are hard to read. Let’s take a look at one now.

Uh oh! In this particular setup for a test, we are first creating a new repository to hold transaction classes. The testing suite requires we have a couple of different ones, so we create three just to test around with, and throw them into a hash to use later. However, the class we will be instantiating requires quite a bit of attributes to begin. And each of the attributes have long names, and even longer values. What can we do to make this fit in our view area, so we can see what exactly is going on? Simple:

We have broken up each of these hashes with line breaks at each value declaration, and have added the same spacing we did before in our initialize method in the InvoiceItem class. Now it is possible to see all of our test data in a smaller view area in our editor. We can easily see which keys have which values, and if necessary, can change these easily without any scrolling or searching for the key.

This particular strategy can also be used in method calls which have lots of attributes to enter as well. See the example below:

Once again, we run into the same issue. We can’t even see the entire method creation! Working with code like this is an absolute pain. We have to scroll way past our view area to see what attributes we are giving this create! method on User. Let’s fix this the same way we fixed the hashes in the previous example.

Now we have something much more readable and human friendly. We can clearly see all of our attributes, and what we are assigning to each one. Again, this makes it much easier to change information when needed, and makes debugging a whole lot easier if something goes wrong.

We write code to not only work efficiently and correctly, we also write code to be read by human beings. Just like this article, different trains of thought are broken up into paragraphs and smaller, easier to digest chunks. It can be beneficial to separate logic in our code by whitespace around each ‘idea’ and action.

Finally, one of my favorite features of Ruby. Consider the following lines of code:

Here we are taking each element in animals and applying the capitalize method to it. This looks just fine. But we can shorten up our code a bit and make it a bit more readable for a human.

Calling the capitalize method like this shortens up our code a bit, and now we can simply say “give me animals and capitalize each one.” This is powerful and very flexible for simple operations like this. And it works in many, many more situations with all sorts of iterators and enumerables.

This topic is mostly trivial, but a little care into using proper white space, Ruby’s syntactic sugar, proper indentation, and good use of line breaks can make your code that much easier to read and maintain. It may take a bit more effort and time, but I believe it is well worth it.

--

--