Here are a few things that I have thought about and expect from programmers, whether they are on my team or not. I have tried to be honest with myself and really think about what I have written. I hope that readers will use these thoughts constructively to find something they can improve upon in their work as programmers or otherwise, either from what I have said or some thought of your own that my words sparked.
Don’t Reinvent the Wheel
Search for similar functionalities that already exist in the solution. This pertains to functions, user interface, data constraints etc. Building something from scratch should not be the first option even though it is often the most fun. Reuse existing practices, controls or code when possible. The amount of time spent searching for similarities should be proportional to the complexity of the new work. Include our own code base and third-party code bases that we already use. Also consider third party solutions we don’t already use for non-trivial functionality. Do not hesitate to ask other team members if they know of similar functionality within the application already, but please do perform a search of the resources you know on your own first.
Consider Uses and Contracts (Explicit and Implied)
Search for all uses of members, classes, controls, components, database views, table schema etc that have been modified in such a way that the existing contract has been changed. This requires critical thinking about what can be considered a contract, for example:
- Could a function never return null before, but now it can?
- Did dependency on string casing change in a way that wasn’t clearly a bug fix? Even if it was a bug fix, does anything depend on the prior behavior?
Follow the tentacles in order to be as confident as reasonable that changes will not cause something else to break.
Know, Understand and Test Your Changes
Examine and understand all code differences, including changes in designer-generated code, before committing changes (checking them in). If you do not know why the change happened, it is likely it was accidental. More often than I expect files get checked in that should not have been changed (for example .sln, .vsmdi, .reSharper) or do not compile. Be careful to understand not only what the code does, but how the code works – there is a difference between syntax and semantics. Knowing what the code does keeps you out of the realm of cargo cult programming.
The compiler is not a code tester – every error the compiler finds can be used as a personal indicator that there are more errors remaining even after the code compiles. It also isn’t the most efficient use of time waiting for the compiler. Ideally high-coverage unit tests should be written to test the new code. Either with or without unit tests, make sure that all of the code has executed before checking in changes – manually cover the code in that case, for example by setting breakpoints and removing the breakpoints as you hit them. Too often when looking at code from a bug report, code is found to handle a rare condition that couldn’t possibly have ever executed successfully even though it compiles – it was code that was never been run before it was checked it.
Do Not Use a Shotgun to Kill Bugs
When fixing bugs (or really when changing any existing code) it is important to understand all of what the existing code does. Not every bug can be reproduced in running code but that may actually be a positive thing. It should be possible to see where the code can go wrong and understand why it happens, but it may take time and practice to develop the skill. Fixes should be justifiable. For example, fixes checked in with comments like “this should probably fix the crash,” or preventing a null reference by skipping the code if the reference was null without understanding why or how the reference is null are not the correct solution and just end up moving the bugs to different places. Do not re-write, re-implement or re-factor code that you do not understand and do not know the requirements of; doing so will only move the bug or create new bugs.
Attention to Detail
This is listed as a requirement on just about every job posting I’ve ever read, but sometimes it isn’t easy to understand what it means.
One of the things attention to detail means to me is that the same care given to the actual code is also given to everything else – specifically including all comments, whether in code or in changeset notes. Typographical errors (slips of the hand or finger) found in comments could be taken as an indication of carelessness and I feel they have the potential to significantly reduce trust in the quality of code. While spelling, grammar and punctuation mistakes can be errors of ignorance (impossible to catch by the original author,) the majority of typos can be found by a single proofread. If comments aren’t proofread, was the code?
Create and Maintain Documentation
Document code where it seems non-trivial to understand what is going on. When changing code, double check the documentation of the modified members and class for accuracy. Comment changes committed to source control with more than just what bug number it fixed – explain a tiny bit about what changed, preferably why if possible. It is an art to balance what comments go in the source code and which go in the source control comments.
Ask Why? and Consider the Problem Being Solved
Consider the problem being solved by the requested feature or change rather than just jumping straight to coding. Consider what the problem is separately from exactly how the analyst has asked for it to work (why vs. how.) Discuss with analyst your understanding of the problem to confirm that you understand. Once you understand what the problem is and why the analyst has requested the feature work the way it does, you have a much better chance at how to do it with the best design. It is best to do this as early in the design as possible because your input is likely to influence the design. Even if the design seems finalized, programmers can often come up with scenarios that were not considered. Do not be afraid of suggesting simplifications to the design, but offer these as suggestions rather than complaints. Be prepared to have your suggestions turned down but use it as an opportunity to learn more about what and why; it is likely that a simpler approach may have already been considered but was not documented as to why it was not good enough.
As you consider the design, try to think critically about what can happen if the system is used in ways other than what the analysts think the normal use will be. It is very tempting to jump in and implement what was asked for. The skill of taking some time to think critically to identify problems while they are easier to avoid is applicable to almost everything.