Building Cathedrals
Three people are working on laying down bricks. You ask the first what he is doing, and he answers 'I am laying down bricks'. You ask the second, which says 'I am building a wall'. Finally you ask the third who tells you 'I am building a cathedral'.
Great programs are created the same way as any other program, one brick at a time, but each brick has to be laid down in the knowledge that it will be part of something greater than the small piece currently being worked on. If it is not known what the final structure will look like it is even more important to make sure the individual pieces can be built upon and reused later, because most code will find itself used in situations where requirements change. Knowing how much to abstract a given piece of code is what separates a good from a great programmer.
All great programmers were at some point merely good, however, and there are many rules that can be used to get better (first you have to learn the rules, and then you learn when to break them). Here are a few I personally try to live by:
- Duplicate code is the root of all evil. Duplicating any non-trivial piece of code (where trivial is e.g. calling the same function from different places) is a maintenance nightmare in addition to being a time bomb that blows up in the face of one of your customer when you forget to update one of the copies of the code.
- Do defensive programming. Anticipate what can go wrong and try to detect it as early as possible. Use assertions liberally, but only for things which are the result of a programming error. If someone is using your API in the wrong way it is much better that the system explodes as compared to silently ignoring the error and then doing the wrong thing.
- Write test cases and use them while developing the code. If you are working on a team this has the added benefit that if someone else makes a changes that breaks your code a test will fail and the burden of dealing with it falls on them instead of you having to spend time to track it down when it crashes in the field.
- Validate all user input and give resonable error messages. Recognize what is good and throw away the rest; trying to list all that is bad is usually futile (imagine trying to list all the foods you do not like instead of the ones you know you like when telling someone what you want for dinner).
- Avoid accessing list elements by index if there is no reason to; use an iterator (or the enhanced for-loop in Java 1.5) instead.
- If you have two (or more!) lists/arrays where the elements at equal indices belong together, put then in the same object and use one list.
- Avoid using strings as identifiers (TypeTags are your friends). If you have to use string identifiers (e.g. http request parameters) convert them into proper types as soon as possible. String identifiers have no type safety (e.g. you cannot use a refactoring tool to rename one) and are much slower.
- Use a static type checking tool (many IDEs, like Eclipse, has one built in) as this can catch many errors as you are typing. Unused variables and using a reference which can be null are typical bugs that should be caught this way.
October 17, 2006
More puzzles
We recently sent out some recruiting brochures. It is customary to include a couple of puzzles, so we used a couple of our favorites. Two are old, two are (as far as we know) new. Feel free to leave a comment with your answers.
Fix this program
Find a way to make the following C program fragment print 42 dashes, by changing only one character. Then find two more!
int i, n = 42;
for (i = 0; i < n; i--)
printf("-");
Bonus puzzle: 43.
Bonus puzzle 2: find ways to make it print exactly one dash.
Magic function
What does this function do, and why does it work?
int magic(int i) {
int j = 1337, k = 0;
do k -= i += (i<0) + i;
while (j *= 42);
return k;
}