The quest for better code
I am largely dissatisfied with code that I usually write. Usually I write a working version, then a cleaner working version and then finally a cleaner Object Oriented working version. Obviously this takes some time but more on that later. Lately, I found Sandi Metz’s rules to be quite practical and reasonable. These are -
- Classes can be no longer than one hundred lines of code (I would even say sixty).
- Methods can be no longer than five lines of code.
- Pass no more than four parameters into a method. Hash options are parameters.
- Controllers can instantiate only one object. Therefore, views can only know about one instance variable.
These are great principle and I think if you combine them with SOLID principles and some functional ones (like immutability and no-side effects) you can write great code.
So I try to apply these principles in the code I write now. Being a Scala learner, I am also impressed by it’s functional nature and things like Option, which simply put, saves you from null (or nil) checks. For example in Scala -
case class User(id: Int, name: String, age: Int)
object UserRepository { private val users = List(User(1, "Rocky", 34), User(2, "Annie", 33)) def findById(id: Int): Option[User] = users.find(u => u.id == id)}
UserRepository.findById(2).map{u => u.name}.getOrElse("Not Found")//returns "Annie"UserRepository.findById(3).map{u => u.name}.getOrElse("Not Found")//returns "Not Found"
So in the example above, I did not have to check that my User returned is null or not. Usually this will take 2-4 line of code if written in an imperative style. Check for null and do an action and do another action in case a null value is returned.
More on Options here.
Back to Ruby, this is some code that I wrote lately -
def get_recent_tweets_from_favorites(current_user) tweets = []
if current_user favs = current_user.get_favs else favs = system_favs end
favs.each do |fav| tweets << get_last_three_tweets(fav) end tweetsend
While this is a relatively simple 4-5 liner method, the actual LoC are 10 when you include the if and each. This makes me a sad panda. I wish I had something like Scala’s Option to help me. But wait there is Rumonda! which makes my code above look like -
def get_recent_tweets_from_favorites(current_user) tweets = [] Option(current_user).map(&:get_favs).get_or_else(system_favs).each do |fav| tweets << get_last_three_tweets(fav) endend
It works! Now we have a simpler method with 4 lines of code (I cheated a bit) and we have used Monads, knowingly or unknowingly :) Happy programming!