Six Ways to Write More Comprehensible Code
Total Page:16
File Type:pdf, Size:1020Kb
Six ways to write more comprehensible code http://www.ibm.com/developerworks/linux/library/l-clear-code/index.ht... Six ways to write more comprehensible code How to keep your code from destroying you Level: Introductory Jeff Vogel ([email protected]), President, Spiderweb Software 29 May 2007 As a developer, time is your most valuable resource. These six tips on how to write maintainable code are guaranteed to save you time and frustration: one minute spent writing comments can save you an hour of anguish. I learned to write, clear, maintainable code the hard way. For the last twelve years, I've made my living writing computer games and selling them over the Net using the marketing technique that was once charmingly known as shareware. What this means is that I start with a blank screen, start coding, and, a few tens of thousands of lines of code later, I have something to sell. This means that, if I make a stinky mess, I'm doing it in my own nest. When I'm chasing down a bug at 3 a.m., staring at a nightmare cloud of spaghetti code, and I say, "Dear God, what idiot child of married cousins wrote this garbage?", the answer to that question is "Me." So I have been well rewarded by learning about good, sane programming techniques. Some of these practices are described in this article. Many skilled, experienced, morally upright coders will always know some of this stuff by heart. All these people will get from this article is a chance to bathe in my delightful prose style and remember how horrible life was before they got the clean code religion. But there are many who, like me, stumbled into programming in an unexpected or unusual way and never had anyone drill this stuff into them. These things are basic to many but, to others, they are invaluable techniques that nobody has told them. So, to those who don't want to make a mess, this is for you. The example case For illustration purposes, our example program throughout this article is a hypothetical computer game called Kill Bad Aliens. In it, you will control a spaceship. It will move horizontally back and forth at the bottom of the screen, shooting bullets upwards. Your ship will be controlled by, say, the keyboard. Figure 1. Our hypothetical game 1 of 10 31/05/2007 05:53 p.m. Six ways to write more comprehensible code http://www.ibm.com/developerworks/linux/library/l-clear-code/index.ht... The game will take place in periods of time called Waves. During each wave, aliens will appear, one after another, at the top of the screen. They'll fly around and drop bombs. Aliens will appear a fixed period of time apart. After you kill a certain number of aliens, the Wave ends. Killing an alien gives you some points. When you finish a wave, you get a bonus number of points depending on how quickly you finished. When a bomb hits you, your ship blows up and another appears. When you are blown up three times, the game is over. If you got a high score, you are valuable as a person. If your score is low, you are not. So you sit down and start to write Kill Bad Aliens in C++. You define objects to represent the ship, your bullets, the enemies, and the enemy's bullets. You write the code to draw all the objects. You write the code to move them around as time passes. You write the game logic, the alien AI, the code to read the user's will from the keyboard and so on. Now how do we do this so that, when the game is ready, the code is comprehensible, easily maintained, and, in general, not a mess? Tip 1: Comment like a smart person. Comment your code. Obviously. If you write a procedure, fail to comment it, and return to it a few months later to rework it (and you will), not having comments will cost you time. And time is your most valuable resource. Lost time can't ever be replaced. But commenting, like anything else, is a skill. You get better with practice. There is good commenting, and there is bad commenting. You don't want to write too much. Suppose you write comments for a function that, in the future, saves you ten minutes of time understanding your code. Great. But suppose your comments are so verbose that it takes five minutes to write them and then, later, five minutes to read them. Then you've saved yourself zero time. Not so good. You don't want to write too little, either. If code goes on for a page or two without something breaking down what's going on, well, I hope that code is clear as crystal, because otherwise you're wasting future time. And you don't want to comment in stupid ways. When people first start writing comments, they often get hyper and write things like: // Now we increase Number_aliens_on_screen by one. 2 of 10 31/05/2007 05:53 p.m. Six ways to write more comprehensible code http://www.ibm.com/developerworks/linux/library/l-clear-code/index.ht... Number_aliens_on_screen = Number_aliens_on_screen + 1; Uhmmm, duh. If something is so obvious, it doesn't need a comment. And if your code is such a tangle that you need a comment for every single line of it, you'd probably profit from making it simpler in other ways first. Comments don't just save time, they cost it. They take time to read, and they spread out the actual code on the screen, so you can have less of it on your monitor to inspect at one time. And, while we're at it, don't ever do this: Short get_current_score() { [insert a whole bunch of code here.] return [some value]; // Now we're done. } Oh? We're done? Thanks for letting me know. That big right bracket and the infinite expanse of empty space beyond really didn't tip me off to that. And you don't need a comment before the return statement saying, "Now we return a value," either. So, if you are writing code, in the absence of a boss or a company policy telling you what to do, how do you comment it? Well, what I do for code I am stuck with maintaining myself is write an introduction. When I return to a procedure I forgot that I wrote, I want to see an explanation for what is going on. Once I understand what the machinery is doing, it becomes infinitely easier to understand the actual coding. This generally involves: 1. A few sentences before the procedure/function saying what it does. 2. A description of the values being passed into it. 3. If a function, a description of what it returns. 4. Inside the procedure/function, comments that split the code up into shorter tasks. 5. For chunks of code that seem thorny, a quick explanation of what is happening. So we need a description at the beginning and a few signposts inside explaining the road taken. Doing this is very quick, and it saves a ton of time in the long run. Here is an example from the theoretical Kill Bad Aliens. Consider the object representing the bullet the player fires. You will frequently have to call a function to move it upwards and see if it hits anything. I would probably code it something like this: // This procedure moves the bullet upwards. It's called //NUM_BULLET_MOVES_PER_SECOND times per second. It returns TRUE if the //bullet is to be erased (because it hit a target or the top of the screen) and FALSE //otherwise. Boolean player_bullet::move_it() { Boolean is_destroyed = FALSE; // Calculate the bullet's new position. [Small chunk of code.] // See if an enemy is in the new position. If so, call enemy destruction call and // set is_destroyed to TRUE [small chunk of code] // See if bullet hits top of screen. If so, set is_destroyed to TRUE [Small chunk of code.] // Change bullet's position. 3 of 10 31/05/2007 05:53 p.m. Six ways to write more comprehensible code http://www.ibm.com/developerworks/linux/library/l-clear-code/index.ht... [Small chunk of code.] Return is_destroyed; } If the code is clean enough, this sort of commenting should be sufficient. And it will save plenty of time the dozen times I return to this function to fix a dumb mistake I made. Tip 2: Use #define a lot. No, a LOT. Suppose that, in our hypothetical game, we want the player to get ten points when he shoots an alien. There are two ways to do this. This is the bad way: // We shot an alien. Give_player_some_points(10); This is the good way: In some global file, do this: #define POINT_VALUE_FOR_ALIEN 10 Then, when we give up some sweet points, naturally, we'd write, // We shot an alien. Give_player_some_points(POINT_VALUE_FOR_ALIEN); Most programmers know, on some level, to do things like this. But it requires discipline to do it enough. Just about every time you define a constant number, you should strongly consider defining it in one central place. For example, suppose you want the play area to be 800 by 600 pixels. Be sure to do this: #define PIXEL_WIDTH_OF_PLAY_AREA 800 #define PIXEL_HEIGHT_OF_PLAY_AREA 600 If, at a later date, you decide to change the size of your game window (and you very well might), being able to change the value in this one spot will save you time twice.