Over the last number of years, I have learned the following practices that I believe are worth sharing. While there exists the perception that the best devs are those who can describe a product or feature in gory details and implement very complicated algorithms, the truth is far from that. The following are some hallmarks of the best developers.
#1 Unit test everything
Unit tests are absolutely critical for shipping high quality shipping code. Too many times code is thrown over the fence to testers that simply does not work. Unit tests force the developer to make sure all of the code paths work as expected. It does not prevent the code from being bug free, but certainly makes the tester work for his/her bugs. In addition, in very complicated code unit tests are extremely useful for preventing regressions. In this respect, unit tests speed up coding rather than slow it down. When an area has very strong unit tests, I feel more comfortable making changes to a core area because I know the tests will let me know if I break something down the line.
#2 Make everything testable – even that which is difficult
When developing unit tests, it is often tempting to avoid unit testing an area that is extremely difficult to unit test. The best developers, though, will figure out a way. There are a number of techniques that allow one to unit test anything – such as stubbing out other areas. Remember that unit tests are not intended to be end-to-end tests. They are intended to test that a specific area of code works as designed.
#3 Be paranoid
There’s a common adage out there that the best developer is someone who looks both ways before crossing a one way street and in several respects this is true. Always expect that someone will try to do something malicious with your code or use it in a way you wouldn’t recommend. If you make a method public, always assume that each piece of data could be faulty. This paranoia will significantly reduce bugs down the line.
#4 Always ask the “why” question
Whenever someone comes to you with a feature request, always make sure you understand the “why” part. Why do they want this particular feature? What are they trying to accomplish? Sometimes the feature requested may be quite complex, while in reality what really solves their problem is something much simpler. Never think of things as “features” but instead as “solutions”. Your goal is to solve a problem, not to add a specific feature.
#5 Seek to reduce code
It is a known fact that the more code there is, the higher the probability for bugs. Some perceive the best devs as those who write the most code, but the truth is the best devs are those who prefer to write the least. Code is bad, and the goal should be to try to avoid it as much as possible. You can do this by always seeking to not reinvent the wheel and reuse code as much as possible. Also, the best designs often contain the least amount of code.
#6 Make designs flexible, but only code what is needed
It’s great to create flexible code that allows you to easily change behavior as necessary. However, always keep in sight what you actually need to build. Do not make something configurable for users of your code if there is not specific ask that it be configurable. Instead, design your code in such a way that if it does need to be configurable at some later time, you can easily add that functionality. Remember that every time you provide someone with the ability to change how your code behaves, you also increase the probability of bugs. Therefore, design your code in a way that you can easily add functionality as necessary, but do not expose this flexibility outside of your code unless their is a specific known need.
#7 Never make assumptions
Very often it is extremely tempting to say “well, they should do this”, or “I assume this bug occurred because they did this”. Never assume anything. Whenever you find yourself saying the words “I assume”, that should immediately become a call of action. Find out exactly whether this assumption is true. Often you will find that it is not. In particular, never assume how something works.
#8 Build your code in testable blocks
There is a tendency by developers to create huge chunks of code. Some developers then add unit tests once the huge chunk is finished. This is bound to lead to poor unit tests and poor code. Instead, write code in smaller pieces. With each smaller piece, add the appropriate unit tests to ensure that it works as you expect and only then should you proceed to build upon the code. With each piece of code, you will have confidence in the current code while adding the new code. When something goes wrong, you’ll be able to quickly find the source of the problem as it will have something to do with the changes you just made.
#9 Think in terms of your users
Who are the users of your code and what are they trying to accomplish when they call you? Returning an error that states that a handle is invalid may be useful if your code lives deep in the depths of your product, but if your code returns errors directly to the end user this is unlikely to be helpful information. Likewise, when designing an API think first how people should use your API, then figure out how your API should work. Too often software shows design that is too close to what is actually happening, making it difficult for users to understand it.
#10 Pay attention to your mistakes and build upon them
A number of developers take offense at bugs in their code. Clearly it must be someone else’s fault! It could be the user’s fault because he/she did something unexpected. It could be the tester’s fault for calling your code in a way not possible for a user, or it could be another developer’s fault for calling your code in an improper manner. Regardless, in almost all situations there is something you can do about it. How did this bug occur? What could you have done better? Always strive to understand not just what causes a bug and how to fix it, but also why it occurred in the first place. Did you make an assumption? Could a unit test have fixed the issue? Is this something you just missed? Once you understand how the bug occurred in the first place, you’ll have a better idea on how to prevent similar bugs from occurring in the future.