I was going
to call this "X secrets of highly effective developers", like some
people, only these things shouldn't be secrets. Note this is as always my
not-so-humble opinion, so it is entirely likely that this article is either a)
misguided, b) missing things, or c) entirely wrong, but I can't give you anyone
else's opinion now can I.
These are all typical cliché's, but I'd like to try explain them just as a brain dump anyway.
1. Code for people, not computers.
This is really the absolute number one goal. Everything else can be taken as a corollary to this.
If you don't code for people, you are writing un-maintainable code. However, it's easy to throw the term around like a lot of other slogans/buzzwords without actually having a solid understanding. What this means to me is in fact "Try and write your programs as if they were plain english"
Well, you know, not quite english, because english has it's own giant set of problems too, but the point I'm trying to make is that someone else should be able to read your code and it should flow as if it has topics, headings, sentences, paragraphs, and so on. You should read it like a book, not decipher it like a code. In fact, code is a crap word, we should call it something like "instructions", instead of code.
How do we do this? With years of experience, and a constant drive to learn and
apply new things.
But for now, here's a couple of pointers which I find have helped me so much that I feel like slapping other developers who don't do them:
2. Use good names
This is obviously a subset of #1, but if I had to pick the most important thing, this would be it. Again, "Use good names" is a meaningless phrase, what you should do is "call things what they are". Whenever you have a variable/function/class/whatever, ask yourself "what actually is it? a buffer? a file handle? a person? what?", and then call the variable that. Simple, but mostly always overlooked by most programmers. Other developers should almost always be able to look at a variable/function/class and make a correct guess as to what it is, and what it might do otherwise you probably have a bad name.
This is doubly useful because sometimes you will have trouble expressing
what a something actually is. Sometimes, this is
just not being able to think of the right word (go learn english :-D ), but more
often than not, it is a strong signal that your design isn't correct, or you
have accidentally gone down the twisty path towards a tangled mess of garbage.
For example, if you can't think of a single good name for a class or function, it's probably because it's doing more than one job, and should be broken up into 2 classes/functions.
At the end of the day, if you can't even think of a reasonable name for a thing which makes your program work, then how will anyone else (usually you, 6 months later) ever hope to understand it?
3. Use abstraction
This can be "bottom up" programming, or "top down" or whatever design
methodology is in fashion at the time, but the important thing is that you build
code On top of other code, not alongside it. You are creating a
pyramid, not tiling a floor.
That may not have made too much sense - to try explain it a bit better, think of writing a program that opens a file, writes some string to it, and closes the file.
If you were to use the all-too-common floor tiling method, you'd have a big
long function (or lots of small functions running in sequence, whatever), which
would do the following in sequence:
Allocate a file handle -> call the API open function -> allocate the memory for the string -> keep track of how many bytes we have -> write the memory to the file using the API file writing function -> close the file -> free the string memory.
All the operations are on the same "level", the stuff is just happening in a big row, like laying down tiles next to each other..
If we are to write the above using abstractions, we'd instead have a file
class, and a string class. The file class would deal with the file API (handles
and stuff), and the string class would deal with the string memory. Then,
instead of our program allocating handles and memory, it can just deal with
files and string classes. It would do the following.
Create a file object -> Create a string object -> call file.write( string ) -> cleanup done automatically by objects.
When you write programs correctly with abstraction, you can stack the
abstractions on top of each other, leading eventually to code that is almost
like pseudocode or a domain-specific-language.
This is what object oriented programming, and a lot of other techniques are actually for (as opposed to the common retarded view of inexperienced programmers, that OO isn't being done correctly unless all your classes use inheritance somehow)
4. Do the simplest thing that can possibly work
Now before I get branded as an agile zealot, not everything from agile is
actually bad. What this actually means is Do the right thing, but do it the
simplest and smallest way you can. Don't write code which doesn't directly
help you get things done, or tries to solve problems you don't actually have.
This also doesn't just apply to your higher-level design, but low level too. Functions/classes/interfaces/etc should all be as simple as possible, and do only what they need to.
A classic example of doing the wrong thing here is building a big pile of classes and interfaces and message-handling code before you actually attack your main problem. Yes it's fun to build frameworks and architecture, but at the end of the day, it'll probably just get in the way.
5. Don't repeat yourself, refactor instead.
If you are like lots of developers I've seen, and believe the best way to start a new program/library/class is to find a similar one and copy/paste it into a new file, STOP NOW. BAD PROGRAMMER! SIT!
If you find yourself writing the exact same code twice, refactor it into a common function or class.
If you find yourself writing similar code twice, refactor the common bits into another function or class (generics, dynamic types or other ways of dodging verbose typing, and first-class functions are a big win here), and have the remaining different bits as small and clear as possible.
5. Good design does not come from 'design,' but from refactoring.
If you do all the other stuff, you'll probably find yourself left with a ton
of small functions and classes, and all your other classes will be using them.
This is already better than lots of giant functions which duplicate code and
functionality, but can get a bit messy provided you don't clean them up.
Most likely however, a bunch of your helper functions will all take similar parameters. These are prime candidates for making a new class. Remember also, not everything should to be a class. It's fine to have a bunch of global functions in a namespace (or a static class if you're stuck in C# or java), if that's the nicest way to think about those kinds of objects. The main goal is to always try and make sure that your helper/library functions are as simple, clean, and useful as possible, and are arranged in clear and obvious groups. You can even write unit tests for them :-)
Sooner or later, you'll probably end up with either some kind of framework
(to my knowledge, this is how Rails came about), or a set of general classes,
like the .NET base class library, but for dealing with the kinds of problems
your company faces, or perhaps both.
This is excellent, as you'll be able to re-use these things over and over again in future, meaning next time you have to write that stupid app which has to read the registry and write to files, you'll be able to use your framework/library code and do it in 5 minutes instead of a day. Your boss will love you, and you won't mind programming in C++ anymore because you can actually get things done now.
Also, because you'll have created this framework/library code based on other
working code, and based on what you actually need to do, and refactored it to
best fit your problems as you go, you'll have stuff which is actually useful
People are stupid, and 99% of us can't design our way out of a paper bag. Most frameworks that get 'designed' up front wind up completely missing the point and to solving the wrong problems in the wrong way. But, by keeping existing code simple, clean, non-repeating, and constantly refactoring it, we can end up with some well structured and maintainable code anyway. Owzat? :-)