Bug prevention planning pays off
There’s no getting around the fact that if you write code (whether it’s in
Pascal), you’re going to encounter bugs. That’s a fact of life. But
ideally, you’d like to keep encounters of the creepy-crawly kind to a
minimum, because every minute spent chasing a bug is a minute you could
have spent doing something more profitable. Bug prevention should thus be
high on your list of priorities.
I’ve found that there are certain work habits and practices that, if
followed religiously, tend to make me produce better (more reliable, less
buggy) code, almost against my will. I’d like to share some of those habits
and practices with you now.
It’s tempting to make up variable names as you go, following no particular
pattern, but if you write enough code you’ll soon come to the realization
that a little time spent thinking up intelligent names for variables can
make debugging a lot easier and can even prevent bugs from cropping up in
the first place.
Put some thought into choosing meaningful names for your variables. A
descriptive name like ‘TotalNumberOfItems’, while seemingly long and
unsightly, is actually preferable to a short but cryptic name, such as
‘num’ or ‘n’. Why? Because you don’t have to scan nearby code to figure out
what the variable does. If the variable is named descriptively, it’s even
possible that somebody who has never seen your code before (i.e., your
successor after you get promoted) might be able to figure out how the code
works just by reading the variable names.
An important part of making variable names work in your favor is being
consistent. Some programmers follow a discipline of using prefixes on
variable names, such as ‘n’ at the beginning of numeric variable names
(like nTotal, nMaximum, etc.), ‘b’ at the beginning of Booleans, ‘s’ on the
front of strings, ‘g’ on the front of variables with global scope, ‘k’ on
the front of constants, etc. Consistency of this kind may seem
obsessive-compulsive at first, but down the road you’ll start to appreciate
the way it makes your code more ‘self-evident.’
It should go without saying that in an object-oriented language like
by following a hierarchical naming scheme involving parent and child
objects. For example, if you create a ‘customer’ object, then attach a
‘name’ property to it (with properties like ‘first’ and ‘last’), address
properties, etc., you end up dealing with customer.name.first,
customer.name.last, customer.address.street, etc., instead of a zillion
unrelated-looking variable names. An array of customer objects then becomes
easy to deal with, whereas juggling separate arrays of ‘firstname’,
‘lastname’, and ‘treetaddress’ can be overwhelming after a while.
The Uninitialized Variable
Watch out for uninitialized variables. If you try to use a variable that
has no value (in a comparison statement, for example), you will generate an
error. Assigning a safe ‘dummy value’ to every variable at the time it’s
created is a good habit to get into since it will prevent this kind of
error. What if you’re not sure whether the variable has been declared
before? Check its type with typeof().
if (typeof(myVariable) == 'undefined')
; // don't use it!
The typeof() function will return ‘undefined’ if the variable has not been
Brevity Is No Substitute for Legibility
Always prefer legibility over elegance or conciseness. If an expression can
all means use three statements! You’re not going to win any awards for
brevity, and in many cases, execution speed is the same (or nearly so) with
two or even three statements as it is with one. For example, a nested call
is rarely, if ever, faster than the same statements ‘unrolled.’
// Concise (but unreadable):
x = Math.abs(Math.floor(4 * Math.sin(180 * Math.PI * r)));
// More readable, and just as fast-executing:
x = Math.sin(180 * Math.PI * r);
x *= 4;
x = Math.floor(x);
x = Math.abs(x);
There may be one or two extra register loads going on in the second version
interpreter is pushing just as many values on the stack in either case.
Rolling a bunch of calls into one line of gibberish is no concession to
speed. Don’t be fooled.
Cut and Paste Are Your Friends
A good heuristic for avoiding typos is: Don’t type any more than necessary!
Use Cut and Paste when editing code, to save on keystrokes. I’m such a
klutz that if I have to type a long variable name three times in a row,
it’s a pretty good bet I’ll mess up at least one keystroke somewhere along
the line. So I try to cut down on typing, and bugs, by cutting and pasting
variable names. And guess what? It works!
Typos are a major source of trouble when you’re dealing with nested
function calls and conditions checks, because it’s not clear (after a
while) whether you’ve balanced all your parentheses. Likewise, on long
string literals, you’re likely to break a line with a carriage return
without remembering to introduce a continuation marker (backslash). And in
long strings, don’t forget to check for nested quotation marks. Alternate
the use of single and double quotation marks when nesting quotes.
Otherwise, you’ll make a string terminate early, generating an error.
Incidentally, don’t hesitate to break long lines of code into multiple
lines. You don’t have to use a backslash (continuation marker) as long as
you put your carriage returns between variable names or operators. For
example, I often do things like:
var choice = app.popUpMenu('Typeface:',
// ... etc.
Breaking a long statement like this into multiple lines makes code more
readable and makes typos stand out more, I find.
Comment Your Code
Code that is sparsely commented is hard to maintain. I’m sure you’ve heard
the story about the programmer who, returning to some code he’d written
just two weeks before, nevertheless spent three and a half hours stepping
through the code in a debugger (trying to understand the program’s
structure) before coming to the conclusion that the code’s purpose was so
obvious, it didn’t need commenting.
Today, you may think your code doesn’t need commenting. Tomorrow, you will
realize the folly of yesterday’s decision.
// this is a comment
/* as is this */
There’s something of an art to writing good comments, as with other aspects
of programming. Just labelling an exclusive-OR operation ‘XOR’ doesn’t
explain the purpose of that particular line of code. Think hard about what
the code is doing, then come up with a comment that you (or someone else)
might find helpful during a debugging session. Some lines of code truly
need no explanation. Others may require multiline comments. If you need six
lines of commentary to explain one line of code, use six lines. I’ll say it
again: Brevity is often the enemy of legibility.
Make your code granular by ‘factoring out’ complex operations into short
functions. Generally speaking, it’s easier to understand and debug a
hundred one-line functions than it is to understand and debug one 100-line
function. Of course, it’s possible to go too far in the direction of
‘factoring.’ But my experience has been that most beginning programmers
(which I still am, in many ways, after 12 years) are prone to write
functions that do too much and take up too many lines of code. Break it
down! Keep functions simple. With any luck, you’ll achieve some code reuse
(and not have to write so much code on down the line).
Back It Up
Backing up your code as you work is difficult to remember to do sometimes.
But you’re risking disaster if you don’t make frequent backups. A rule of
thumb that has never let me down is: Always have working code. What that
means is, as soon as something ‘works,’ back it up; and before starting
work on a project that’s halfway done, back it up. You always should be
able to revert to working code if you accidentally paint yourself into a
corner during a ‘bad day.’
Test-Fly Pieces of Code
This is one of my all-time favorite tips. Create a small, blank, one-page
PDF document and name it ‘eval.pdf.’ Add two form fields to it: a button,
and a multiline text field. Call the text field ‘Code.’ Don’t attach any
scripts to it. Call the button ‘Evaluate’ and add a script to the mouse-up
eval(this.getField('Code').value); // evaluate Code
What this line of code does is take whatever you’ve typed into the Code
presents it to the runtime interpreter, as if to say ‘Here, run this.’ If
you type ‘app.beep(0)’ in the Code window, for example, hitting the
Evaluate button should make your computer’s speaker beep.
Use the Code window to test snippets of code before you place them inside
scripts in another PDF document. Whenever you’re not sure how something
works, dummy up a function in the Code window of your form and Evaluate it.
Use the app.alert() method to display the result to the screen. You won’t
believe how much time this trick will save you during development.
Get to Top-Level Scripts Fast
Another real timesaver is to put a button on the any form-in-progress that
takes you straight to the document-level scripts in your form. I don’t know
about you, but I get tired of click-click-clicking through menus and
submenus to get to Acrobat’s top-level code window (Tools:Forms:Document
link on your page somewhere, then in the ‘Type:’ popup menu, select
‘Execute Menu Item.’ Use the dialog that comes up to navigate to the
a link onscreen that, with one click, takes you straight into the
document-level scripts area.
Note that you can also create a link that takes you straight to Page Open
and Page Closed action scripts.
And remember: After a particularly rough coding session, take a long break.