Archive for June, 2009
Rating your code from A to F
Assuming that a piece of code is functional and that it complies with its requirements, that piece of code can be rated from A to F based in two parameters, the simplicity and the extensibility.
The simplicity is a characteristic that tells us how easy is to understand and read the code. In order to determine how simple a code is, the following can be taken into consideration.
- Good identifiers for Class names, variables, methods…
- Straight forward logic.
- Crisp and minimal abstractions.
- Low representational gap. The representational gap is how different is the domain logic of an application from its design, having a low representational gap means that the design has very few differences with the domain logic it represents.
The simplicity is classified in “Very simple”, “Simple” or “Not simple”
Very simple: Code that doesn’t require additional documentation to be understood, any developer can pick up the code and do changes without further explanation.
Simple: Code that requires some additional documentation to be understood, only developers with some background on the design and in the business domain knowledge will be able to do changes.
Not simple: Code that requires lots of additional documentation to be understood, only senior developers with a deep knowledge of the application will be able to do changes.
The extensibility is a characteristic that tells us how easy will be to add new functionality on top of the current code, don’t confuse extensibility with over engineering, which is a bad practice. In order to determine how extensible a code is, the following can be taken into consideration.
- Atomicity, use of DRY.
- Testability. Code that includes unit tests.
- Use of design patterns.
- Low coupling
The extensibility is classified in “Very extensible”, “Extensible” or “Not extensible”
Very extensible: To add new functionality on the top of the code is really easy, no refactoring would be necessary.
Extensible: To add new functionality on the top of the existing code is relatively easy, minor refactoring would be necessary.
Not extensible: To add new functionality on the top of the existing code is difficult, complex refactoring, even redesign would be necessary.
A- Excellent code.
A is the best rate for code, it can’t be more simple and it can’t be more extensible, the problem with A codes is that is almost impossible to achieve, usually there is a trade off between extensibility and readability, that’s why I always recommend to produce B codes.
B- Good code
B code is good code, B code will ensure that the maintenance of the application will be much easier. This code is the one I recommend to aim for.
C- Average code
C code is what some developers consider good enough, if we are under a tight deadline or high pressure we might have to aim for this rate of code. Maintenance of the application is harder than with B code but still is possible.
D- Dangerous code
With D code still is possible to maintain the application, but it takes a lot of time to perform any change and the risk to enter new bugs doing so is high, some teams don’t want to refactor D codes because they don’t want to change something that is working, that’s an error, D codes easily become E codes after a few changes and then it will become unmaintainable.
E- Bad code
E code is bad code, the application is unmaintainable, urgent changes are necessary, still there is time for reaction to change the code and improve it.
F- Start over again
Not only the application is unmaintainable, the code should be scrapped and created again from scratch
As explained in the article the pyramid of software quality, readability is one of the most important characteristics of good code. These are a few tips that may help you to produce it.
1.- Use a top down approach (TDD).
Using a top down approach is similar to TDD you start with the highest level logic: the validation of what your code has to perform (The unit test), and then you add layers of abstraction, which as you keep coding become more separated from the domain problem and more related with implementation details. This approach helps to produce more readable code because:
- Producing the unit tests will give you a general idea about how readable is your code. Readable unit tests generally mean readable code.
- Separating the implementation details and the domain logic makes them easy to understand.
2.- Make your design match your domain (LRG and DDD).
In the excellent book applying UML and patterns, Craig Larman introduces the concept of Low Representational Gap (LRG). The representational gap is how different is the domain logic of an application from its design, having a low representational gap means that the design has very few differences with the domain logic it represents. LRG and Domain Driven Design DDD are much related concepts as they both focus on designing a domain model that matches the real model.
An application that has a LRG will be much easier to read because the actual domain logic will match the implementation domain logic.
3.- Use abstractions only when necessary (YAGNI and KISS).
Sometimes is tempting to make everything too much flexible. As software developers we always have to have in mind these two principles: You Are Not Going to Need It (YAGNI) and Keep It Short and Simple (KISS), remember: Code is not better because it has abstraction layers that may be useful in the future, they are going to make your code harder to read, just use abstraction layers when you need them now, not just in case they are necessary in the future.
4.- Use good names.
Using good names in your code is critical, it will make a difference between a code which will be read as plain English and a code where you will be shouting WTF?!! every two lines.
In this article: tips to write variable names, there are a few tips that can actually be applied to name any named element not only variables.
5.- The 5 minutes explanation test.
Apart from using the previous 4 tips and some other guidelines you may think appropriate to create readable code, I like to pass the 5 minutes explanation test to my code to know how readable it is.
- Grab a fellow programmer who spares some time to review your code
- Walk him through your code, just a high level introduction.
- Ask him if he understood your code.
After following these steps you can evaluate how readable your code is based on how much it took you to explain it. I call it the 5 minutes explanation test because as a rule of thumb if it takes more than 5 minutes for the other programmer to have a high level idea of the design, assume that the code is not readable.
What is the Slow death march?
It’s likely that you have heard before about the slow death march applied to the software development, actually I’m sure that if you have a few years experience you would have been involved in a few projects where you have seen this happening.
This phenomenon responds to those dynamics where a team somehow knows they will fail to complete the project successfully, but no one does anything to prevent it.
When the team starts marching in this way, it is essential to detect it as soon as possible and to act immediately as the longer this dynamic is in the team, the harder will be to remove it.
Signs that the team has started marching.
There are many signs that may help you detect if your team is falling into this dynamic, but the main indicator is team motivation, the problem with motivation is that hard to tell when the motivation is going down until is very late. A few indicators could be.
- The team is starting to miss deadlines with apparently no reason. Could be an early sign that they are loosing motivation, concerns should be raised from the second deadline missed.
- Communication is not as fluent as is used to be. In a healthy team communication happens in an informal way almost continuously, if you detect that your team is starting to communicate less, probably is because motivation is going down.
- The team is been under high pressure for too long with no reward. This is one of the big motivation killers.
- Decisions have been imposed by management and the team doesn’t agree with them.
Motivation and broken windows.
In the book “The Pragmatic Programmer: From Journeyman to Master” Andrew Hunt and David Thomas explained the broken window effect:
One broken window, left unrepaired for any substantial length of time, instills in the inhabitants of the building a sense of abandonment—a sense that the powers that be don’t care about the building. So another window gets broken. People start littering. Graffiti appears. Serious structural damage begins. In a relatively short space of time, the building becomes damaged beyond the owner’s desire to fix it, and the sense of abandonment becomes reality.
If the motivation is low, people is not going to care about broken windows, if that happens, is likely that the team will find itself sooner than later in the slow death march.
Who is responsible for it?
Probably developers will think that the responsible for this dynamic is management, but that’s false, the responsible are all the parts involved in the project, including all the individuals of the teams, that’s you and me. If you are in a team that is starting to loose motivation, ask yourself:
- Am I helping the team to work better? Sometimes we are so focused on our own opinions that we forget that we are actually getting in the way of our team to get things done.
- Am I doing what the team is expecting me to do? A motivation killer is the lack of confidence among the developers of the same team, every meeting you have, do you find yourself answering “why have you been doing that for the previous days”? If so, you have a communication problem and the motivation of the team is going to be affected.
- How can I help? One of the easiest formulas to help building a great team is to offer yourself to help someone.
A great article related with this subject is “Dealing with Bad Apples” by Jeff Attwod via codinghorror.com
Oh my god! I’m slowly marching to death! What can I do?
That’s an easy one, simply do something for the project, anything! That can sound a bit ridiculous, but is true, team members in these dynamics usually spend most of the day in the Internet or simply starring the screen, the best remedy is to start doing something and to encourage other people to do the same.
This article may help you to focus on your tasks and not loosing motivation: 5 Tips for creating good code every day; or how to become a good software developer
Why debugging is important?
Debugging is the group of activities that software developers perform to fix a bug, so being a good debugger is a very important part of being a good developer.
3 reasons to consider debugging one of your main skills:
- We spend a lot of time debugging. By improving your debugging skills you are likely to change this balance so that you can spend more time coding new features.
- There are always errors in your code and you will have to debug them without introducing new errors. There are two famous quotes that illustrate this.
- Debugging is sometimes the only way to check that some code is working fine.
3 consequences of being a bad debugger
- Low productivity. Debugging is one of the main activities when developing software, bad debuggers will have to spend more time debugging and that will affect their productivity.
- Not being able to fix the real bug. One of the challenges when fixing an error is to identify it, errors are like icebergs: their visible part is just 10% of the problem, which is the only part that bad debuggers are going to look into, leaving the other 90% alone ready to cause some titanic catastrophe.
- Leaving collateral damage. Modifying code to fix an error has the risk of generating a completely new one, bad debuggers are likely to forget about this and will create some collateral damage every time they try to fix a bug.
For all the previous reasons, debugging has to be treated as an essential part in the software development, and the following steps will help you to be a better debugger.
7 Steps to fix a bug
The correct approach to debug is to follow a very structured procedure based on repeating the same steps for every bug, the reason for this is that bugs usually are symptoms of a much bigger problem going on, so in order for us to uncover the real nature of the error we have to make sure we do an exhaust and systematic revision of it.
The degree of formality when following the steps may vary depending on the bug, for critical errors is better to document every single step in a document, for minor errors, or bugs found while coding is just enough to follow them as a mental guideline to make sure we cover all the error.
Step 1. Identify the error.
This is an obvious step but a tricky one, sometimes a bad identification of an error can cause lots of wasted developing time, is usual that production errors reported by users are hard to be interpreted and sometimes the information we are getting from them is misleading.
A few tips to make sure you identify correctly the bug are.
- See the error. This is easy if you spot the error, but not if it comes from a user, in that case see if you can get the user to send you a few screen captures or even use remote connection to see the error by yourself.
- Reproduce the error. You never should say that an error has been fixed if you were not able to reproduce it.
- Understand what the expected behavior should be. In complex applications could be hard to tell what should be the expected behavior of an error, but that knowledge is basic to be able to fix the problem, so we will have to talk with the product owner, check documentation… to find this information
- Validate the identification. Confirm with the responsible of the application that the error is actually an error and that the expected behavior is correct. The validation can also lead to situations where is not necessary or not worth it to fix the error.
Step 2. Find the error.
Once we have an error correctly identified, is time to go through the code to find the exact spot where the error is located, at this stage we are not interested in understanding the big picture for the error, we are just focused on finding it. A few techniques that may help to find an error are:
- Logging. It can be to the console, file… It should help you to trace the error in the code.
- Debugging. Debugging in the most technical sense of the word, meaning turning on whatever the debugger you are using and stepping through the code.
- Removing code. I discovered this method a year ago when we were trying to fix a very challenging bug. We had an application which a few seconds after performing an action was causing the system to crash but only on some computers and not always but only from time to time, when debugging, everything seemed to work as expected, and when the machine was crashing it happened with many different patterns, we were completely lost, and then it occurred to us the removing code approach. It worked more or less like this: We took out half of the code from the action causing the machine to crash, and we executed it hundreds of times, and the application crashed, we did the same with the other half of the code and the application didn’t crash, so we knew the error was on the first half, we kept splitting the code until we found that the error was on a third party function we were using, so we just decided to rewrite it by ourselves.
Step 3. Analyze the error.
This is a critical step, use a bottom-up approach from the place the error was found and analyze the code so you can see the big picture of the error, analyzing a bug has two main goals: to check that around that error there aren’t any other errors to be found (the iceberg metaphor), and to make sure what are the risks of entering any collateral damage in the fix.
Step 4. Prove your analysis
This is a straight forward step, after analyzing the original bug you may have come with a few more errors that may appear on the application, this step it’s all about writing automated tests for these areas (is better to use a test framework as any from the xUnit family).
Once you have your tests, you can run them and you should see all them failing, that proves that your analysis is right.
Step 5. Cover lateral damage.
At this stage you are almost ready to start coding the fix, but you have to cover your ass before you change the code, so you create or gather (if already created) all the unit tests for the code which is around where you will do the changes so that you will be sure after completing the modification that you won’t have break anything else. If you run this unit tests, they all should pass.
Step 6. Fix the error.
That’s it, finally you can fix the error!
Step 7. Validate the solution.
Run all the test scripts and check that they all pass.
Plus a few recommendations…
Document the bug and the fix.
Document each step, the code changes, and the new unit tests, add also the unit tests if possible to your build and to your regression test routine.
These three technical considerations are actually part of Joel’s article, The Joel Test: 12 Steps to Better Code
- Use the best tools money can pay to debug.
- Use a source control tool.
- Use a bug tracking system.
Preventing and helping to find future bugs.
Log is the developer’s best friend when it comes to find errors in a production environment, make sure there is always a log in your application, that is readable, and that it is helpful to track down what the application is doing.
I have recently published an article called “10 commandments for creating good code“, in which the commandment number 8 says “Comments are evil”, well, as I was expecting this has created some controversy and a few comments have been written in dzone.com and news.ycombinator.com arguing about comments being evil, so I have decided to write this small post where I hope I can bring some light into what I really mean when I say “Comments are evil”.
Code quality scale
I believe most of the developers will agree with the following scale.
The worst code is: Bad code without comments.
Bad code is: Bad code with comments.
The average code is: Average code with comments.
Good code is: Good code with a few comments…
…but… Great code doesn’t need comments!!!
First commandment in the “10 commandments for creating good code” is DRY (Don’t repeat yourself), I believe this is the main guideline for a software developer, what most software developers don’t see is that every time a comment is written, you are repeating yourself! Now not only you will have to perform maintenance activities on the code, but you will also need to make sure that the comment keeps its synchronicity with the code. Not a big deal? Well, think in all the other principles you follow when you code, each of the small changes you perform in your code to make it better, like spending time to name a class, moving a method to a different class because it just feels right… are insignificant by themselves, is when you put all them together when you can tell that you have created a masterpiece of code.
Should we use comments at all?
Of course!!!!!!! My mistake was to name the commandment “Comments are evil”, is just a guideline, as any other principle you apply in software development, sometimes we just produce crappy code, for many reasons, we are under pressure, we don’t know the technology we are working with… Then for the love of god, use comments!! There are other times where we have prodcuded good code wich has a few comments, that’s fine, we cannot always look for the perfection. What “comments are evil” really means is that you should always push yourself to use as few comments as possible, not because you are lazy, but because they are not necessary.
1.- DRY: Don’t repeat yourself.
DRY is usually the easiest principle to understand, but it is quite harder to apply. It means that when finding similar code in two or more places, we should abstract them into a new method and change the previous code fragments so they will now call the new method with the appropriate parameters.
DRY is maybe the most universal coding principle, I have never found a developer who would argue that repeating code is good, but, I have found developers that forget about this principle when coding unit tests, as an example: imagine that you have changed the interface of a class which had lots of unit tests, if you haven’t used DRY, you will have to manually change the call to this class interface to match the new signatures to each test case.
2.- Write short methods.
There are three very good reasons for writing short methods.
- Your code will be easier to read.
- Your code will be easier to reuse (short methods are likely to produce loose coupling).
- Your code will be easier to test.
3.- Use good names for your classes, methods and variables.
There is nothing nicer than using some other developer code and not having to read its documentation because the names of the classes and the methods are telling us everything, so, make everyone’s life easier and take this approach, expend always a few seconds before naming any element in your code.
4.- Assign the right responsibility to each class.
One class, one responsibility, that will sound familiar to those who know about the SOLID principles, but not any responsibility, the right responsibility, so if we have the class Customer, we won’t assign to it the responsibility to create a new sales action, we will just assign it the responsibility to handle all the data related with a customer.
5.- Keep your code organized.
This organization is at two levels.
- Physical organization: Whatever the structure you are using, packages, namespaces, folders… Organize your classes in such a way that is easy and intuitive to find where the code is stored.
- Logical organization: Whatever belongs logically together should have access to each other members, but what belongs to a different logic structure has to access them by an interface. These logic groups are commonly implemented as layers, services…
6.- Create lots of unit tests.
The most tests you have, the better, they are our safety net for all the changes we will have to perform in the code in the future.
7.- Refactor often and sooner.
Software development is a continuous discovery process, in order to keep up to date with good code that matches the new/changing requirements is essential to refactor the code as we go. As this is a risky task there are 2 main preconditions to avoid entering new bugs into the system.
- Have lots of unit tests.
- Do small refactor steps at a time. In software development there are very few things more annoying than start refactoring 2000 lines of code to after 3 hours of work realize that is necessary to roll back to the original version because now nothing works and the track of which change is causing the problem is lost.
8.- Comments are evil.
This particular one is a bit controversial, most of us were taught that comments are good, and actually it’s better to have a comment in an obscure piece of code than just having the code by itself, what this point means is that: even better than having a comment for an obscure piece of code is to not to have that code at all, just refactor it until is a nice and readable piece of code. [EDIT] Please read this other post for a better explanation of what “comments are evil” means.
9.- Code to an interface, not to an implementation.
This is a classic one, coding to an interface will free us from the implementation details, we just define a contract and rely on calling the defined operations on the contract, expecting that the actual implementation will be passed to our code or decided at runtime.
10.- Have code reviews.
We all make mistakes, and there’s nothing better than asking some other person to have a quick and informal review in our code to find them, in order to make the reviews, it’s better not to wait until the code is completed, it’s better to ask for reviews whenever some important part of the code has been completed or when a few days have passed from the previous review.