Also: Just because an older version isn't "fully supported" doesn't mean you can't download files (or ask questions, or get help), it just means there won't be any more new EBFs released. For a complete list of everything available for download see Downloads - EBFs / Maintenance.

Friday, January 27, 2012

Another kind of comment inside program code is the Exhortation: A warning or reminder for the maintenance programmer when making changes to code in the vicinity.

Exhortations may be the only kind of comment that deserves to be decorated with boxes, ALL-CAPS and/or exclamation! marks because they are the only kind of comment that may be more important than the surrounding program code... by definition all other comments are less important than the program code because

comments aren't compiled,

comments aren't tested,

comments aren't executed, so therefore

comments can easily be (and often are) wrong.

Exhortations are a bit different: they offer blunt warnings, not subtle guidance or explanations. When an exhortation becomes out-of-date, and therefore wrong, it's usually easy to detect and easy to resolve by simply deleting the comment.

When an exhortation is valid, however, it is often valuable to both newcomers (as a warning about a possible pitfall) and experienced programmers (as a reminder about that pitfall).

Example 1

Here's an example from inside a SQL script that contains CREATE TABLE and other definitions for tables that are subject to the "Data Upgrade" process when an old copy of the application database is upgraded to a new version:

-- ***********************************************************************
-- ***** EVERY CHANGE DOCUMENTED HERE MUST ALSO BE DOCUMENTED IN *****
-- ***** 015c_rroad_data_upgrade_startup.sql EVEN IF IT IS *****
-- ***** NOT SIGNIFICANT TO THE DATA UPGRADE PROCESS. *****
-- ***********************************************************************

What it's saying is that every single schema change made in this module must be checked against code in the other module to see if anything has to be done over there as well.

The "MUST ALSO BE DOCUMENTED" part of the exhortation is saying that a record of that checking must be kept in the other module; in this case, the modification history comments are stored in both locations as evidence the change wasn't forgotten.

Example 2

Redundant code is sometimes just easier to deal with than to eliminate with complex logic. Here's an example of an exhortation that warns of the existence of a debugging version of a tble; when the original base table is altered, the debugging version probably needs to be changed as well:

-- ********************************************************************************
-- ***** WHEN MAKING CHANGES HERE, SEE ALSO THE TRACE_* VERSION OF THIS TABLE *****
-- ********************************************************************************

Example 3

Here's an warning about NULL-versus-non-NULL values that was added to the code after several catastrophic mistakes were made; this warning may only benefit newcomers, but it doesn't hurt for experienced developers to be reminded as well:

-- *****************************************************************************************************
-- ***** DO NOT CHANGE NULL / NOT NULL CONSTRAINTS WITHOUT CHECKING EVERYWHERE THE VALUES ARE USED *****
-- *****************************************************************************************************

This exhortation appears twice in the code, and so does the one from Example 2: both at the beginning and end of the associated CREATE TABLE statement to reduce the chances it'll be missed.

Example 4

Here's an example involving the same business rule being implemented by separate UPDATE statements:

The business rule's the same, but the syntax and semantics of the UPDATE statements are different, and the logic required to implement a single common UPDATE was deemed not worth the effort, not when the changes are easy to make in two locations and an exhortation takes care of the "oops I missed that" problem.

Example 5

Here's an example of two alternate versions of the same table, only one of which exists at run time but both must be maintained... and most changes must be made to both versions.

Once again, the exhortation appears twice inside each table because they're quite long; the ellipsis '...' represents many columns.

Wednesday, January 25, 2012

There are many kinds of comments inside program code, one of them being Section Titles: Comments that introduce the reader to a block of code with the primary purpose being to answer the question "Is this the code we're looking for?"

Folks who hate comments might say: "Different sections of code should appear in separate functions or procedures, with the section title becoming the procedure name."

In the real world, however,

not every section becomes a procedure,

section title comments tend to be longer and more descriptive than procedure names, and

applications with extremely high ratios of call-return-interfaces-to-lines-of-worker-code may become hard to understand and follow.

In other words, every real-world program can use section title comments, and this article is about writing good ones.

Example 1

Here's an example of a not-very-good section title; it's not good because it is vague, telling us only that the section involves sample sets rather than some other commonly-used data objects used in the application:

------------------------------------------------------------------------
-- Perform some standard sample set updates as required.

That section title is not good because the section itself violates the rule for high cohesion as the following nested subsection titles show:

Each of the subsection titles is pretty good, but neither has much to do with the other except the fact they both mention sample sets... hence the vague section title. The two subsections appear together because they can share one FOR loop; perhaps this is an example of coincidental cohesion (the worst), perhaps it is communicational cohesion (the second best, when parts of a module are grouped because they operate on the same data), or somewhere in between, but it is certainly not functional cohesion (the best): when parts of a module are grouped because they all contribute to a single well-defined task of the module.

In this case, the vague outer section title is as good as it's going to get without changes to the actual code... but maybe it's not so bad after all: the vagueness itself can be taken as a warning that "coincidental cohesion lurks within".

In other words, the code ain't perfect so neither are the section titles.

Example 2

Here's a section title that's pretty specific, but not exactly helpful:

------------------------------------------------------------------------
-- Loop through each database that is trying to connect past the connection timeout interval.

Instead of describing what the code's doing, it's concentrating on the how: "loop through each database". The first line of code is a FOR loop so nobody needs to know the "how", it's the "what" of the section that should be described in the title.

Much better: "stop sampling" is a big deal in the world of database monitoring, and it's the whole reason this code section exists yet it wasn't even mentioned in the original title.

Example 3

Here's a section title which confuses the "what" and "how" for a long section of code:

------------------------------------------------------------------------------------
-- Process Alert #1: Loop through each database that...
-- should be sampled,
-- or has timed out,
-- and has not had a recent successful sample.

The word "process" is unforgivably vague since it could mean "issue", "clear" or "cancel", or even "edit", or some combination of all of them; in this case the action is "issue". Also, "Alert #1" is probably appropriate only for someone already familiar with Foxhound, someone who knows that Alert #1 is "Database Unresponsive".

Here's the revision:

------------------------------------------------------------------------------------
-- Issue Alert #1: Database Unresponsive for each database that...
-- should be sampled,
-- or has timed out,
-- and has not had a recent successful sample.

Example 4

Here is yet another vague "Loop through" section title on a FOR loop exhibiting lower-than-functional cohesion, together with the subsection titles:

------------------------------------------------------------------------
-- Loop through each database that...
-- should be sampled but isn't,
-- has timed out and should be retried, or
-- still has user_request = 'Stop Sampling' even though sampling has stopped.
------------------------------------------------------------------------
-- Exit immediately if Foxhound is shutting down.
------------------------------------------------------------------------
-- Get the Sample Schedule settings for this moment in time.
-- This information will be used in later sections.
------------------------------------------------------------------------
-- Start sampling again after sample loop connection number was dropped.
------------------------------------------------------------------------
-- Start sampling for timeout retry.
------------------------------------------------------------------------
-- Check and reset user_request if sampling has already been stopped.
------------------------------------------------------------------------
-- Stop sampling if required to by the sample schedule.

In this case the associated code was changed and enhanced to provide new features, and the section and subsection titles were changed as well.

The biggest change, however, was to the outer section title to make it clear that the subsections exhibit lower-than-functional cohesion:

------------------------------------------------------------------------
-- Start or stop sample sessions as required.
------------------------------------------------------------------------
-- Exit immediately if Foxhound is shutting down.
------------------------------------------------------------------------
-- Get the Sample Schedule settings for this moment in time.
-- This information will be used in later sections.
------------------------------------------------------------------------
-- Start sampling each database that should be sampled but isn't.
------------------------------------------------------------------------
-- Start sampling if the sample schedule requires it.
------------------------------------------------------------------------
-- Start sampling for timeout retry.
------------------------------------------------------------------------
-- Check and reset user_request and schedule_request if sampling has already been stopped.
------------------------------------------------------------------------
-- Stop sampling if required to by the sample schedule.

Also, the outer section title was stripped of its point-form overview of subsection descriptions: sometimes repetition helps but not here; the subsection titles are all that's needed.

Note that one of the lines above isn't really a section title, it's a warning to the reader:

-- This information will be used in later sections.

The Bottom Line

The Number One Reason, perhaps the only reason for Section Title Comments is to answer the question: "Is this the code we're looking for?"

Also: Just because an older version isn't "fully supported" doesn't mean you can't download files (or ask questions, or get help), it just means there won't be any more new EBFs released. For a complete list of everything available for download see Downloads - EBFs / Maintenance.

Also: Just because an older version isn't "fully supported" doesn't mean you can't download files (or ask questions, or get help), it just means there won't be any more new EBFs released. For a complete list of everything available for download see Downloads - EBFs / Maintenance.

Wednesday, January 18, 2012

No amount of slick propaganda can rearrange the basic software development life cycle:

Develop, Test, Release, Repeat

No one can put Release before Develop, not unless they have a time machine. It is possible to put Release before Test but that is usually so unsuccessful that an impartial observer might say the Release never happened.

If the same staff are involved in both Develop and Test (as they often are, and perhaps should be) then a big problem can arise when the cycle reaches Repeat:

It's hard to go back to development when you've been testing for release.

During final testing the code is usually frozen and everyone is risk-averse. The only changes allowed are those required to fix problems, and not all problems are fixed... no one wants to make any changes that might break something else.

After the Big Release Day, it's time to Repeat which means go back to Development. For management, that's easy: it's all just one long schedule to them, just one day later, and there's probably been some slippage which means everyone's already late for the next Release.

For developers, it's not so easy... especially if they just came off the death march called Test. They're still risk-averse, and Development requires risks to be embraced: Concentrate on the enhancements without obsessing about the effect they might have on the rest of the program. They're also tired, and Development requires enthusiasm.

What the developers need, and often do not get, is an extra step to decompress and rejuvenate:

Develop, Test, Release, Relax, Repeat

A word to the wise for management: If developers don't get time to Relax, they'll take it anyway... perhaps not consciously, but through bad morale, lost productivity and extra sick time. And it might cost more in the long run.

Whoa!

We haven't even reached the Gotcha! question yet, what's going on? Inserting NULL is what DEFAULT AUTOINCREMENT tries to do when it runs out of numbers:

When the next value to be generated exceeds the maximum value that can be stored in the column to which the AUTOINCREMENT is assigned, NULL is returned. If the column has been declared to not allow NULLs, as is true for primary key columns, a SQL error is generated.

Here's the thing, though: UNSIGNED BIGINT works as expected, and so does the following code...

Yes, indeed...

...the underlying cause is that SYSTABCOL.max_identity is defined as BIGINT so it can't hold any value larger than 9,223,372,036,854,775,807. Never mind that DEFAULT AUTOINCREMENT can't be used to generate negative values, signed BIGINT was chosen as the data type.

Never mind that the Help says any old integer will do, which, presumably, includes UNSIGNED BIGINT: "When using AUTOINCREMENT, the column must be one of the integer data types, or an exact numeric type."

Wouldn't that make sense? That's what DEFAULT GLOBAL AUTOINCREMENT does, isn't it? If you explicitly insert a value outside the partition, the next DEFAULT AUTOINCREMENT value is calculated as if the explicit INSERT never happened: "Default column values are not affected by values in the column outside the current partition".

That's NOT the last value that was inserted into t3, it can't store that because it wouldn't fit in a BIGINT. No, it stores the maximum possible positive BIGINT value, and that, folks, is the point of this whole game:

Also: Just because an older version isn't "fully supported" doesn't mean you can't download files (or ask questions, or get help), it just means there won't be any more new EBFs released. For a complete list of everything available for download see Downloads - EBFs / Maintenance.

There's lots of good stuff, most of which I agree with, like making names pronounceable and using vertical alignment.

The comments about comments, however, seem a bit shallow, especially in the CACM article: examples of poorly-written comments in poorly-written code are used to conclude that comments should be avoided. The opposite is true: well-written comments are enormously helpful in explaining the "what" and "why" of a piece of code; no matter how readable the code is it only presents the "how".

Comments have other uses, sometimes historical: documenting as-yet-unsolved bugs, explaining why some unexpected or unusual change was made, even explaining why some change was not made.

To put this information anywhere other than in the code itself is to guarantee future maintenance programmers will never read it.

16 and 17: The CREATE SERVICE statement names the search procedure as being responsible for responding to browser requests with raw HTTP.

43 through 53: This loop analyzes the HTTP parameters passed from the browser and stores the values in the appropriate local variables. You can also pass named parameters via the CREATE SERVICE and CREATE PROCEDURE statments, but in the long run it's easier to maintain a simple NEXT_HTTP_VARIABLE() loop like this one.

55 through 68: This code performs some cleanup and editing of the input paramenters.

70 through 97: This CASE statement processes the two possible actions: 'Search' when the Search button is pressed, and 'Open' when a file name is double-clicked.

90 through 95: The xp_cmdshell system procedure call opens the selected file using "Wordpad Classic". This exact code probably won't work for you unless you have installed the original Windows XP Wordpad utility as "wordpadx.exe" in this particular location. However, it will work if you change this line

117 through 161: The SELECT returns a single HTML string to the browser. The \x0d\x0a characters are all unnecessary; they have been included here to make the HTML text more readable.

122 through 126: The JavaScript function searchF() and openF() allow the "Search" and "Open" actions to be customized. This may not be required in the case of searchF(), but with openF() the file_name parameter depends on where openF() was called from.

151 through 158: The SQL LIST() and STRING() functions builds a series of active, double-clickable P paragraph tags to display all the matching file names. Each ONDBLCLICK attribute calls openF() to pass the action and file_name HTTP input parameters to the search service.

Here are the Windows commands to create the SQL Anywhere database, start it with the HTTP server running on port 12345, compile the code using dbisql and then launch it in your browser: