Tuesday, September 27, 2005

Programming Habits and Style

While writing another article, the issue of programming style came up. A programmer’s style of programming goes a long way toward defining how well your habits are developed, and adds to how easily identifiable your code is. Developing good programming habits will help you in the long run develop good code. I have my own personal style for developing software; I grew up programming Pascal, so a lot of my habits from that language hold over in other languages as well. I believe this has been a huge benefit as it has caused my way of thinking to be such that I reject some of the shortcuts that other languages allow.

While I am a proponent of the Scientific Method, I am always skeptical about anyone selling “The One True Way ™”. Despite what anyone will tell you, there is no one “correct” way to develop code, including my own. But that doesn’t mean there isn’t a wrong way.

Lets look at an example. Here is a simple program that will initialize an array of 10 elements with random numbers, and copy that array.

#include <cstdlib>
#include <ctime>

using namespace std;

int main()
{
int grp_a[10], grp_b[10];

srand(time(0));
for (int x = 0; x < 10; grp_b[x] = grp_a[x++] = (1 + (rand() % 20)));
return 0;
}

While this is syntactically correct, this program represents bad style in my opinion. The biggest problems are that the logic is not well defined and it’s difficult to read. It is very nice that the program can accomplish the initialization of the array, assignment from grp_a to grp_b, and increment of the counter in one line of code, but it is poor coding. The reason I say so is the lowest common denominator, a beginner programmer, would not be able to read this and follow its logic.

Surprisingly I see code like this quite a bit. In my opinion this is done by show off “Cowboy Programmers” and code of this nature has no place in the production of quality code. Coders who write like this try and show their mastery of a language by showing what the language is capable of doing. A true master would show the way for a learner, not flaunt their mastery.

I have rewritten the code in a way that I feel represents good standards.

//Headers required for program. Cstdlib is needed for the random number functions
//and ctime is needed for seeding the random number generator
#include <cstdlib>
#include <ctime>

//use the C++ standard namespace

using namespace std;

//Constants used to define the max array size, our minimum and
//maximum random numbers, and zero value


const int
ARRAY_SIZE = 10;

const int RANDOM_MIN = 1;
const int RANDOM_MAX = 20;
const int ZERO = 0;

//Function declaration for retrieving our random number

int get_Random_Number();

int main()
{
     //variables for our loop counter, and two arrays
     int count, grp_a[ARRAY_SIZE], grp_b[ARRAY_SIZE];

     //Seed the random number generator with the current time

     srand(time(ZERO));

     //For all elements in array, set position in grp_a
     //to a random number, then copy that value into
     //same position in grp_b

     for (count = 0; count < ARRAY_SIZE; count++)
{
          grp_a[count] = get_Random_Number();
          grp_b[count] = grp_a[count];
}

     //Return to OS
     return 0;
}

//Function for retrieving random number. Allow RANDOM_MIN

//through RANDOM_MAX as possible range of values, using
//remainder of division by RANDOM_MAX.
int get_Random_Number()
{
     return (RANDOM_MIN + (rand() % RANDOM_MAX));
}

The code is a little more verbose, but a newer programmer would easily be able to read it. There are comments now indicating what the code is doing. I replaced the hard coded numbers with constants and moved all variable declarations to the beginning of main(). I also moved the random number generation into a function, allowing for a clear, English like description of what we are expecting to be returned rather than having to kludge through some hard coded formula. Take the extra time to write the full logic out to save yourself and the person who may be maintaining that code later on some sanity. Human time is much more expensive than machine time, so try and make it easier for future maintainers. Go with Occams Razor: the simpler answer is usually the better one.

Let’s look at some of my habits that go into my programming style. First, I always plan out my program before I write a single bit of code. The size and scope of the project will determine what method I use. Sometimes I write out logic in pseudo-code like format. I usually use a top-down approach, writing general modules and describing what each specific module will do later. A lot of people consider this excessive and skip this step, but trust me; it cuts down on development time. There is a reason that beginner programmer books include this topic, despite the fact that most college professors just gloss over it. Larger projects call for something more conceptual, such as flow-charts, Entity Relationship Diagrams, or UML if it is Object Oriented. Don’t get caught up on proper syntax of planning. As long as you plan it out in a logical fashion beforehand and make sure all developing parties involved can understand whatever syntax you use, you are in good shape. If the stakeholders can understand your Use Cases, Flow Charts, or napkin sketches, then it is good. Don’t be discouraged if it is not strict to RUP syntax, or whatever the “Design Method of the Month ™” is.

By trying my best to plan out before I write code, I can determine what language will best suit the project as well. Don’t limit yourself to one language—allow for flexibility. Choosing the right language for your project will save you more headaches in the long run.

If I use the pseudo-code approach, I will usually just open that file in my editor, comment it out and leave that in the description of the program, or leave the pseudo-code lines as my main body’s comments. Commenting is crucial to programming, yet it is often left out. I usually leave out comments only if I am providing an example. My rule of thumb for commenting is to comment blocks of code that are working to accomplish one task in the problem solving steps. Commenting code takes you one step closer to the holy grail of software development: reusable code. I always try to imagine that I am describing my programming logic to a beginner programmer. This will allow anyone to follow your code, regardless of programming experience. Encouraging other programmers to do the same, especially in a development team, will assist you in easily using and understanding their code. Remember: “Good programmers write good code; great programmers ‘borrow’ good code.” So be a good programmer and help others become great programmers. ;)

The actual layout of the program should be formatted with easy to follow indentions, and code that is broken out into logical blocks. For instance, if there are 3 steps required to initialize an array, those 3 steps should be grouped together, with a comment preceding the code explaining that you are initializing the array. Variable declarations should be put at the beginning of a function, not in the body of the code. I detest using “for (int x = 0; x < WHATEVER; x++)” because x is declared inside of the loop declaration and is limited to the loop scope. Even for something as miniscule as a counter, it should be at the beginning of the function. Variables should have meaningful names for what there purpose is. N is not a good variable name for a string holding someone’s name.

Try to remove “magic numbers”. Magic numbers are hard coded literals in the middle of code body. I prefer to replace literals with constants defining what they are. For example, instead of defining an array with “x[10]”, I will usually define “const int ARRAY_MAX = 10” and define the actual array as “x[ARRAY_MAX]”, or something to that effect.

I also avoid dynamic memory allocation and pointer use whenever possible. More often than not, I have written code that did not need to allocate memory on the heap at all, and introducing them only created headaches in the long run. When I do need to use new and delete, I immediately write the delete statement as soon as the new statement is done. This insures that that the delete statement is not missed. And I avoid overloading new and delete like the plague. For particulars on C++ programming practices, I recommend Scott Meyers Effective C++ series of books. He goes into great detail about explaining good programming practices such as avoiding the use of macros. A good reference library should be more than decoration.

Finally I test the results. Does the final program meet the requirements? Are the results of the program verifiable and repeatable? Run the program through the rungs with a good set of test data to see if there are any anomalies. Back in the old days I used to run programs through a profiler to see where potential bottlenecks would occur, then work to optimize that portion of code. I have yet to familiarize myself with the OSS tools for doing this, and the current crop of Windows tools for doing this are poor at best. I also will run my program through a debugger. Familiarize yourself with how to work with your platform’s debugging tools. Personally I feel that one area where Windows development excels is with its debugging tools. Watch in real time all variable assignments, and check that objects are instantiated and deleted correctly. On the *nix side, I am partial to Kdevelop and the GNU command line debugger, gdb.

Those are some of my basic programming practices. Of course the topic goes much more in depth and this only scratches the surface. Let me know if you have any suggestions for good coding practices or debugging tools.

2 comments:

Anonymous said...

Check the address below to download most popular debugging tools

http://www.loranbase.com/idx/14/0/Debugging-Tools.html

laser said...

srand(time(ZERO));
error: invalid conversion from `int' to `time_t*'