Save the Right Time
It is so easy to "save time" during development. But it's seductively easy to "save time" on a task that will require you to waste much more time later. Therefore, beware of exactly how you are saving time, especially when you are writing code. While many people think of "writing code" as the "job" of a programmer, saving time when you are writing code can be disastrous.
-
Always prefer read time optimization to write time optimization when coding. You write a piece of code one time, and will read it at least several times (possibly dozens) after that. If you save yourself a minute writing the function, but have to spend an extra five minutes deciphering the function each time you go back, you've made a ridiculous tradeoff. (This is a key flaw of inheritance.)
-
Always prefer maintenance time optimization to construction time optimization when coding. Maintenance is the most critical, longest, least predictable, most difficult, and most expensive phase of the FSDLC. Anything you do that makes maintenance more difficult (even unknowingly, even by accident) is a ridiculous tradeoff. This is one of the key reasons for the design phase, to create good application designs that simplify maintenance.
-
Declare and initialize all variables immediately before they're needed. There are situations where you have to write longer code (especially during development, before you've been able to identify all the functions you're going to encapsulate out). Searching for variables at the beginning of a long function (50-100 lines) is a nightmare.
-
Keep longer functions organized into shorter blocks as much as possible. Preferably encapsulate those blocks out, when this isn't possible however, organize it into blocks that are as well abstracted as you can; this lets you imitate the look and advantages of encapsulated functions.
Binding Time
Binding time is a key concept in programming; this is the time that you initialize the value of a variable. You can bind the value of a variable when you're writing code, when the program is compiled, or when the program is running. Binding time has important site design implications, but it's most important to keep it in mind during construction. That's when last minute modifications to the design can make it so tempting to take short cuts.
I had frequently heard about binding time and its importance, but I had a very difficult time learning even this much about it, much less good rules for it. I had heard people describe early vs. late binding time as a trade off between ease (early binding) vs. flexibility (late binding). Now that I understand binding time and its role in an application, I respectfully disagree with this analysis. The "complexity" involved in late binding is so negligible, and the benefits derived from it so important, that I consider late binding a best practice. Rather than recap the theoretical discussions that gave me such problems, here is a much better way to understand binding time.
-
Coding Time - The earliest form of binding time is to initialize a variable when you write the code. This means using a magic number or a magic string, ie literal numbers and text, e.g.
myVar = 21; or employeeSalary = baseSalary * 3.286. This is foolish. Even advocates of early binding time don't go so far as to recommend magic numbers. They are classic examples of WET (Write Everything Twice) code, they're completely inflexible, and they provide no semantics to explain what the number stands for.
-
Compile Time - This is the earliest usable form of binding time. In best practices, this means using named constants and / or enumerations. e.g.
myVar = freezingPointOfWater or myVar = (int)Temperature.FreezingPointOfWater This a useful technique because it's highly semantic and prevents the value from being changed. When working with an item whose value does not change, this is the way to go, but obviously it's not feasible for variables. Initializing constants at compile time also means that you benefit from compiler errors if something goes wrong. Misspelling the name of a constant or enumeration is a common error, for example, but the compiler finds all those problems for us automatically so we can eliminate them.
-
Load Time - This is one form of late binding previously lumped together as "Run Time". Binding at any phase of run time provides much more flexibility than the alternatives and should be used except in cases like named constants where we Don't want that flexibility. Load time is when the application first loads. Any variables defined from external resources or configuration files are defined at load time, e.g.
myVar = WebConfigurationManager.ConnectionStrings[inputConnectionNameInWebDotConfig].ConnectionString; Load time is an excellent choice for application variables that need to be set once and never again. Load time variables are also excellent choices to store in caches (usually static variables that are permanent for the life of the applicaiton) to avoid having to fetch the values from external resources more than once.
-
Instantiation Time - This is another form of late / run time binding. Instantiation time occurs when a particular object is created. While the application will only load once, it may initialize (create) an object many times. This usually means encapsulating initialization of the variable in a separate function. e.g.
myVar = getValueOfMyVar(); or employeeSalary = getSalary(employeeId); Instantiation time is an excellent choice because function calls are so beautifully flexible. Moreover, variables that a user configures at the start of a program or the beginning of an action must be defined at instantiation time or later. If you try to initialize them earlier, they must be reinitialized later (probably at instantiation time anyway). Instantiation time is one of the two key ways of binding to permit user personalization and other dynamic functionality in an application.
-
Just-in Time - This is the latest binding time. Just-in time occurs when a particular object is used. This means encapsulating initialization of the variable in a separate function and calling that function at the time of use. e.g.
myVar = getValueOfMyVar(); or employeeSalary = getSalary(employeeId); The technique for binding at just-in time is identical to binding at instantiation time; the only difference is when the function which determines it is called. If the variable is initialized once when the object is created (e.g. in an object's constructor) then it's initialized at instantiation time; if it's initialized repeatedly (e.g. in an event handler) then it's initialized at just-in time. Just-in time is one of the two key ways of binding to permit dynamic functionality in an application.
Encapsulating variable initialization is the most important form of late binding and a best practice; it's at the heart of the factory pattern, one of the workhorse design patterns in software development. While the factory pattern has a number of formal requirements, the primary purpose is to delay binding time until the last possible moment. Inside the factory function (which actually generates the variable's value), I find enumerations to be especially useful, since factory items are typically highly related and have only a limited number of options – which is practically a definition of when enumerations are a best practice. This is also a great pattern, because code throughout your project calls the factory and thereby gains instantiation or just-in time flexibility (and information hiding). Yet inside the factory, you gain the advantage of throwing compile time errors, such as if you misspell an enumeration. You literally get the best of both worlds.