Great Code [2012]

First off, a disclaimer. This post is really from some work I did in 2012! We’re revisiting this topic again at Redgate in 2018 with different constraints (our code works in more web environments, it works together more and the world has moved on).

With the benefit of hindsight, I was misguided when I did this work. Great is the wrong adjective to aim for! Simplicity should be the guiding policy. Simple code is easy to change, fits in your head and is easy to test.

I’ve added a few comments in italics where things have moved on significantly since then.

Without further ado, engage time machine!

(via WikiMedia Commons)

Why should we care about Great Code?

I believe that the internal quality of a product is vital to long term success. The costs of technical debt creep up over time and slowly strangle products, making it far more difficult to add features, fix bugs and release frequently.

[When this was written we’d be lucky to do a release every few months, now we release at least weekly].

We want Red Gate to be a great place to work for software engineers. As an engineer working with a dirty codebase is a horrible experience and we should do everything in our power to avoid it. I’d like us to make great code more explicit and work together to move our code in the right direction.

What makes Great Code?

Yesterday I spent some time talking to most software engineers (apologies to those I missed) about what makes great code.

The points we had consensus on are:

· Classes should have clear dependencies (dependency inversion principle)

· Classes should do one thing (single responsibility principle)

· Classes should work at a single layer of abstraction.

From these very simple principles other properties emerge. Code that has clear dependencies is easy to test. Code that works at a single layer of abstraction is easier to read. Code that only does one thing is easier to maintain. This certainly isn’t a finished set of ideas; I just wanted to identify a few areas in which we could improve. We’ll reassess these regularly and incorporate feedback.

As a majority agreed on these, I’d like us to start thinking about how we can get better at applying these principles to all the code that we write.

One very simple step we can all take is to do a code review before committing. Code reviews help share knowledge and just the act of talking about the code often finds issues before submitting. I’d like to encourage everyone to adopt code reviews. If you got strong objections or want to discuss / argue about the benefits, please talk to me!

[Every team at Redgate now does code review. GitHub and pull requests in particular have been AMAZING at doing this].

What else came up?

What does great code?

One interesting aspect was that testability was valued as highly as actually testing the software. The act of writing code that is testable results in clear dependencies and this property alone makes it important. I don’t think anyone can disagree with readable and maintainable code, but I think those are emergent properties of other aspects rather than first-class principles themselves.

Commenting was a contentious issue. Some people advocated self-documenting code, whereas others wanted comments on all methods. I’d like to park issues like this (and coding formatting standards) because we don’t have consensus.

[Most teams now just check in a R# config and we don’t argue about tabs/spaces any more. Most of the time. Almost]

The single-responsibility principle was mentioned by a few groups and also came up in other guises (specific, isolated, localized, layered, work at a single layer of abstraction).

Many people said that being able to check out the code, build it, and have it actually work first time is an important property. On the surface, this seems like it should be an easy problem to solve but obviously there are reasons why this isn’t the case. I’m keen to hear from you if your project doesn’t build first time (and you have good reasons for doing so). In my case, SQL Source Control doesn’t build first time because we’ve been too lazy to fix it. Not a great excuse, so we’ll fix that today.

[All code now uses a build script. You check it out, run the build scripts and everything works]

What next?

The challenge now is to try and find out why we don’t write great code all the time. Everyone I spoke to had very clear opinions on what great code is, but the majority of people I worked with were working on codebases they didn’t consider great. Code doesn’t have to be perfect all the time, but it should be just “locally crazy”.

Legacy code (SQL Compare, SQL Backup Reader, Hyperbac or even yesterday’s code) is part of this problem. These problems can only be improved incrementally. It might take years to make a real difference to a code base, but I think it’s important to do that (if for nothing other than personal pride) otherwise we’ll be saddled with unmaintainable code for years to come.

With new code perhaps we consider experimenting is more important than the quality of the code? We can (and should) embrace technical debt when exploring new ideas, but we need to find techniques to ensure that this is isolated and doesn’t upset the overall design of the system. It’s important for us as software engineers to consider the internal quality of a product and push-back if we feel that too many corners are being cut in order to release quickly.

A large number of us have been on refactoring, design-pattern or TDD courses. What is stopping us applying these practises on a day-to-day basis?

I’ll be trying to find out answers to these questions over the next few weeks. Please talk to me if you have any comments.

I’d also like to experiment with ways we can view technical debt and make the internal quality of the product more visible. Any experience reports with this (SQL Monitor and Code Coverage for example) would be appreciated.