- Good names
- No duplication
- Small pieces
- Loosely coupled
None of these are original. They have all been endlessly discussed, analyzed, and written about.
Why is "i = j * 2 + k" bad? Because you have no idea what it means without studying a much bigger context of code to try to figure out what i, j, k, and 2 mean.
It isn't just that you should have long names, they also need to be accurate. A name that was once accurate but has since drifted into being use other ways is just as bad as a comment that gets out of sync with the code.
Not all good names need to be long. There's nothing wrong with using "i" for a loop index, where it's obvious what it is.
Good names often eliminate (or at least greatly reduce) the need for comments.
Consistency ties in with good names. Use the same name consistently for the same purpose. Conversely, do not use the same name for different purposes!
- Coding standards or guidelines
- Coding idioms
- Organization, design, architecture
Code reviews, pair programming, and joint ownership of code all help with consistency.
Programming by "copy and paste" is easy and quick. It's code re-use - isn't that supposed to be good?
The problem is that code isn't "write-only". You're going to need to fix it, improve it, add features to it. And that becomes much harder when you've got the code in multiple places.
Another problem is that it usually isn't just "copy and paste", it's "copy, paste, modify". So now you not only have multiple copies of the code, but they're each slightly different. Even tougher to work with. And it lowers your consistency.
But there are subtle advantages to DRY (Don't Repeat Yourself) that are just as important as these obvious issues. To avoid duplication, especially when the duplication has variations, you have to think about the code, what is common, what varies, why it varies. Does it vary just because you used different variable names? Maybe you should be consistent. Does it vary because it behaves differently? Maybe whoever uses this code (end user or another programmer) would benefit if it behaved consistently.
In some ways this is both one of the easiest and the toughest guidelines.
It's easy because anyone can see whether you've broken your code up into bite size chunks. You can write automated tools to check function / method / class sizes.
It's hard because it's not just breaking it up, it's how you break it up. You want the pieces to have high cohesion and low coupling, and that's hard. We've been struggling with this one for a long time e.g. Yourdon & Constantine covered this in depth in Structured Design in 1979!
A big benefit of small pieces comes from giving the pieces good names.
If every line of code connected to every other line of code then complexity would go up as the square of the number of lines of code. N squared goes up fast. If this was the case we'd never manage more than tiny programs.
On the other hand if every line was totally independent, then complexity would only go up linearly with the number of lines of code. That's a lot more manageable.
The reality is somewhere between. The bigger a program gets, the more important it is to keep the pieces as independent, loosely coupled, as possible. The higher your coupling is, the bigger the risk that a change will have unexpected side effects (also known as bugs).
Again, this is easier said than done. Structured programming aimed to reduce coupling. One of the big goals/advantages of object-oriented programming is looser coupling. "Interfaces" are another tool. Our tools are getting better. But are programs are also getting bigger.