March 16, 2007

Configurable Defaults

Configurable defaults?
I know a 100% reliable test to distinguish amateur product manager from the professional. When amateurs hear about loadable parameters (shown in my previous post), they immediately urge a closest developer to make those parameters editable from the application's user interface. Professionals never do that mistake.

Everybody's got something to hide
Should I explain? Not all parameters have to be available for user. Some values are result of big research effort, such as value ProcessTimeOut stored in the Registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Asr.
It's better to protect users from the accidental change of such values. Unfortunately, in many products I saw too much configurable parameters irrelevant to everyday user needs.

Cost of configurable parameters
Configurable values need not only load function, but also its store counterpart. Coding efforts increase in two times, and testing efforts increase in four times (please think why). In addition, you need to develop and test user interface controls to change configurable values. The cost difference between configurable values and semi-constants is significantly larger than the cost difference between semi-constants, encapsulated decisions, literal aliases, and parameter descriptors discussed in my previous posts. That's why parameters should be configurable only when the ability to configure them brings profit to at least 80% of your target auditory. Do not urge people to pay for features they do not need.

As a rule, prefer to process things automatically. The user is often the weakest part of the information technology processes. Avoid the user involvement in the processing, unless it is necessary.

Quiz
Give three examples when user-configurable defaults are preferable to non-configurable defaults. Note, I mean defaults, not documents created by the user.

March 15, 2007

From Encapsulated Values to Semi-Constants

Decisions to semi-constants
My previous post is about encapsulated decisions. In trivial case, they are just encapsulated values - constants hidden behind the function interface. You can make a step further and make them loadable from some storage. I call such parameters semi-constants, because their values are predefined, but the parameters themselves are not const in the C++ sense.

Listing 1. Example of semi-constants
class LoadableParameters
{
public:
Parameters() { Load(); }
const char * ProductWebSite() const { return web_site_url.c_str(); }
unsigned long HashTableSize() const { return hash_table_entries_number; }
private:
std::string web_site_url;
unsigned long hash_table_entries_number;

void Load()
{
Repository storage;
const char key_name[] = {"product_attributes"};

const char site_value_name[] = {"web_site"};
if ( ! storage.LoadString( key_name, site_value_name, &web_site_url ) )
{
const char web_site_url_default[] = {"http://myCompany.com"};
web_site_url = web_site_url_default;
}

const char hash_value_name[] = {"web_site"};
if ( ! storage.LoadUnsignedLong( key_name, value_name, &web_site_url ) )
{
const unsigned long hash_table_size_default = 37;
hash_table_entries_number = hash_table_size_default;
}
}
};


Application of semi-constants
Semi-constants are well suited for use in the families of similar products, when the value of particular parameter is different for different products. For example, the software accompanying the hardware device can have loadable reference to the device's web page. The plug-in for the host program (such as add-in module for Microsoft Office) can have loadable lowest supported version number of the host application. If features of your product are implemented as separate modules, you may make loadable list of features that are available for particular flavor of the product: product "business" has features A, B, and C, product "home" has features C and D, product "big money" has A, B, C, and D.

Where to store semi-constants
In a popular scenario, the product installation procedure stores values of the parameters inside the Windows Registry, or in the disk file. At startup, the program reads those values.
Modern network-oriented scenarios assume that values are stored on the remote server. The product support team owning the server may change the values invisibly for product users.
If your product uses the database for the customer's data, you may add a table for storing parameters of your application.
Some systems assume that user's credentials (user name and password) are stored on smart cards.
Standard way of keeping locale-specific data, such as text strings translated to target languages is to store them inside language packs as Windows Resources.

By default
Note that attempt to load a semi-constant may fail (for example, because the server is temporarily disconnected from the network). You need provide default values using named constants or encapsulated decisions described above.

Delayed load
If you will made the instance of LoadableParameters class from Listing 1 the global variable or the member of the class instantiated as a global variable, parameters will load at the program startup. If the loading of semi-constants involves long-executing procedures (such as loading data from the Internet), you may want to postpone actual loading to the moment in time when parameters will asked for a first time.

Listing 2. Delayed load of semi-constants
LoadableParameters & LoadableParametersInstance()
{
// constructed at first call of LoadableParametersInstance()
static LoadableParameters single;

return single;
// destroyed at exit from the program
}

March 7, 2007

From Aliases to Encapsulated Decisions

This post continues the theme "Pass names, not values".

Listing 1. Another style of grouping aliases
{
const TCHAR WEEKLY_INSTRUCTIONS[] = {"\\\\ServerName\\FolderName\\Instructions.txt"};
const DWORD QUERY_ATTRIBUTES = 0;
const DWORD DO_NOT_SHARE = 0;
const LPSECURITY_ATTRIBUTES HANDLE_CANNOT_BE_INHERITED = NULL;
const DWORD NOT_APPLICABLE = 0;
const HANDLE DO_NOT_USE_TEMPLATE = NULL;

LPCTSTR file_name = WEEKLY_INSTRUCTIONS;
DWORD access_mode = QUERY_ATTRIBUTES;
DWORD share_mode = DO_NOT_SHARE;
LPSECURITY_ATTRIBUTES security_attributes = HANDLE_CANNOT_BE_INHERITED;
DWORD actions_to_take_regarding_file_existence = OPEN_EXISTING;
DWORD attributes_and_flags_or_security_quality_of_service = NOT_APPLICABLE;
HANDLE template_file_supplying_attributes = DO_NOT_USE_TEMPLATE;

HANDLE hFile = ::CreateFile(
file_name, access_mode,
share_mode, security_attributes,
actions_to_take_regarding_file_existence,
attributes_and_flags_or_security_quality_of_service,
template_file_supplying_attributes
);
}

First part of the scope defines the aliases for literals:
• application-defined path;
• and operating system values that do not have textual aliases.
Note that aliases are defined as const. Use of const prevents aliases from accidental change by assignment, like
NOT_APPLICABLE = expression;


Second part initializes parameter descriptors. According to safety rule, if the function has the pointer parameter, and does not modify the value of that pointer, the author should declare the pointer as const. The CreateFile function prototype violates this rule for 4-th parameter, represented in our example by argument security_attributes: according to CreateFile specification, it is an input-only pointer, but it is not declared as const. That looks a little bit odd by contrast with the first parameter file_name: input-only pointer, which is const. Oh, that Win32 API…

Third part of the scope is the function call.

Local aliases to global aliases
Sometimes you may want to share a literal between different scopes. The simplest way to share a literal (more precisely, the alias) is to make it the global alias:
const TCHAR WEEKLY_INSTRUCTIONS[] = {"\\\\ServerName\\FolderName\\Instructions.txt"};

// scope 1
{
// ...
LPCTSTR file_name = WEEKLY_INSTRUCTIONS;
// ...
}

// scope 2
{
// ...
LPCTSTR file_name = WEEKLY_INSTRUCTIONS;
// ...
}


Visibility
Scopes, which reuse the alias, may be parts of the one file, or they may be spread between modules. The alias is visible from the module, if the use of the alias in the module does not contradict to programming language rules (in other words, if the compiler does not complain that the alias name is unknown). There are several different flavors of global aliases, distinguished by an alias visibility.

Popular flavors of global aliases
VisibilityHeader (.h)Implementation (.cpp)
Module local constant   const int x = 1;
Shared constantextern const int y /*= 2*/;#include "MyHeader.h"
const int y = 2;
Implicitly copied constantstatic const int z = 3;#include "MyHeader.h"


Aliases to encapsulated values
If words "global variable" scare you, hide literals (and arrays of literals) inside inside encapsulated values - functions returning constants, like this one:
LPCTSTR WeeklyInstructionsFilePath() throw() 
{
return "\\\\ServerName\\FolderName\\Instructions.txt";
}


Compound encapsulated decisions
Encapsulated values are trivial forms of encapsulated decisions. Encapsulated decisions that are not trivial include at least two parts:
• the function call
• and the preparation of well-known reusable arguments for this call.
Compound encapsulated decisions may use other encapsulated decisions.
The Listing 2, which derives from the Listing 1, shows an example.

Listing 2. Encapsulated file opening.
const TCHAR WEEKLY_INSTRUCTIONS[] = {"\\\\ServerName\\FolderName\\Instructions.txt"};
const DWORD QUERY_ATTRIBUTES = 0;
const DWORD DO_NOT_SHARE = 0;
const LPSECURITY_ATTRIBUTES HANDLE_CANNOT_BE_INHERITED = NULL;
const DWORD NOT_APPLICABLE = 0;
const HANDLE DO_NOT_USE_TEMPLATE = NULL;

HANDLE OpenWeeklyInstructionsFileExclusively( DWORD access_mode )
{
LPCTSTR file_name = WEEKLY_INSTRUCTIONS;
DWORD share_mode = DO_NOT_SHARE;
LPSECURITY_ATTRIBUTES security_attributes = HANDLE_CANNOT_BE_INHERITED;
DWORD actions_to_take_regarding_file_existence = OPEN_EXISTING;
DWORD attributes_and_flags_or_security_quality_of_service = NOT_APPLICABLE;
HANDLE template_file_supplying_attributes = DO_NOT_USE_TEMPLATE;

return ::CreateFile(
file_name, access_mode,
share_mode, security_attributes,
actions_to_take_regarding_file_existence,
attributes_and_flags_or_security_quality_of_service,
template_file_supplying_attributes
);
}

HANDLE OpenWeeklyInstructionsForQuery()
{
DWORD access_mode = QUERY_ATTRIBUTES;
return OpenWeeklyInstructionsFileExclusively( access_mode );
}

HANDLE OpenWeeklyInstructionsForRead()
{
DWORD access_mode = GENERIC_READ;
return OpenWeeklyInstructionsFileExclusively( access_mode );
}


Encapsulated decisions as design specification
Despite their simplicity, encapsulated decisions are in fact very valuable part of your program: they concentrate your constructive thought. Interestingly, those functions are highly reusable across modules and projects.

Popular flavors of encapsulated decisions
VisibilityHeader (.h)Implementation (.cpp)
Module local function   int x() throw() { return 1; }
Shared functionint y() throw();#include "MyHeader.h"
int y() throw() { return 2; }

Implicitly copied function
inline z() throw() { return 3;}#include "MyHeader.h"


Listing 3. Poor man's encapsulated decisions
HANDLE OpenWeeklyInstructionsForQuery()
{
return ::CreateFile(
"\\\\ServerName\\FolderName\\Instructions.txt",
0, 0, NULL, OPEN_EXISTING, 0, NULL
);
}

HANDLE OpenWeeklyInstructionsForRead()
{
return ::CreateFile(
"\\\\ServerName\\FolderName\\Instructions.txt",
GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL
);
}


Cost of typing against the cost of maintenance
What is better: 15 lines of the code in the Listing 3 or 36 lines of the code in the Listing 2?
Many people rushing to finish already late project say "Of course, 15 lines - it is shorter!" Indeed, the Listing 3 contains less code than Listing 2. However, the understanding of the Listing 3 requires much more time than typing of 21 explanation lines: you need to refresh in your memory the description of CreateFile (about of 170 lines), then you need to compare implementations of OpenWeeklyInstructionsForQuery and OpenWeeklyInstructionsForRead parameter by parameter.

Summary
If your code contains multiple calls of CreateFile or any other function with literal arguments, think about encapsulating those calls inside product-specific functions having less number of arguments. Use parameter descriptors and aliases inside encapsulated decisions in order to make your code comprehensible.

February 12, 2007

From Literals to Aliases

Abstract
Previous post was about parameter descriptors. Values that initialize the parameter descriptors may be easy or hard to understand. Today we'll try to make the understanding easier.

Yet another everyday nightmare of code reader
Recall the Listing 2 from previous post.
Readers are happy when the parameter descriptor is initialized by system constant with self-explaining name, such as:
DWORD actions_to_take_regarding_file_existence = CREATE_ALWAYS;

Unfortunately, that cases occurs rarely. More often, the initializer is a brain-teasing value, like that:
DWORD access_mode = 0;

What this 0 mean? No access? Full access? Read-only access? Poor reader again and again has to:
• open documentation (if available), or the source code (if available),
• search the description of the parameter in question (if available),
• and look at meaning of value 0 (if available!).

When developers miss parameter descriptors, the situation is even worse.

Listing 1. Find the mistake
HANDLE hFile = ::CreateFile(
"\\\\ServerName\\FolderName\\Instructions.txt", 0,
0, NULL, 0, CREATE_ALWAYS, NULL
);

In the expression above, the developer mistakenly put CREATE_ALWAYS after last 0. It should be before last 0. The person reviewing developer's work overlooked this bug. Did you?

Literals are sources of problems
Values like 0, NULL, or "\\\\ServerName\\FolderName\\Instructions.txt", being used directly in the source code, are called literals.
It is very tempting to use literals when you're in hurry in order to save several precious seconds. However, you or your colleague will spent minutes if not hours in attempts to understand why the code does not work as expected. In other words, passing literals as function arguments trades maintainability for code writing speed.

From literals to aliases
Help your readers! Instead of literals, use aliases - appropriately named constants that add clear interpretation to otherwise meaningless values like NULL, -1, and hardcoded paths to data. This simple action:
• makes the code self-documented,
• greatly reduces the effort to understanding what is expected from the function call,
• helps to find errors easily.

Listing 2. Find another mistake
{
const TCHAR WEEKLY_INSTRUCTIONS[] = {"\\\\ServerName\\FolderName\\Instructions.txt"};
LPCTSTR file_name = WEEKLY_INSTRUCTIONS;

const DWORD FILE_ACCESS_FOR_QUERY_ATTRIBUTES = 0;
DWORD access_mode = FILE_ACCESS_FOR_QUERY_ATTRIBUTES;

const DWORD FILE_DO_NOT_SHARE = 0;
DWORD share_mode = FILE_DO_NOT_SHARE;

const LPSECURITY_ATTRIBUTES DEFAULT_SECURITY_ATTRIBUTES = NULL;
LPSECURITY_ATTRIBUTES security_attributes = DEFAULT_SECURITY_ATTRIBUTES;

DWORD actions_to_take_regarding_file_existence = CREATE_ALWAYS;

const DWORD FILE_ATTRIBUTES_AND_FLAGS_NOT_APPLICABLE = 0;
DWORD attributes_and_flags_or_security_quality_of_service = FILE_ATTRIBUTES_AND_FLAGS_NOT_APPLICABLE;

const HANDLE FILE_DO_NOT_USE_TEMPLATE = NULL;
HANDLE template_file_supplying_attributes = FILE_DO_NOT_USE_TEMPLATE;

HANDLE hFile = ::CreateFile(
file_name, access_mode,
share_mode, security_attributes,
actions_to_take_regarding_file_existence,
attributes_and_flags_or_security_quality_of_service,
template_file_supplying_attributes
);
}

How aliases help
When you read Listing 2, you may ask: "From one hand, the file containing weekly instructions opens with access just for query attributes. From other hand, the file has to be created from scratch always. Is this behavior intended? Is the code correct?".
I bet, you never ask that question seeing the Listing 1 in real code because the Listing 1 is not informative - all you can say is that some file is creating always, thanks to the one non-literal constant.

Note how the good alias for literal file path may substantially change the mental interpretation of code. The word "weekly" helps the reader to find and prevent problems of the code: are instructions (whatever they are) updated weekly? Should the reading or writing occur exactly once a week?

I recommend create aliases for every hardcoded path to data: file name, registry key, URL, database connection string, and so on. Try your best to give them informative names.


Alias for 0: to write or not to write?
By creating the named constant for a zero you make the code understandable - in most cases. Most notable exceptions are:
• Loop counter for (int count = 0; count != LIMIT; ++count)
• Head of a vector &slides[0]


Parameter descriptors and aliases go best together


Function call helperParameter descriptorsAliases
What it describesWhat the function doesWhat is passed
Aspect of the dataAbstractConcrete
SideServerClient


Summary
Do not assume the reader of your code knows what means 0 or -1 passed as a parameters to the function.

February 9, 2007

From Arguments to Descriptors

Listing 1. Maintenance quiz for typical source code
Look at following statement:
HANDLE hFile = ::CreateFile( instructions, 0, 0, NULL, CREATE_ALWAYS, 0, NULL );

Can you guess what means 6-th parameter (with third 0 passed as argument)?
Do you know the type of 4-th parameter (with first NULL passed as argument)?

Stop maintenance nightmare
Your code includes tens to thousands function calls:
• to system API,
• to C++ standard library,
• to third-party libraries,
• to your own classes and free functions.
Readers of your code (including yourself) do not remember the exact ordering, type and purpose of each parameter/argument in each function call. So help your readers!

From in-place arguments to parameter descriptors
Instead of passing argument directly to the function, use parameter descriptors - appropriately named temporary variables, initialized with arguments you want pass to the function.
Now everybody instantly sees:
• the type of each parameter and argument,
• the role that parameter/argument plays during the function call.
Parameter descriptors are good documentation of the function being called.

Listing 2. Parameter descriptors make the code readable
{
LPCTSTR file_name = instructions;
DWORD access_mode = 0;
DWORD share_mode = 0;
LPSECURITY_ATTRIBUTES security_attributes = NULL;
DWORD actions_to_take_regarding_file_existence = CREATE_ALWAYS;
DWORD attributes_and_flags_or_security_quality_of_service = 0;
HANDLE template_file_supplying_attributes = NULL;

HANDLE hFile = ::CreateFile(
file_name, access_mode,
share_mode, security_attributes,
actions_to_take_regarding_file_existence,
attributes_and_flags_or_security_quality_of_service,
template_file_supplying_attributes
);
}


Recommendations
When you create the parameter descriptor, make sure it has exactly the type of the correspondent function parameter. That will help you detect type mismatches between actual argument and formal parameter.
I recommend enclose parameter descriptors and the function call in curly braces in order to limit the visibility and the lifetime of parameter descriptors.

Do not copy brainlessly
When inexperienced developers create parameter descriptors for WinAPI functions, they often copy the names of parameters from API documentation to names of parameter descriptors. That shortcoming reduces the usefulness of parameter descriptors, because API names often are too cryptic. Can you guess the purpose of the parameter dwCreationDisposition?
My advice: do not copy parameter names from the documentation. Instead, describe their meaning. Well, copy - not the name, but the sentence describing the parameter - to your code. Then convert sentences to parameter names.


Summary
Do not assume the reader of your code knows the type and purpose of arguments passed to the function or returned from the function. State them explicitly via parameter descriptors.

February 7, 2007

Reading Is Living

Reading as living
Developers read the software code for living. They write too, of course, but writing of one line is accompanied by reading of 5-10 lines. You literally live in the world of code, and you make the code alive. Reading is living. If nobody reads the code, that code probably is dead.

Room
In order to live comfortably, you need enough space. That means, you need optimal density of information: not too dense, not too sparse. The code with too long lines without visual separators, and the code full of comments and blank spaces, both look equally unattractive.

Navigation
When you move across the virtual space, you need:
• consistent separation of the module to logical parts, such as levels, subsystems, and functional points;
• reasonable size of correspondent parts;
• "landmarks", which simplify the orientation in the code;
• clear visual separation between main stream of control, secondary roads, and dead-ends.

Communication
The reading of the source code may be more informative than the talk with your colleague - if you wrote the code with future maintenance in mind. The main goal of the code is to convey ideas.

The code must speak to the reader.

Most source codes I saw during my life looked unnaturally. The reason is not the artificial nature of programming languages. Programs look so strange because authors do not express their ideas in a human manner. They try to speak like silicon-based aliens. The people-oriented coding practices seem to be better alternative.

Work
Your work on the code magically turns into work of the code inside the computer. You are interested in detection whether given piece of the code does really work, or just pretends to be useful.

Rest?
Is the source code good place for the rest, entertainment, funny jokes? I don't think it is. The personal life is private. The code (especially commercial one) is stored for decades in the source control repository, and is open to any reviewer. Keep your private life and your more or less public code separately.

February 6, 2007

Reading Is Learning

Reading as learning
Developers and maintainers read the API documentation and the source code in order to learn something new, or well-forgotten old. Reading is learning. When you have enough knowledge to complete the task, you stop reading and start writing.

Elements
Learning starts with simplest, easiest, recognizable, and memorable things. When you are learning, begin from the basics, like object's lifetime control (opening/closing, construction/destruction). When you teach the reader of your code, begin with well-known, trivial operators of C++ language, variables of fundamental types, plain function calls, and non-template classes.

Glossary
In order to understand the problem area, the human being needs to know the language of that problem area - specific words that define essential things and actions. Definitions of problem-oriented terms should be used consistently. Well-chosen term describes only one entity. When you read or write the code, watch out aliases - different names for one object or process. Watch out homonyms - cases where one name used for different objects or processes. The worst case is a term with a variable meaning applied to group of unrelated objects - in fact, it does not have any meaning.

Examples
The easy way to clarify the definition is to give an example. The shorter the distance between the definition and example is, the better the understanding is. Good examples do not contain irrelevant details.

Sequence
The order, in which new definitions are appearing and using, does matter. The reader easily understands new terms, if they are based on already well-known terms. Forward definitions ("I'll explain it in details next week, just be patient") require extra motivation of the reader, so avoid using them.

Reminders
Good teacher helps the students to memorize the course and reminds them already explained stuff. Often you should repeat the part of the lesson, or else the auditory will forget everything.

February 2, 2007

Reading Is Searching

Reading as searching
People read in the search of information. Reading is searching. When you found the relevant information, you stop reading.

Cache
The simplest thing to do with search is do not throw found information away. Keep useful results in the cache:
• in the dedicated document under source control,
• on the team wiki page,
• in the web log on corporate web server.

Shortcut
If the information is already stored in safe place, provide the short annotated path to it:
• a bookmark in your web browser,
• a cross-reference from the project document,
• an universal resource locator (URL) in the source file.
Note that in Windows you can create your own information access protocols: in addition to http, ftp, and mailto you may create your own custom protocol that will start particular application with particular parameters. The example is in MSDN article titled "Registering an Application to a URL Protocol".

Keywords
When you want to find the information, it seems reasonable to use the specialized search system, such as:
• http://search.yahoo.com/,
• the Search tab in the Visual Studio,
• the Silly Dog in the Windows XP,
• its daughter from Clippit - Ms. Dewey http://www.msdewey.com/,
• or some less exotic engine.
In any case, you may use the set of keywords as the kind of URL. The more specific keywords you use, the less results the engine will find, and the more relevant the found results are.

Algorithms, or how to search
You may learn much more about Internet searching on the web site http://www.searchlores.org/ of Fravia+. Although the site now lives not so intensively as in previous millennium, and sometimes turns from the inspiring poesy of searching to bore prose of software cracking, it is still a good resource.

Reference lists
As number of cached records, shortcuts, and keywords grows with time, people tend to combine references into arranged lists: favorite links, alphabetic index, information directory, list of deliverables, and so on. Don't miss the good opportunity.

January 31, 2007

Why and How of Reading

Starting the series of articles
In order to turn the source code reading into profit generator, we need to know why different persons - developer, reviewer, and maintainer - read the code at all. We'll analyze typical reading scenarios and elaborate a set of reading-friendly software development techniques. Those techniques will allow you to accomplish reading tasks faster and cheaper than your competitors.

Why the developer reads the source code
The short answer is because the developer makes sure that just written code does what it is supposed to do.

Implementing the function
When you are in the developer position and write the body of the function X (assuming the design work is already done), your tasks are:
1. Find the sequence of elementary steps you need to perform in order to complete the task. For example, in order to create memory-mapped file in Windows, you need first open the file, then create the memory mapping section linked to that file, and then create the range of addresses ("view") over the memory mapping section. The time here is spent when you:
• Read background topics in the Operating System API documentation.
• Search for ready samples in API documentation.
• Search for algorithms in books and in the Internet.
2. Write the inline code, the call to API function, the call to third-party library function, or the call to your own function implementing each step. You spend the time on following topics:
• Search for the function doing the required task in the Operating System API documentation.
• If previous search failed, try to find a function doing the required task in your project or previous projects.
• If previous search failed, try to find a function doing the required task in third-party library (including libraries already bought or to-be-bought).
• If there is no such function, or if the library is too expensive for your task, you need to write the function yourself. Generally, it means dividing the task to subtasks and repeat previous steps for subtasks.

Calling the function
When you write the call to the function Z, your tasks are:
1. Gain basic knowledge about the function: type and purpose of return value and parameters, prerequisites, special conditions, errors. Your occupation is:
• Read the official documentation of Z provided by authors or vendors.
• If the official documentation is missing, getting the description from the Internet.
• If there is no shared description (which by itself is very bad sign), read source code. At least the header file to this function should exist, or else you should not use that function.
2. Transfer the function description from a genuine or surrogate documentation to the call site in the source code. That includes:
• Copying the function signature and adapting it to your coding conventions.
• Obtaining values to be used as function arguments, and passing them to function parameters.
• Analyzing results and processing errors.
3. Integrate supporting libraries into your build system:
• Copy libraries and correspondent C++ headers into your source code tree.
• Include header files into correspondent C++ files.
• Include paths to libraries and header files in your build configuration.
• Add library object files to your linker configuration.
4. Make sure the function does compile:
• Verify that all formal parameters have matching actual arguments.
• Verify that types of correspond to types of formal parameters.

Debugging
When you go through your code step by step in the debugger, your tasks are:
1. Determine what particular function does:
• Analyzing the function name.
• Analyzing the names and types of passed arguments and return value.
• Analyzing how parameters are passing.
• Reading comments, if available.
2. Determine how the function execution affects the system:
• Determine what data should change.
• Determine how the data should change.
• Examine which data are in fact changed.
• Examine how data are in fact changed.

Why the reviewer reads the source code
The short answer is because the reviewer prevents the code with undesirable effects to be stored in the master source repository.

Reviewing
When you are in the reviewer position and look at new code fragment, your tasks are:
1. Understand what the code fragment does. Time passes when you:
• Determine which parts of fragment are known/unknown.
• Determine the origin of unknown parts: written by developer, parts of Operating System API, and parts of third party API.
• If unknown parts are written by developer, search and read the implementation.
• If unknown parts come from some API, read the documentation. That is similar to how the developer gains base knowledge about the function.
2. Determine whether the actual behavior of the fragment is the desired behavior:
• Look for visual symptoms of frequently occurring bugs - resource leaks, buffer overflow, possibility of bad pointers to pass in, disabled compiler warnings, etc.
• Look at compiler warnings for symptoms of frequently occurring bugs: variables used without being initialized, assignment in conditions, type cast with truncation, etc.
• Compare actual behavior of the fragment with the functionality required by project documentation.
• Compare the fragment implementation with known similar fragments in other parts of the source code.
• Ask the developer to explain parts that are difficult to understand.
3. Ensure that new fragment follow the project's coding style. The time is mostly spent by visual searching for formal mismatches in the new text.

Why the maintainer reads the source code
The goal of maintainer is to make desired change in the existing functionality.

Maintaining
When you are in the maintainer position and look at the project's source code, your tasks are:
1. Understand the purpose of the project, module, or file. Time eaters are:
• Read the project documentation: requirements and design documents.
• Read the test documentation: acceptance checklists and step-by-step test cases.
• Read the reference information accompanying the shipping product: manuals, instructions, help system.
• Read the source code. That is similar to understanding of code fragment by reviewer.
• Search the description of intended behavior in transcripts of developer's communication with a customer.
• Read the source code of test client applications.
2. Determine what parts of the module to be changed are risky:
• Look in the version control system for files that change often or experience big changes.
• Reading bug reports.
• Reading product release notes and frequently occurring problems reported by help desk.
• Talk with testers, developers, and managers.
3. Find the place in the code where the change will be made:
• Read the code from the "just do it" perspective.
• Read the code from "minimal change" perspective.
• If the part to be changed is risky, read the code from the "minimal harm" perspective.
4. Apply the change. It is similar to developer's tasks.
5. Document your change. The time consuming operations are:
• Update the project documentation.
• Update the reference information accompanying the shipping product.

Try it at home
Could you analyze these tasks yourself? Why the reading requires so much time? How to make the source code, and optionally other documentation, readable? Invent your own solutions of that problem. Which relationship between tasks will help you? What can be automated? This is your homework today.

January 29, 2007

Readability: the Key to Big Income

Power of global code base
During the history of computers, about of 350 million different computer programs were written around the world. Each year brings to life about of 10 million new programs, including new versions of existing ones.
Most of those programs were compiled from source codes written on different programming languages. And those source codes still exist today. There are about of 200 trillions of source code files stored in the Internet.

Can you get advantage of reuse?
There are hundreds of ready solutions for literally any information technology task available in the Internet in the form of source code. It seems very attractive and profitable to use the knowledge collected by others. Just steal the appropriate source code file, add it to your program, and enjoy the success. Your information processing task solved, the program sells. Easy money!

Oops, not so easy.

Write-only code
A random pick of 60 code files doing particular task - getting the function name by its address - shows that source files are in general:
• unreadable due to ugly coding style,
• hard to understand due to obscure logic and lack of explanation,
• difficult to reuse due to numerous explicit and implicit dependencies between code parts.
Code maintainers call this kind of source code "write-only".

Unreadable code breaks the business
The reuse of the source code stolen from Internet is just an example of the problem related to write-only code. Another problem, more important to you, regards the code written by yourself. If the source code of your own program is write-only, you cannot maintain the program - in other words, your business cannot grow. Did you heard about the operating system OS/2? Its development stopped because nobody at IBM could understand its sources.

When the source code is read
In order to write readable source code, you should understand what "readable" means.
Any source code line of any computer program is read at least four times:
• when the developer creates new fragment of the source code containing this line,
• when the developer walks the code fragment line-by-line in the debugger after writing and compiling the fragment,
• when the lead engineer or code buddy reviews it in the difference utility before storing into source version control system,
• and when the maintainer searches for the code to be changed due to extension or bug fix.
The maintainer in turn writes new fragments, and the history repeats.
Each of those activities requires the person in charge to spend some time. Big time is big money. Preventing the time loss is equal to gaining money. Soon we'll see where and how you may earn a few bucks for bread and butter.

January 22, 2007

Laws of code maintenance

Law of additional effort
The greater the quality, the greater the amount of work.

If the quality is not your goal, you just ask what is the code goal, write the source code, compile it and throw away (I mean, give it to customer).

If you want to keep the customer happy, you also write project documents, do code reviews, perform step-by-step debugging, add prerequisite and invariant checking to the code, write test client applications, ensure complete code coverage, profile the code and optimize the performance, determine memory usage trends and reduce the program working set… And so on, and so on.

Law of additional typing
Most program maintenance problems are caused by poor communication ability of the source code. If the code does not express the intent of the author, the maintainer cannot determine the purpose of code fragments, and cannot change the code properly.

Any maintenance problem may be solved by additional explanation, which in fact means additional typing. Additional typing is the cost of understandability. And poor understanding it is the reason of 80% of maintenance failures, as my experience in code reviews shows.

Two sides of code fragmentation
1. The less is the size of the function or class, the less are chances to make error, the more are chances to write correct implementation from first time, and the more are chances to reuse that code in other projects.
2. The less is the size of the function or class, the more your project is fragmented to small pieces, the harder it is to understand it, and the more are chances to make error establishing interfaces.

January 19, 2007

Model Of Your Reader

An importance of looking well
Artificial beings, such as software building the program and OS runtime, parse the program source code and the compiled binary automatically. So turn your face to your human readers - reviewers and maintainers. Remember, the program is you. Spend a bit of efforts to represent yourself smoothly. Make your code looking well.

Listing 1. Excerpt from the source code of real program
void
RescanFile(
HWND hwnd
)
{
COMPITEM ci=NULL;
int iStart;
int iEnd;
int i;

if (selection_nrows < 0 && DisplayMode == MODE_EXPAND) {
if (DisplayMode == MODE_EXPAND) {
iStart = 0;
iEnd = 1;
} else {
iStart = selection;
iEnd = iStart + selection_nrows;
}
for (i = iStart; i < iEnd; i++) {
ci = view_getitem(current_view, i);
if (ci != NULL) {
compitem_rescan(ci);
}
}
} else {
sdkdiff_UI(TRUE);
MessageBox(hwndClient,
LoadRcString(IDS_NOTHING_RESCANNED),
szSdkDiff, MB_ICONSTOPMB_OK);
sdkdiff_UI(FALSE);

return;
}
PostMessage(hwndClient, WM_COMMAND,
IDM_UPDATE, (LPARAM) ci);
} /* RescanFile */


Quiz
Try to answer following questions, while looking at the Listing 1:
• What is i?
• What is the purpose of variable selection?
• Why the function is called RescanFile?
• Which variable represents the file?
• What does the function compitem_rescan()?
• What is the effect of the expression sdkdiff_UI(FALSE) ?
• What is hwnd?
• Can hwndClient be invalid? What are consequences?
• Which operator is executed after last compitem_rescan(ci) ?
• Does the message box mean success?

Analysis
By reason I'll explain in another place, I did not compile and test the code containing that fragment. I assume the code compiles and even works, but I don't like how it looks. For me, this code is hard to read, hard to understand, and hard to maintain.
1. Three different naming conventions mixed together: WinAPI notation (names like "typeName"), C++ notation (names like "something_somethingelse"), and UNIX guru notation (names like "ci"). The code looks inconsistent.
2. Variables with names starting from i are defined and used by rules of C. The variable ci is initialized as in C++, but used like in C. The code is inconsistent.
3. The names of variables and functions do not tell me, what is going on.
4. Although the source code calls WinAPI functions MessageBox and PostMessage that may fail, it does not analyze their return values.
There is no notion of successful completion and failure.
The execution flow is not contiguous.
5. Most variables processed by the function RescanFile come from nowhere and go to nowhere. Data flow is implicit, the inputs and outputs are not reflected in the function's parameter list.
6. I have to look outside the function in order to understand types of DisplayMode, current_view, etc.
7. Real meaning of conditions tested by ifs is not clear.
Each of those little inconveniences may lead to big time loss and even to bugs introduced during the maintenance.

Program is you. Or your user?
Believe or not, the developer writing the code (especially in a hurry) writes for an ideal reader - the best reader in the world, who is capable to understand completely every line of each source file, its purpose, and the reason why it was implemented in that particular way. Now the time for an interesting fact: the more experienced the developer is, the less mental power is expected from the ideal reader. Experienced developers give the reader more hints, than beginners do.

Write as if your ideal reader is less capable than you. Or, at least, write for the person like you. You are the most powerful model of your ideal reader.

Recommendations
I recommend following steps to make the code readable:
1. Don't force you reader to scroll the program back and forth. Describe the function parameters explicitly. Declare, define and initialize variables inside block scope where they are used. Make the code flow more linear, contiguous.
2. Use names that help to understand the code.
3. Use consistent naming notation.
4. Process the code returned by platform API functions.
5. Have clear notions of success and failure of the function.
6. Express the meaning of conditions.

Listing 2. Corrected source code
enum HowToCombineThreadInput
{
THREAD_INPUT_DETACH = 0,
THREAD_INPUT_ATTACH = 1
};

void CombineCurrentThreadInputWithMainThread(
HowToCombineThreadInput combination
)
{
BOOL attach = ( THREAD_INPUT_ATTACH == combination );
sdkdiff_UI( attach );
}

compitem * GetComparisonByRowNumberOrGetCurrentComparison(
view * currentView,
long rowNumber
)
{
return view_getitem( currentView, rowNumber );
}

void RedoChecksumBasedComparisonForRepresentation(
compitem * comparisonOfTwoFilesRepresentation
)
{
compitem_rescan( comparisonOfTwoFilesRepresentation );
}

bool ChecksumBasedComparisonForSelectedRows(
HWND differenceViewerMainWindow,
int displayMode,
long selectedRowInTable,
long numberOfRowsInSelection,
view * currentView
)
{
if ( ! ::IsWindow(differenceViewerMainWindow) )
{
return false;
}
if ( ! currentView )
{
return false;
}

const bool atLeastOneRowSelected = ( numberOfRowsInSelection > 0 );
const bool viewIsExpandedViewOfOneFile = (displayMode == MODE_EXPAND);

if (
! atLeastOneRowSelected && ! viewIsExpandedViewOfOneFile
)
{
CombineCurrentThreadInputWithMainThread( THREAD_INPUT_ATTACH );

LPCTSTR messageText = LoadRcString( IDS_NOTHING_RESCANNED );
// TODO: add resource IDS_PROGRAM_TITLE
LPCTSTR programTitle = LoadRcString( IDS_PROGRAM_TITLE );
UINT contentsAndBehaviorOfDialogBox = MB_ICONSTOP or MB_OK;

if ( ! ::MessageBox(
differenceViewerMainWindow,
messageText,
programTitle,
contentsAndBehaviorOfDialogBox
) )
{
// TODO: process fatal error
}

CombineCurrentThreadInputWithMainThread( THREAD_INPUT_DETACH );

return false;
}

long selectedRowFirst = selectedRowInTable;
long selectedRowAfterLast = selectedRowFirst + numberOfRowsInSelection;
if ( viewIsExpandedViewOfOneFile )
{
selectedRowFirst = 0;
selectedRowAfterLast = 1;
}

compitem * comparisonOfTwoFilesRepresentation = NULL;
for (
long rowNumber = selectedRowFirst;
rowNumber < selectedRowAfterLast;
++rowNumber
)
{
comparisonOfTwoFilesRepresentation =
GetComparisonByRowNumberOrGetCurrentComparison(
currentView,
rowNumber
);
if ( comparisonOfTwoFilesRepresentation != NULL )
{
RedoChecksumBasedComparisonForRepresentation(
comparisonOfTwoFilesRepresentation
);
}
}

if ( ! ::PostMessage(
differenceViewerMainWindow,
WM_COMMAND,
IDM_UPDATE,
(LPARAM)comparisonOfTwoFilesRepresentation
) )
{
return false;
}

return true;
}

void RescanFile(
HWND // hwnd
)
{
if ( ! ChecksumBasedComparisonForSelectedRows(
hwndClient,
DisplayMode,
selection,
selection_nrows,
current_view
) )
{
// TODO: process error
}
}


Maintenance complexity
I had to look in a lot of places in order to complete the sample. Particularly, I had to know about author-defined types: compitem, COMPITEM, view, and VIEW. I had to read comments to the functions and look into implementations, because comments are not always informative.
I don't apologize for names like GetComparisonByRowNumberOrGetCurrentComparison,
because they describe what those functions really do - by desire of their author.
The rewrite of code revealed bugs in the implementation:
• misuse of types int and long,
• fatal error in the code processing error situation,
• hard-coded (non-localized) program title.

Don't trouble the trouble
Although Listing 2 gives more hints to potential reader, I would not like to maintain it. Potential sources of problems are more visible now, but prevention of problems requires making change in other parts of the program. The code fragment now is 4 times longer (The function that does main work is 2 times longer than Listing 1). More things to remember, more chances to introduce an error.

Rewriting the bad code makes sense only if the bad fragment is encapsulated.

January 18, 2007

Tool vs. Brain

What that tool automates?
You have to do a lot of routine operations manually before even trying to use the tool, which is supposed to automate those operations. Only in this case you can understand what kind of your efforts is saved by tool, an in what way. Often the tool requires you to pay too much - or to do too many operations unnecessary for task solution.

Brain still required
Note that automatic tools do not reduce the need of the software engineer to use brain. Tools may verify the syntax, but they cannot write syntactically correct code that solves your task. Tools can find that some code has been changed, but they won't say what the change is for. Tools can notice that since version 1.23.4685 the function Baz returns not the same value as earlier, but they cannot know why. It is the human, not the tool, who makes a decision.

Make you code understandable
A person, which makes the decision about your code, should have enough information to be sure. Main source of information is the code itself. The same briefly: source code. Even with the best tools on the Earth, the maintainer still has to look into your sources. So make them easy to understand.

January 15, 2007

The Program Is You

The program you write is you
The program consists of commands you issue to solve the customer's problem. Notice again: you issue commands, not the computer. You solve the problem, not the compiler. When the user complains "when I press the button, nothing happens", it's you who forgot to process WM_BUTTON_PRESS, not the operating system. Computer, OS, and compiler just store your solution in the form that hardware and software can process. The program is digitized content of your mind.

The program is the representation of you that is "shown" to customer, like distorted sounds of your voice is the representation of you that is "shown" to person calling you by the phone.

So that #%$^& product our team finished last year is me?
Yes, it is.

And that #%$^& blob of #%$^& code I wrote Friday evening is me?
Yes, it is.

World of code
Several hours of each workday you live in the world created by code authors. It's somehow like reading the book or watching the spectacle - you are in. The difference is that you can take other book if the author makes you feel boring. Quit the theater during the action is harder, but still possible. Quitting the boring source code is unfeasible by the reason of salary you are receiving for maintenance of that code.

Don't annoy your readers.

Simple rules of ethics
Don't make silent assumptions. Always articulate your assumptions explicitly. If the C++ syntax doesn't allow you express the assumptions in code, make them in comments.
Don't even try to save a little of your coding time by the cost other people will pay. You are here to make the life of code clients and product users easier, not harder.

Programming is issuing commands
The programmer does not "make" the software package. (Software package, by the way, is the most important part of the product.) The programmer just issues the orders how the package should be organized and how it should behave. Programming is all about orders and commands.
As a programmer, you did issue much more commands to the build system, runtime system and OS than all your managers to you.

You are responsible
You are personally responsible for each command you wrote. Even if the code is generated by the "code wizard application", you are the author because you pressed the button "Generate the code". If the particular code in the program is not attributed to particular person, you are responsible for a whole program.

How the human differs from computer in executing commands
Very often commands you have receive from leaders were unclear or simply dumb. Sometimes you did not know what to say and said - er, um, hm, etc.
Imagine for a moment, that all the manager tells you is recording and can be replayed. No one manager will like it. Even the best manager sometimes contradicts to himself. However, you may vary the way you execute the order and as a result you come with a solution that satisfies the manager.

Computer cannot do that.

Even if the manager speaks to you in artificial language (see "Software for Your Head" by McCarthy), you can stop participating in the discussion. Computer cannot. From other hand, the computer, or the software product, can break/crash/hung.

Be careful writing the code
The source code of the program is the testament graved in stone. It is your trace in the memory of the human race. Often it is the only trace you leave.

January 12, 2007

An art of expression

Programming language expresses all
Due to lack of automated tools verifying contract obligations of the source code, modern developers have to express those obligations in a form of programming language expressions. The source code becomes the contract regulating the use of the source code. Got it? See the recursion?

The source code of modern program has multiple purposes:
• to solve user's task;
• to express the designer intentions;
• to regulate data flow and control flow between client and server parts of the program;
• to catch the mistakes that occur during development;
• to implement non-functional requirements.

The code is written by the developer - for whom? Who consumes the source code?

Three views to the code consumer
The beginner programmer often thinks that the consumer of the source code is the compiler. Respectively, beginners attempt make the program "easy to compile". In other words, they try to fit the program into their, often wrong, expectation of how compiler works.

The average programmer often thinks that the consumer of the source code is the author. Respectively, more or less advanced programmers attempt to express their ego programmatically. They use esoteric C++ constructs and create own variant of function SetStringInComboBox, often as a private member of some class.

And only the experienced programmer, creating design documents by source code, surviving code reviews lead by (incompetent) manager, and leading code reviews of (incompetent) subordinates, understands that that the ultimate consumer of the source code is random unprepared reader.

Brothers and sisters! You write programs for yours poor tired brothers and sisters. Please, help them easily understand your code. Source code is a communication medium. If other people easily understand your code, then you and the compiler will understand it too.

Source code is a communication medium. Assume your boss, or your grandfather, or your daughter will read your source code. Make it clean.

The code is better than conversation
To be more precise, the code stored under the source control is better than the poorly remembered conversation that occurred six months ago.

As a study (*) shows, "many problems arose because developers were forced to invest great effort recovering implicit knowledge by exploring code and interrupting teammates and this knowledge was only saved in their memory".

Face-to-face meetings with teammates represent an interruption that is hard to recover from.

Instead of trying to draw the implicit knowledge from humans, use explicit, written repositories of code-related knowledge:
• Project documentation,
• Bug databases,
• Source history,
• Comments,
• Books and magazines,
• Internet.

Write down mental models. Scan the napkin where the author explained the idea, and store the picture in the source control system.

(*) Thomas D. LaToza, Gina Venolia, Robert DeLine. Maintaining Mental Models: A Study of Developer Work Habits. ICSE'06, May 20-28, 2006, Shanghai, China.

January 10, 2007

$500 For Stopping My Wrong Program To Compile

Callable entity
Anything the C++ programmer can call - functions, operators, constructors and destructors - are the callable entities. The callable entity is anything that has parenthesis in its definition.
The callable entity is the server providing useful work, the caller is the client delegating the efforts to server.
Victor Shtern recommends to programmers do push responsibilities to the server.

The callable entity has contractual obligations
The work with each callable entity involves you in relationship of several kinds:
1. Between you (server) and your client.
2. Between you (client) and callable entities functions your code calls.
3. Between you (developer) and the originator of the idea why the entity doing so and so should be written.
4. Between you (developer) and maintainer of your code.
5. Between you (maintainer) and inventors of ideas behind the original code.
6. Between you (author of current code version) and the user of the software product.
7. Between you (author of current code version) and the vendor of development tools: compiler, linker, and so on.
Each relationship is regulated by specific contract.


Client-server contract describes:
· Pre-conditions that should be met before client calls the server.
· The data passed between client and server (entity's interface).
· Effects of the server execution other than client-server data exchange.
· Post-conditions that should be met after the server completes the call.


Inventor-developer contract describes:
· What should be done, and why.
· What should be not done, and why.
Developer-maintainer contract describes:
· What the code in fact does, and why.
· What the code in fact does not, and why.
· How the wrong program behavior is reported to maintainer.


Author-user contract describes:
· Guarantees of fitness for particular purpose (functionality, stability, performance, security).
· How the wrong program behavior is reported to user.


Author-tool vendor contract describes:
· Rules of programming language.
· Deviations from standard rules.
· Application programming interface (API).


"- So my function BeepAndExitProgram() five lines long is the subject of all those contracts?"
Yes, it is.

Bad contracts
Unfortunately to most participating parties, often contracts of any particular callable entity are:
· unwritten;
· or stored separately from the callable entity code;
· or ill-formed (incomplete, ambiguous, contradictive), so they allow multiple interpretations;
· or cannot be executed to force parties behave according to contract.

Good contracts
You should avoid such careless approaches to contracts. Keep your assumptions and requirements about callable entity explicitly written in the form of the source code.

The rule is simple: the code that violates contract should not compile.

What I pay for
The problem of modern software engineering is the lack of tools that:
· verify various contract obligations,
· and do not allow the product to build if it violates obligations.
Just find the way to express contracts in C++ (or otherwise integrate contract verification with a build process), and I'll pay $500 for that tool.
Start with a simple C++ preprocessing tool (parser), that detects only one of hundreds possible pre-conditions: that COM function could be invoked without preceding CoInitialize(). I'll pay $25 for that tool. Anybody will.

January 9, 2007

Getting Paid to Hello, World

Regular operating system users definitely will have no profit from the program, which source code is:
#include <Windows.h>

#include <string>
#include <iostream>

int main()
{
try
{
std::string greeting( "Hello, world!" );
std::cout << greeting << std::endl;

DWORD frequencyHertz = 880;
DWORD durationMilliseconds = 400;
::Beep( frequencyHertz, durationMilliseconds );

return 0;
}
catch(...)
{
return 1;
}
}

However, software developers have benefits from the program itself and from the process of its implementation. Developers use them as technology test tools.
Developers know well: even the production of such a simple program is a challenge, and the resulting string output on the command line terminal may be considered a victory. And money will follow the winner.

What are details?

First, the program source code must be stored.
"Stored" in our era means not merely "saved on the disk", but also "put under source version control" and "regularly backed up".
Give the simple, seamlessly working storage system to developers, and they will pay.

Second, you should have libraries supporting the programming language. In our case, this is C++ Standard Library represented by and header files, which, in turn, include other header files.
This is where std::string, std::cout, and std::endl came from.
There are several known realizations of the C++ Standard Library. Writing your own one requires significant intellectual effort, so for starters you may just re-implement a part of the library or make your own extension. If it makes the client's life easier, clients will reward you in casso.
Going in another direction, you could sell by competitive price the detailed comparison of different C++ library implementations. Your recommendations, which one is good for particular usage scenario, will make someone's day.

Third, you need the libraries connecting your program to resources of your operating system. The use of function Beep requires the inclusion of , which requires the installation of Windows Platform Software Development Kit (often called Windows SDK or Platform SDK).
On my Windows box I have 4 different versions of Platform SDK. Offer me a way to manage them easily, and I'll refund your efforts.

Next, the program source code must be accessible to build tools: compiler, linkage editor, and librarian.
Open the program text above in your latest super-powerful Integrated Developer's Environment (IDE) and try to "just build" it. You can't. In order to build, you need to provide command line parameters for build tools. In big IDE that means you should set up the infrastructure: project file, makefile, build configuration, or whatever the manufacturer calls it. You may conquer the market with the tool that generates the project by single source file. Or the utility that converts Autotools makefiles or project files made by XCode to solutions usable from Visual Studio.

Most important, the program must compile.
It should be written according to the rules of the programming language: C++ in our case.

To be continued...