Typically when people think about coding standards they may think of Google's Java Style Guide, GNU's C++ Coding Conventions, MSDN's C# Coding Conventions, Apple's Objective-C Conventions, Android's Style Guidelines for Contributors, or so on. There's practical guidance out there for most languages and frameworks. These conventions provide you with several advantages. They will help you write code that other developers in that language or on that platform will recognize and therefore be able to maintain easier. They help you code for compiler optimization or run-time optimization. And they help you navigate some of the trickier elements of the language. What these guides don't generally do is give you a good framework of how to write code that will last.
My Software Craftsmanship series was a good start down this path but now let's dive a little deeper and see if we can't identify some additional practicals tools that you can use to help you design and write code that will last. Ultimately that should be out goal; to write code that lasts. Code that lasts may not be elegant and it may have flaws but ultimately it has proven useful. So useful that it has stuck around. Sometimes you find code that lasts just because others are afraid to touch it for fear that it will break. I would argue that, while that code may have bad style or convention, it has lasted because it did it's most important task well; it did the one thing it was written for and has proven useful for that task.
Traditionally when we evaluate code fitness we look at how well the developer adhere'd to a certain style guideline or how well they adhere to a language or platforms best practices. We often look for places where patterns have emerged in the code and try to optimize our implementation around this pattern. We look for poor memory utilization or inefficiencies in dealing with large data sets. These are are great things that MUST be part of a code review. But these things alone don't tell the full story of whether or not the code will last.
So here are a few tips from me on what I like to also look for during a code review that I think identifies a pattern of code that will last.
What would a standard ______ expect?
Fill in that blank above with whatever framework, language, or platform the code was written for. So often it's the case that we let other framework, language, or platform coding standards creep into unrelated code. A standard developer for that framework, language, or platform should be able to open the code and see style that their familiar with. They should recognize patterns that are specific to that framework, language, or platform. The code should flow in a way that's optimized for the way that framework, language, or platform expects to run the code.
A few ways I often see this brake down are:
- Adding a dependency on a third party tool that auto-manages code style (like ReSharper)
- Adding a dependency on a particular IDE (like Eclipse) when the framework, language, or platform is IDE agnostic (like C++ or Java)
- Not using an IDE built for the framework, language, or platform. I.e. Xcode for iOS or Visual Studio for Microsoft .NET (though I would argue NOT for Mono)
- Curly braces, tabs, and spaces. Let any Java, C#, or Ruby developer read the others code and you'll hear be able to hear the complaints from down the hall.
- Programmatically defining UI elements in code when the framework or platform provides built in mechanisms to define such elements.
Often these dependencies are added with good intentions. They're added because the organization wants to increase productivity. Or they're added because the organization believes that conformance to a style is important for the cohesiveness of the code, which it is. Conformance to a style other than what a standard developer of that framework, language, or platform would expect is not cohesive. Cohesiveness should be determined both by how self cohesive it is as well as how cohesive it is within the ecosystem it is built for.
What external dependencies has this code been coupled to? Are they necessary?
Developers become very fond of particular pieces of code or particular third party libraries. We're taught in school and at work that we need to focus on re-use. Often this is misinterpreted to only mean reference other code. But occasionally, or often, it's the case that in order to reuse existing code without adding unnecessary dependencies we need to refactor the code we're referencing into a library or module. This allows us to manage the dependencies of our new code correctly. Often we reference the correct code but add dependencies on other packages or classes that are unnecessary because we fail to refactor the dependencies as their consumption changes.
When this code changes what affect will that have on other non-related code?
This is a smell that you've either got something messed up with your class encapsulation or code organization. You should expect direct consumers of your code to change as your interfaces change. But you should not expect transporters of your code to have to change. One way to test this is to simple change a public method signature and compile. See what will no longer compile. Are all the places you have to change reasonable?
How discoverable are the features of this code? Do I have to read the code to understand what it does?
This one is one of the more subjective items on the list. But I still think they're questions that absolutely must be asked. Your first pass during a code review should be to look at how the code is used. If during this pass you find yourself asking things like "why do you need to pass that?" or "why is this call necessary?" then you're probably looking at code that could use better method naming or is written with the wrong level of encapsulation.
Post a Comment