Today I want to talk about modifications we make before we begin explicitly refactoring.
1: Pull each routine into its own file.
Like all modifications, this has pluses and minuses. The major minuses are:
a) There are more routines to keep track of -- using version control, this shouldn't be an issue
b) Make files-- If your program had been a monolithic beast before you started working with it, you may have been able to avoid the special joy that is MAKE. As they say, it builds character....
The advantage here is locality. Having each routine in its own file lets us find something on line 127, instead of searching for line 12097. The more we work with the code, the more powerful it becomes to have a small, clear area in which to focus our attention.
2: Turn the compiler warnings to maximum, and clean up everything.
This is another pain point. When I first applied this to my most recent project I had well over 200 things to fix. Mostly these fall into one of 2 classes:
A) Variables that are declared but not used. These are completely benign. Delete each one and move on. No fuss, no muss.
B) Variables that are not properly initialized. This is the first class bugs that we encounter. Everything should be explicitly initialized. Yes, I know that it is possible to use compiler switches to have uninitialized variables set to 0. That works, until you switch compilers or change optimization settings, or .... And then you get gibberish results, usually the night before a conference presentation, so that you aren't sure whether any of the results are trustworthy.
The basic principle is that we want the code to convey _ALL_ of the information about what the routine does. Relying on compiler settings is another way letting important information slide out of the code.
But the real reason for getting to the compile clean is that when we change the code, a new warning will stand out in clear relief, rather than getting lost in a blizzard of old, non-information warnings. Having the ability to draw our attention to an unintended change, as close in time to when that change was made, is invaluable.
3: Insert IMPLICIT NONE at the beginning of every routine.
Again, the goal is to make the code communicate as much as possible. The implicit variable types made sense in the late 1950's. They don't today. Using IMPLICIT NONE forces us to clearly how we intend to use variables.
The biggest advantage of IMPLICIT NONE is that it it removes a class of errors. Under implicit typing, if we misspell a variable name, the compiler silently creates a new variable. IMPLICIT NONE lets the compiler help, just like a spell checked does for a document.
4: Use KIND to specify the precision you require.
The specification of precision is another way of making your intent clear. The whole point of KIND is to facilitate updating and porting code. Take advantage of it.
5: Add INTENT settings for every argument.
INTENT is another way of letting the code communicate. Every argument can be specified as read only, write only, or read/write. This is another point may seem like overkill, but as we start to restructure the code, the added information to the compiler will again help it draw our attention to the exact point at which we've change the code's behavior.
All of this may seem like busy work, and who needs that? But I've found that taking the time to lay a solid groundwork pays handsomely, because it give quick feedback when a change has an unintended consequence.