And I always respond, "Hey, you kids, GET THE HECK OUT OF MY YARD!"
No, no, I usually demur with, "Oh, gosh, I don't know," as I look down shyly and shuffle my feet.
But, I've thought about it a lot recently, after writing so much solo code for Delicious Library (for the first time in many years), and then taking on a new programmer and trying to impart my style on him. And what I've come up with is a style I call:
* The Way of the Code Samurai *
Now, I don't actually know much about real samurai, but the basic thing I've heard is they stand and stare at each other for hours, and then suddenly BAM strike once and the other guy is down.
That's how you should code.
- Think first. Think some more. Think about the whole problem. Think about a little part of the problem you're going to start with. Think about the whole thing again, in relation to your idea on the starting point.
Don't write code until you know what you're doing. Now, you may not be able to "know what you are doing" just from thinking, in which case you should start a TEST project and write a bunch of ugly code to make sure your ideas are correct.
Then, start again, in a new directory. We had seven or so different test project directories during the making of Delicious Library -- one for the video barcode reader, one for the store, one for amazon XML parsing, one for talking to Bluetooth scanners, etc. We learned how to do what we were going to do BEFORE we uglied up the main project with a bunch of code.
Then we copied the good parts out of the test project, and left the cruft. This let us observe rule #2...
- Write all your code "clean," the first time you write it. Make classes for everything. Use enumerated types. Don't take shortcuts. Don't have any part of the code where you say, "Oh, yah, I just glossed over that for now." You are NOT going to go back and fix it. Seriously, how often do you say to yourself, "I think I'll dive into this messy code today and try to make it nice and pretty without adding any functionality?" Nobody is going to pay you for that. In fact, I got called on the carpet for cleaning code during a major update to a piece of software at a previous job -- "What are you doing spending time modifying code that already works? Just add your new features and be done." Never mind that I couldn't understand the code, or that clean code is stable, maintainable, extensible code.
Don't gloss over anything. Write every line to be bulletproof. Write every method as if every other method was out to get your code and try to make it crash, and your job was to make sure it wasn't going to happen in YOUR code. Assume the user is going to hit keys randomly. Assume the network is going to get disconnected. Assume the hardware will fail.
Explain your routines aloud. Do you find yourself saying, "Well, this isn't totally correct, but it works because I know that at this point..." STOP. Redo it. Write it the correct way.
And, if you're in code anyways to extend it or fix a bug, CLEAN IT. Clean as you go, always. Don't consider code to be static. Turn those constants into an enumerated type like you always meant. Take those hard-coded strings and make class variables for them. Replace english phrases with NSLocalized versions. Clean, clean, clean as you go.
- Less source code is better. This is almost always true. The exceptions to this are so rare that every time you think you've found one you should REALLY doubt yourself. Less lines of source code almost always means less code that new programmers have to understand when they come on the project. It means less stuff for you to remember next year when you are in the middle of another version. It means fewer places for you to have mistyped something. It means fewer instructions to execute. It means fewer things to change when you re-architect.
There are some interesting corollaries here. For instance, if you're writing a class to display some text in red (for some reason), don't add a bunch of methods "for the future" that allow you to draw the text in blue or green or purple. Because that's more code than you need right now, and "less code is better."
But, if you suddenly realize you want to draw purple text, you could write the red code again, except put in the color purple. But, "less code is better," so you really need to abstract out the text-drawing code and make it take a color parameter, so you can re-use the same code.
The lesson I'm getting at is, don't try to make code general until you actually need it in more than one place. The worst libraries in the world are the ones people write without actually writing any code that uses them to do actual work for actual users.
And don't write longer, more obtuse code because you think it's faster. Remember, hardware gets faster. MUCH faster, every year. But code has to be maintained by programmers, and there's a shortage of good programmers out there. So, if I write a program that's incredibly maintainable and extensible and it's a bit too slow, next year I'm going have a huge hit on my hands. And the year after that, and the year after that.
If you write code with more code that's fast now, you're going to have a hit on your hands. And next year, you're going to have a giant mess to maintain, and it's going to slow you down adding features and fixing bugs, and someone's going to come along and eat your lunch.
I'm not saying slow code is good. There's a time and a place for optimizations...
- The time and place are AFTER YOU ARE DONE. Optimize methods ONLY after they work and are bulletproof AND you've done testing in Shark and discovered the method is critical.
Don't optimize as you write. Why not? Because, in all probability, you're wasting your time. The VAST majority of the code programmers optimize is executed so rarely that its total speedup to the program can be measured only in nanoseconds. Also, you're probably not very good at it. No insult, but machines and libraries are complex systems, and your guesses as to what's slow are probably pretty bad. You need to run testing tools to find out what is ACTUALLY slow. The results are often surprising. (For instance, in the Mac OS 10.3, having lots of tooltips in a view that you remove and add back to a window a lot is EXTREMELY slow. This is not something you can possibly know to optimize without doing testing in Shark.)
The next time you go to make something a little harder to read but a little faster, ask yourself, "How often will this REALLY get called?" Is the answer less than a hundred times a second? Because processors can now process several BILLION operations a second.
Now, Delicious Library isn't the zippiest program in the world on all hardware, but, actually, it's a LOT faster than it was initially. Those huge, beautiful covers really suck down memory. When we first wrote Library, if you loaded more than about 400 items, the cover images would suck up all your main memory and the program would just crawl. (It was like iPhoto 1.)
I re-architected the entire cover caching and compositing system, and got it so that we could comfortably handle several thousand items, and, if you use the table mode, possibly tens of thousands (I've never had that many items). This took several weeks to do. Now, I'm not as zippy as iPhoto 4, but I'm actually pretty close to the performance of iPhoto 2, and considering I'm just one guy and this was Delicious Library 1, I'm pretty proud of that.
What's the point? The point is, if I'd spent a bunch of time optimizing other parts of the program as I wrote it, I would not have had those weeks at the end to fix the imaging path, which was the slowest part. I would have had to have shipped with a program that could only handle a couple hundred items, and then I would have immediately had to patch it when people started scanning in thousands.