Showing posts with label improvement. Show all posts
Showing posts with label improvement. Show all posts

Saturday, February 8, 2014

The Best Programming Language


This post is not about Java vs .Net or Python vs Ruby or any other programming language in particular. This post is about the programming language that is common to all of them. We call these technologies languages but in fact all of them are written in English (well aside for a few). But unlike English article or blog post, programs are often very hard to read. In this post I want to share with you how I try to keep my code readable and maintainable.


Inflation of comments


When I started to program professionally, more than ten years ago, I felt it was pretty hard for me to get into others' code. I was having a hard time reading it. I wanted to make sure my code wouldn't look like that. So, as we often do, I made sure my code was filled with tons of comments. Let me demonstrate with an example (code taken from my refactoring hands-on post, comments added for this post):


That's not the whole class but it pretty much reflects the idea. Now, take a second and try to figure out what this code does more or less.

I bet you didn't read the comments. And if you did, that's only because I have been writing about comments from the beginning of this section. Most of the times we are blind to comments, we ignore them. Especially if there are many of them and we're already used to them stating the obvious. Do I really need a comment stating that this is THE item Dao?



All of the comments in this code are pretty much the same, stating the obvious. This is not readable code, this is cluttered code that needs to be maintained twice - once for the code itself and once for the comments. Problem is - comments tend to rot. We don't have to maintain them, the computer doesn't care what they say, the computer only cares about the code. So, we tend to neglect them. I bet most of you didn't even find the copy-paste mistake hidden in the comments.



Deflation of comments


After I realized that I was inflating comments, I started to deflate them. I wanted my code to feel that when there's a comment, it must be read, it's important. I eliminated all of, what I call, "The Duh" comments.



I only added comments when I really needed to explain what I was doing. For example, what I like to call "The eeeefff if" (because it's such a code-smell):


No way you can understand what this if-clause checks on the first read. You'll have to read it at least twice, and usually 3-4-5 times, to be certain you understand the code. In this kind of scenarios I had usually put a comment that stated what this clause was supposed to do: "Checks if the document has a title or if we can extract a title" (for the sake of the example - let's assume we can extract a title from video/image). Ok, that's a bit clearer now, but we need to consider two things:
  1. People will still, most likely, try to read the code before the comment (assuming they won't ignore it completely).
  2. Even after reading the comment, it is still not clear why we are so certain we can extract a title. The if is complicated, and we can't be sure the comment is aligned with the code and hasn't rotten yet (at least without checking the source-control history).
We'll get back to this example later.


/* No comment */


At the last stage (so far...) in my journey for writing readable code I realized, with the help of Uncle Bob in his book Clean Code, that I should make my code readable without comments. This may sound extreme at first, "A code that is readable without any comments?", well... yes. Aside for some very specific cases, you should aspire to avoid comments in your code and just write code that is readable on its own.



Writing code in English


If you manage to write code with each line looking like a sentence in English, each method is a paragraph telling about a very specific subject of the big picture, and each class tells only one aspect of the story - you will not need almost any comments, and your code will be readable. And also as a beneficial side effect, well-designed and maintainable. Uncle Bob compares this type of code to a newspaper where you have articles (classes) with headers (high-level method abstraction), sub-headers and text which both represent different levels of implementation details.

The two main ways in my opinion to achieve such code are:

  1. Descriptive naming
  2. English-like sentences

Descriptive naming


There's just so much to say about descriptive naming which makes it a topic for another post. I'll just give a few pointers for good naming:

  • Give clear descriptive names which can be used in English-like sentences (will be described in the next paragraph)
  • Avoid using abbreviations
  • Stick to conventions
  • Avoid making the reader scroll up/down the page to figure out what a variable/method/class is meant for. With good naming, usually no scrolling will be needed.
Giving names to things is amongst the programmer's most difficult tasks. I suggest that when you write the code, start with any name (even foo for a method), and improve it as you go along (but before you commit or ask for review).


English like sentences


Try to maintain the reading flow of your code-lines. Make them look like English sentences. For example:



You can read the if sentence like an English sentence "if store has client - currentClient". That is far more readable than doing something like:




The same goes for the ugly "if" we saw before. We can extract it to a private method in the Document class that simplifies the question in a form of an English sentence "if doc has title":

The new if form

The Document object will encapsulate the answer to the question by calling two private methods that each simplifies another aspect of the question: 


Each part of the question is simplified in its own method


This way completely abstracts for the readers the question "has title?" and its two sub-questions - "already has a title?" or "can a title be extracted?" The proximity of the methods in the code also counts, but I won't get into that in this post.



Is it ever ok to use comments?


Of course! Comments are needed for java docs, they are needed to explain a nasty hack, explain why you preferred one algorithm over the other (when it's not obvious) or add a TODO comment (but if it stays there for more than a few days - it's also a code-smell). Also, each rule has its exception and sometimes comments may be needed to explain the code. But try to make it the exception, and not the norm, to use comments to explain yourself.


Further Reading


This post was longer than most of my posts and I haven't even started to scratch the tip of the iceberg on the subject. I invite you to read Uncle Bob's Clean Code in order to improve your code-writing skills. Also, don't hesitate to contact me (via Twitter, Linked-in or just leave a comment here) for more help on the subject.

I also invite you to send me code examples with explanation comments (even if we don't know each other yet) and we'll try to figure out together how to remove them and create a clean, readable code instead.



Find me on Twitter: @AviEtzioni



More interesting posts from this blog:

Monday, January 6, 2014

Hands On: Effective Refactoring of Legacy Code

In my previous post I talked about refactoring. At the end of it I promised to give a hands-on tutorial on refactoring.

The last two weeks at Outbrain were great fun when we had our "Quality Time". In this quality time we invested two whole weeks in reducing our technical debt. As part of this quality time, people gave tech-talks about quality. As a fan of code-quality and continuous improvement I gave a talk about my team's first retrospective and another talk which was a hands-on tutorial on refactoring legacy code. Here it is, I hope you'll enjoy it. 




This talk is based on Testing and Refactoring Legacy Code which I really liked so I decided to translate it to Hebrew and change it to include common smells in Outbrain's legacy code. 


Find me on Twitter: @AviEtzioni



More interesting posts from this blog:

Friday, December 27, 2013

Refactoring - Invest in your future

Legacy code


Legacy code - This phrase gives most of us the shivers. We imagine legacy code as a messy, unreadable, complicated, written-with-old-technology-and-conventions code. We embrace the dreadful phrase "if it works - don't touch it". We try to avoid as much as possible touching this horrible thing called Legacy code.

Smelly code: Picture taken from here

What exactly is legacy code?


I tried to get an answer to this question. Is it a code written using old technology? A code written by somebody who doesn't work for the company anymore? A code that is very fragile or complicated?

Well, I guess all of the above are correct. But I believe code becomes legacy code the moment it is too intimidating to be changed. No matter what the reason is. If you wrote a new feature today, which took you a lot of mental energy and time, and tomorrow you'll be afraid to change it, then I've got news for you - you have a new-born-healthy-two-days-old legacy code. Congratulations!!!


Refactoring


As time passes, our code becomes harder and harder to understand and maintain. The developers who touch the code come-and go, functionality evolves which causes much more flows that serve an ever-growing demand of use-cases to appear, technologies change but each of them leaves its signature on the code-base. 

Refactoring is our way, as engineers, to clean up the mess.  Some people think that refactoring is a matter of aesthetics. While this may be true, aesthetics is merely a mean to an end - making the code more easily-maintainable and more readable to others. And when I say others, I also mean - the future you. You are the one who most likely will read this code in the foreseeable future, and I bet you prefer not to struggle with it.


Invest in your future


A lot of engineers fail to realize that refactoring is an investment. You invest in order to save much more later. Every minute that is spent trying to understand the code, is a minute wasted. If the code is read by different people, in different times, and each of them struggles to understand it because it's complicated - that's a lot of time wasted. If only one of them would break the habbit and invest in a one time effort for a better future - a lot of development time would be spared and could be invested in new features.



The fear factor


And still, people, mostly, but certainly not exclusively, juniors, afraid of refactoring. And I have to admit, there are good reasons to be afraid. How do you start to read this code? It's so complicated and full of variables and branches and try-catches and too many lines of-code and code-duplications.

It's also very intimidating to refactor when the person who wrote most parts of the code does not work with you anymore. Or maybe it was you who wrote it but you just can't remember why the hell you did it this way, but you're sure there was a reason.


Eliminating the fear factor

Luckily we have so many tools in our disposal that, when you think about it, completely reduce the amount of fear one should have when refactoring.

You can always revert back to the way things were before the change using the source control.

Tests are key for an efficient refactor process. You need to have tests that test the code. If you don't have tests (or don't have enough) - write tests before starting to refactor. Not only will it improve your confidence that you don't break anything while refactoring, but it will also help you to better understand the code. 

Try to find ways to control and monitor your changes. For example, in Outbrain, we use feature flags in order to control new features. When we want to implement a new change, we create a flag that instructs the service on whether to use the new feature or not. This can be easily used for refactoring. And of course, it helps reverting in no-time. Just turn-off the feature and you're back to the way you were before the change. We also monitor a lot of metrics in every part of the flow. Usually, I can easily monitor the behaviour of a flow and see if anything changes. Even if you don't have advanced monitoring tools, you could utilize the log for monitoring the changes. Find your way to control and monitor the changes, this will greatly ease your fear.

Suspect comments. Comments are deceiving. They lead you to think that the code does something which it may or may not do. Computers don't understand comments, they just read the code. You should trust the code. If there are comments, use them as a guidance, but be very suspicious about them. Comments rot much faster than code. Also try to write code that is readable without comments when you refactor.

Take baby steps. Don't ever ever ever try to make too many changes at once. Always do one simple change and try to test it. You should allow yourself to be certain what was the exact change that caused problems. Don't make a project out of it. Refactoring is not a "all or nothing" deal. Every change you make, even the tiniest one, is blessed.

Boy scouts rule. Aside from taking baby steps, try to embrace the boy scouts rule - always leave the place cleaner than it was when you got there. While you work, do minor refactorings all the time. It's pretty easy and doesn't take a lot of time or effort. If everybody does that, the accumulating affect will create a much more robust code-base.

Invest only where necessary. If you see an ugly code which is very rarely touched, don't refactor it (aside from, maybe, very minor refactorings). It's not worth the effort. Invest in places to which you come back again and again. 

Use refactoring to learn. You already need to change and extend a piece of code. While refactoring you make the code readable for yourself. You start with minor refactoring, and bit-by-bit you unravel the code and learn how to extend it as you go.


What's the worst that could happen?


When I started working at Outbrain, I had an on-boarding with Ori Lahav, Co-Founder of Outbrain. Ori told us to embrace the phrase "What's the worst that could happen?" and told us to not let fears limit our progress.

When you refactor you should think exactly that - what's the worst that could happen? If you're not touching an extremely sensitive component, then it's no biggy. If you do refactor a sensitive component, there are probably, and hopefully, enough tests around it, and monitoring, and backups, and failover mechanisms and so on. And, of course, you'll take baby-steps and test and monitor the change carefully. So go ahead and refactor. What's the worst that could happen??

What's the worst that could happen?

My next post will include a video tutorial that shows how to refactor a piece of legacy-code using TDD. Hope you already can't wait!


Find me on Twitter: @AviEtzioni



More interesting posts from this blog:

Friday, November 29, 2013

My Retrospective About My Retrospective

Business as (too) usual


Back when I was working for my previous employer, we used SCRUM as our development methodology. As part of this methodology we used to end every sprint (3 weeks of work) with a retrospective.

During the retrospective, the team would sit down together and discuss what had been done well and what had not.

This was a recurring ceremony in which the team-leader guided us into talking about the painful pain points (along with the good things we've done - we don't want to be ALL negative). We used to discuss various issues; a lot of those times it was done with a lot of enthusiasm, maybe even some arguing.



Let's make things more exciting


During one of our 1x1s, my team-leader told me that he felt like we had to shake the dust from the retrospective process a bit, make it more exciting and, more importantly, more productive. His suggestion was to have a rotation between team members and let each of us lead a retrospective. We were encouraged to freshen things up, any way we deemed fit.

What should I do?


I started thinking about what I wanted to do when it was my turn to lead a retrospective. Should it be around one specific topic? Should it be a game? Should I stick with the original format?

I decided to think about what was bothering me in our retrospective process. It was a sort of a retrospective with myself about our retrospective.

I narrowed it down to 3 issues:
  1. Priorities: I realized that we don't always address the most important issues. We let our emotions, instead of our common-sense, affect the topics we discuss. Instead of finding 1 or 2 things that can improve us best, we discussed things that really annoyed us. We could not always change these things , or know that the change would have a great impact on the team or productivity, even if it happened.
  2. Not everyone's voice can be heard: Some people are more passionate when they speak, some are shy and some are more verbal and can convince others. It's hard to let everyone be equally heard and it was really important for me to try.
  3. Action items: We all want to change the world, or at least our organization, for the best. But we have to be realistic and set feasible goals that, in each sprint, would improve us a little more. 
So I decided to create a flow in my retrospective which will allow everyone to speak their minds, and will lead to an extremely focused set of 2-3 feasible and improving action items.


So that's what I did


Step 0: Preparations


I booked a room with a big erasable board. Before the team arrived I wrote down all of the stories we worked on during this sprint with different colors (in order to make it very clear where we were at the end of this sprint):


  • GREEN - ACCEPTED
  • ORANGE - IN PROGRESS
  • RED - NOT STARTED
I put some papers and pens on the table for the team to use later on.


This is how the board looked like when the team arrived



Step 1: Map the problems


I asked the team (myself included) to take 3-5 minutes and write down the four impediments which bothered them most. After all were finished we took turns in reading our notes and then wrote each of those on the board, near the stories that were already written there. Everyone could speak their minds about what was bothering them, without interruptions.



In Step 1 we added our painful impediments


Step 2: Suggest solutions


Pointing what's wrong or what's painful is fairly easy. Coming up with a solution to the problem, may sometimes be a bit more complicated. I, again, asked everybody to write down their suggested solutions to the impediments that were raised. When finished we took turns of reading the solutions and wrote down these suggestions on the board as well. This uncovered two interesting issues:
  1. Unsurprisingly - some of the problems had the same solution
  2. For some of the problems we couldn't find a solution with a good ROI (ROI = Return Of Investment: Meaning - all the solutions we found weren't worth their effort) 
This step made us focus on a smaller set of problems and their solutions. And again, everyone had their chance to suggest solutions.


Lastly - we added our suggested solutions


Step 3: Let's vote!


Lastly, I told the team that each team member gets 10 points. Those points were used for voting on the solutions that were suggested in the previous step. Each team member was allowed to distribute the points in any way (s)he wanted to. At the end, the top 3 solutions were chosen as our action items for the next sprint.

Step 4: Leave with a good taste


Before wrapping up I asked everyone to say what they thought was good during the sprint. I wanted the retrospective to end with a positive note. Also, it's important to preserve what we do well.


Conclusion



After the retrospective, I again did a little retrospective with myself. I tried to figure out if I managed to address the 3 problems I tried to face at the beginning. The democratic nature of the retrospective allowed everyone's voice to be heard, as I wanted, and prevented unnecessary arguments about the priorities. Also, at the end, we had to vote on some action items. Having time to think about what's really important and what we can really change, made us choose action items that were feasible. Those action items were tracked during the next sprint - I sent an email that sums up the retrospective right after this meeting. During the sprint I sent a reminder when we encountered an issue that was brought up in the retrospective. Finally, before the next retrospective I sent an email describing our decisions from the previous retrospective and whether we applied the action items or not. At the next retrospective we made sure we followed on those action items, but that's another team member's story to tell :)


A word of thanks to Gal Zellermayer, my team-leader at the time, who pushed me into thinking out of the box and conduct this retrospective.



Find me on Twitter: @AviEtzioni



More interesting posts from this blog: