Thursday, April 15, 2021

Notes about the Writing Solid Code

Many developers feel no shame when a production bug is found in their code. They ask 'why testers didn't find it?'. Microsoft developers eventually found out some bugs are impossible to spot for the testers. By 1989 the bugs became the developers' responsibility. Developers had to deploy "zero defects" to the Testers.

By 1986 Microsoft had explicit monitoring code build into their production code that alerted programmers about bugs. The two critical questions to ask about every bug are:

  1. How could I have automatically detected this bug?
  2. How could I have prevented this bug?

A Hypothetical Compiler 

Steve Maguire suggests to turn on all compiler warnings. Today IDEs like IntelliJ IDEA handle warnings perfectly in my opinion. He also suggests to use checkstyle-like tools.

Assert Yourself

Use debug version of your program to implement assertions. Use assertions to assert input parameters. Use assertions to detect impossible conditions. Write second version of a complicated algorithm and assert they produce the same result. Don't use assertions to check error conditions which you should handle in the production code. 

Fortify your Subsystems

Look at your subsystems and try to think how other programmers would misuse it. Add assertions to common or hard-to-find bugs. If something (a bug) happens rarely, force it to happen often in the debug version. Create thorough subsystem checks and use them often. Use debug version of your application to trade speed and memory for error detection. 

Step through your Code

We should step through our code line by line even before finding a bug. We should step through every branch of code. As you step through the code, focus on the data flow. The gut reaction that stepping through the code will take too much time, is wrong. 

Candy-Machine Interfaces

Make your interfaces as clear and as obvious as possible. We should consider programmers which will use our interfaces. We should make the interfaces super-understandable, so there can be no mistake in using them. We shouldn't hide any error conditions. We shouldn't burry them into cryptic return values. We should always look for and eliminate flaws in our interfaces. We shouldn't write multi-purpose functions, but a separate functions for each use-case. We should use type-safety to our advantage. If we can, we shouldn't fail in the functions, we should return a reasonable value instead. Avoid boolean arguments. Write comments that emphasize potential hazards.

Risky Business

Watch for variable overflowing. We should be as accurate as possible. Being only close means being buggy. Functions should do one thing and one thing only. We should avoid using nested ternary operators. We should handle special cases just once. Eliminate unnecessary if statements. Always ask yourself 'how could I have avoided this bug?'

Treacheries of the Trade

We shouldn't rely on data or logic we don't own. If I write a function, I cannot rely on other function internal working. 

Write code for an 'average' programmer.

The Rest is Attitude

Bugs don't go away. Either the bug report was wrong, or somebody else fixed it or the bug is still there, just not apparent. We as professionals, should determine, which of the 3 options was it.

We shouldn't fix bugs later. We should fix them first, before writing new features. We should always fix bug causes, not just symptoms. Don't implement non-strategic features. No features are free. Unnecessary flexibility breeds bugs. 

Don't "try" solutions. Use the correct solution. Don't event suggest trying something. You should either know what to do, or you are not understanding the system properly.

Programmer testing of features should have priority over schedule. We should implement and test code in small chunks. We shouldn't test the whole thing at the end, because we will have tendency to cut on testing because of schedule.

Don't rely on testing groups to find your bugs. Don't blame testers to find your bugs. Even minor bugs which don't need to be fixed at all are disturbing - their existence is disturbing. How to prevent similar bugs in the future? Never allow the same bug bite you twice.

We should establish our priorities, like maintainability, performance, etc. and stick to them.

No comments: