How To Write Unmaintainable Code
Originally published at http://mindprod.com/unmain.html.
Reprinted by special permission from the author. Further reprints, without
the express permission of the author are prohibited.
Last updated Thursday, 05-Apr-2001 03:41:36 PDT by Roedy
Green ©1997-2001 Canadian Mind Products.
Introduction
Never ascribe to malice, that which can
be explained by incompetence.
Napoleon
In the interests of creating employment
opportunities in the Java programming field, I am passing on these tips
from the masters on how to write code that is so difficult to maintain,
that the people who come after you will take years to make even the
simplest changes. Further, if you follow all these rules religiously, you
will even guarantee yourself a lifetime of employment, since no one but
you has a hope in hell of maintaining the code. Then again, if you
followed all these rules religiously, even you wouldn't be able to
maintain the code!
You don't want to overdo this. Your code
should not look hopelessly unmaintainable, just be that way. Otherwise it
stands the risk of being rewritten or refactored.
General Principles
Quidquid latine dictum sit, altum videtur.
Whatever is said in Latin sounds profound.
To foil the maintenance programmer, you have
to understand how he thinks. He has your giant program. He has no time to
read it all, much less understand it. He wants to rapidly find the place
to make his change, make it and get out and have no unexpected side
effects from the change.
He views your code through a tube taken
from the centre of a roll of toilet paper. He can only see a tiny piece of
your program at a time. You want to make sure he can never get at the big
picture from doing that. You want to make it as hard as possible for him
to find the code he is looking for. But even more important, you want to
make it as awkward as possible for him to safely ignore anything.
Programmers are lulled into complacency by
conventions. By every once in a while subtly violating convention you
force him to read every line of your code with a magnifying glass.
You might get the idea that every language
feature makes code unmaintainable -- not so, only if properly misused.
Naming
"When I use a word," Humpty
Dumpty said, in a rather scornful tone, "it means just what I
choose it to mean - neither more nor less."
Lewis Carroll -- Through the Looking Glass,
Chapter 6
Much of the skill in writing unmaintainable
code is the art of naming variables and methods. They don't matter at all
to the computer. That gives you huge latitude to use them to befuddle the
maintenance programmer.
-
New Uses For Names For Baby
|
20,001 Names
For Baby |
0-380-78047-X |
Carol McD. Wallace |
Buy a copy of a baby naming book and you'll never be at a loss for
variable names. Fred is a wonderful name, and easy to type. If you're
looking for easy to type variable names, try adsf.
-
Single Letter Variable Names
If you call your variables a, b, c, then
it will be impossible to search for instances of them using a simple
text editor. Further, nobody will be able to guess what they are for.
If anyone even hints at breaking the tradition honoured since FØRTRAN
of using i, j, and k for indexing variables, replacing them with ii,
jj and kk, warn them about what the Spanish Inquisition did to
heretics.
-
Creative Missspelling
If you must use descriptive variable and
function names, misspell them. By misspelling in some function and
variable names, and spelling it correctly in others (such as
SetPintleOpening SetPintalClosing) we effectively negate the use of
grep or CodeWright search techniques. It works amazingly well. Add an
international flavor by spelling theater and theatre in different
situations.
-
Be Abstract
In naming functions and variables, make
heavy use of abstract words like it, everything, data,
handle, stuff, do, routine, perform
and the digits e.g. routineX48, PerformDataFunction,
DoIt, HandleStuff
and do_args_method.
-
A.C.R.O.N.Y.M.S.
Use acronyms to keep the code terse. Real
men never define acronyms; they understand them genetically.
-
Thesaurus Surrogatisation
To break the boredom, use a thesaurus to
look up as much alternate vocabulary as possible to refer to the same
action, e.g. display, show, present. Vaguely hint
there is some subtle difference, where none exists. However, if there
are two similar functions that have a crucial difference, always use
the same word in describing both functions (e.g. print to mean
write to a file, and to a print on a laser, and to display on the
screen). Under no circumstances, succumb to demands to write a
glossary with the special purpose project vocabulary unambiguously
defined. Doing so would be unprofessional breach of the structured
design principle of information hiding.
-
Use Plural Forms From Other Languages
A contributor was the proud author of a
VMS script that kept track of the "statii" returned from
various "Vaxen". Esperanto, Klingon and Hobbitese qualify as
languages for these purposes. For pseudo-Esperanto pluraloj, add oj.
You will be doing your part toward world peace.
-
CapiTaliSaTion
If you use intercapitalization for
function names (capitalize the first letter of each word), randomly
capitalize the first letter of a syllable in the middle of a word. For
example: ComputeRasterHistoGram().
-
Recycle Classnames as Member Names
In C/C++: Make sure to create parameters
and local variables that have the same name as member variables of the
class whose method they appear in. For extra points, create nested
local variables inside {} blocks that have the same name as these
local variables. The goal is for careful examination of scope to be
the only way to tell which context is actually being used.
-
Åccented Letters
Use accented characters on variable names.
E.g.
typedef struct { int i; } ínt;
where the second int's i is actually
iacute. Since these are structures the three int's are not
interchangeable, but with an 8-bit aware editor it's nearly impossible
to distinguish the slant of the accent mark. Insist the new "ints
" are for some specific, but very obscure, situation where a
standard int won't suffice. It helps to toss extra stuff into the
structure before making that claim!
-
Exploit Compiler Name Length Limits
If the compiler will only distinguish the
first, say, 8 characters of names, then always use more characters,
but vary the endings e.g. var_unit_update() in one case and
var_unit_setup() in another.
-
Underscore, a Friend Indeed
Use _ or __ as an identifier.
-
Mix English and Bahasa Indonesia
If English is your second language, use
English for variable names, functions, messages appearing on-screen,
and comments. However randomly intersperse them with words chosen from
your native language. If your boss insists you use English tell him
you can organize your thoughts better in your own language, or allege
racial discrimination and threaten to sue your employers for a vast
sum of money. Now that your boss does not understand your code you can
use obscuration techniques that you would never get away with
otherwise and will foil a future maintenance programmer even if she is
also a native speaker or is equipped with a bilingual dictionary.
-
Extended ASCII
Extended ASCII characters are perfectly
valid as variable names, including ß, Ð, and ñ characters. They are
quite impossible to type without copying/pasting.
-
Deutch, Français, Esperanto
If you cannot find the right English word
to convey the meaning of a temporary variable (and you ignore the
other suggestions about not giving meaningful names to variables), you
may use a foreign language word as the name of the variable. For
example, instead of using variable "p" for a
"point", you may use "punkt", which is the German
word for it. Maintenance coders without your firm grasp of German will
enjoy the multicultural experience of deciphering the meaning. It
breaks the tedium of an otherwise tiring and thankless job.
-
Consider equals As A Variable
Name
Choose variable names that are English
versions of operators, to get statements like:
openParen =
(slash + asterix) /
equals;
-
Bedazzling Variable Names
Choose all variable names to be words that
have no association whatsoever with the actual function of the program
but which have strong inherent meaning as words. For example, you
could end up with statements like:
marypoppins =
(julieAndrews + starship) /
mailbox;
This confuses the reader because they
have difficulty disassociating the concepts of the words from the
logic they're trying to think about. (Combine this with the above
method and it's basically impossible to recover, because search and
replace becomes impossible.)
-
Rename and Reuse
This trick works especially well in Ada, a
language immune to many of the standard obfuscation techniques. The
people who originally named all the objects and packages you use were
morons. Rather than try to convince them to change, just use renames
and subtypes to rename everything to names of your own devising. Make
sure to leave a few references to the old names in, as a trap for the
unwary.
-
Reuse Names
Java lets you create methods that have the
same name as the class, but that are not constructors. Exploit this to
sow confusion.
-
When To Use i
Never use i for the innermost loop
variable. Use anything but. Use i liberally for any other purpose
especially for non-int variables. Similarly use n as a loop index.
-
Conventions Schemtions
Ignore the conventions in Java for where
to use upper case in variable and class names i.e. Classes start with
upper case, variables with lower case, constants are all upper case,
with internal words capitalised. After all, Sun does (e.g. instanceof
vs isInstanceOf, Hashtable). Not to worry, the compiler won't even
issue a warning to give you away. If your boss forces you to use the
conventions, when there is any doubt about whether an internal word
should be capitalised, avoid capitalising or make a random choice,
e.g. use both inputFileName and outputfilename. You can of course
drive your team members insane by inventing your own insanely complex
naming conventions then berate others for not following them. The
ultimate technique is to create as many variable names as possible
that differ subtlely from each other only in case.
-
Lower Case l Looks A Lot Like Digit 1
Use lower case l to indicate long
constants. e.g. 10l is more likely to be mistaken for 101 that 10L is.
-
Reuse of Global Names as Private
Declare a global array in module A, and a
private one of the same name in the header file for module B, so that
it appears that it's the global array you are using in module B, but
it isn't. Make no reference in the comments to this duplication.
-
Recycling Revisited
Use scoping as confusingly as possible by
recycling variable names in contradictory ways. For example, suppose
you have global variables A and B, and functions foo and bar. If you
know that variable A will be regularly passed to foo and B to bar,
make sure to define the functions as function foo(B) and function
bar(A) so that inside the functions A will always be referred to as B
and vice versa. With more functions and globals, you can create vast
confusing webs of mutually contradictory uses of the same names.
-
Recycle Your Variables
Wherever scope rules permit, reuse
existing unrelated variable names. Similarly, use the same temporary
variable for two unrelated purposes (purporting to save stack slots).
For a fiendish variant, morph the variable, for example, assign a
value to a variable at the top of a very long method, and then
somewhere in the middle, change the meaning of the variable in a
subtle way, such as converting it from a 0-based coordinate to a
1-based coordinate. Be certain not to document this change in meaning.
-
Cd wrttn wtht vwls s mch trsr
When using abbreviations inside variable
or method names, break the boredom with several variants for the same
word, and even spell it out longhand once in while. This helps defeat
those lazy bums who use text search to understand only some aspect of
your program. Consider variant spellings as a variant on the ploy,
e.g. mixing International colour, with American color
and dude-speak kulerz. If you spell out names in full, there is
only one possible way to spell each name. These are too easy for the
maintenance programmer to remember. Because there are so many
different ways to abbreviate a word, with abbreviations, you can have
several different variables that all have the same apparent purpose.
As an added bonus, the maintenance programmer might not even notice
they are separate variables.
-
Misleading names
Make sure that every method does a little
bit more (or less) than its name suggests. As a simple example, a
method named isValid(x) should as a side
effect convert x to binary and store the result in a database.
-
m_
a naming convention from the world of C++
is the use of "m_" in front of members. This is supposed to
help you tell them apart from methods, so long as you forget that
"method" also starts with the letter "m".
-
o_apple obj_apple
Use an "o" or "obj"
prefix for each instance of the class to show that you're thinking of
the big, polymorphic picture.
-
Hungarian Notation
Hungarian Notation is the tactical nuclear
weapon of source code obfuscation techniques; use it! Due to the sheer
volume of source code contaminated by this idiom nothing can kill a
maintenance engineer faster than a well planned Hungarian Notation
attack. The following tips will help you corrupt the original intent
of Hungarian Notation:
Insist on using "c" for const in
C++ and other languages that directly enforce the const-ness of a
variable.
Seek out and use Hungarian warts that
have meaning in languages other than your current language. For example
insist on the PowerBuilder "l_" and "a_ " {local and
argument} scoping prefixes and always use the VB-esque style of having a
Hungarian wart for every control type when coding to C++. Try to stay
ignorant of the fact that megs of plainly visible MFC source code does
not use Hungarian warts for control types.
Always violate the Hungarian principle
that the most commonly used variables should carry the least extra
information around with them. Achieve this end through the techniques
outlined above and by insisting that each class type have a custom wart
prefix. Never allow anyone to remind you that no wart tells you that
something is a class. The importance of this rule cannot be overstated:
if you fail to adhere to its principles the source code may become
flooded with shorter variable names that have a higher vowel/consonant
ratio. In the worst case scenario this can lead to a full collapse of
obfuscation and the spontaneous reappearance of English Notation in
code!
Flagrantly violate the Hungarian-esque
concept that function parameters and other high visibility symbols must
be given meaningful names, but that Hungarian type warts all by
themselves make excellent temporary variable names.
Insist on carrying outright orthogonal
information in your Hungarian warts. Consider this real world example:
"a_crszkvc30LastNameCol". It took a team of maintenance
engineers nearly 3 days to figure out that this whopper variable name
described a const, reference, function argument that was holding
information from a database column of type Varchar[30] named "LastName"
which was part of the table's primary key. When properly combined with
the principle that "all variables should be public" this
technique has the power to render thousands of lines of source code
obsolete instantly!
Use to your advantage the principle that
the human brain can only hold 7 pieces of information concurrently. For
example code written to the above standard has the following properties:
A single assignment statement carries 14
pieces of type and name information.
A single function call that passes three
parameters and assigns a result carries 29 pieces of type and name
information.
Seek to improve this excellent, but far
too concise, standard. Impress management and coworkers by recommending
a 5 letter day of the week prefix to help isolate code written on 'Monam'
and 'FriPM'.
It is easy to overwhelm the short term
memory with even a moderately complex nesting structure, especially when
the maintenance programmer can't see the start and end of each block on
screen simultaneously.
-
Hungarian Notation Revisited
One follow on trick in the Hungarian
notation is "change the type of a variable but leave the variable
name unchanged". This is almost invariably done in windows apps
with the migration from Win16 :- WndProc(HWND hW, WORD wMsg, WORD
wParam, LONG lParam) to Win32 WndProc(HWND hW, UINT wMsg, WPARAM
wParam, LPARAM lParam) where the w values hint that they are words,
but they really refer to longs. The real value of this approach comes
clear with the Win64 migration, when the parameters will be 64 bits
wide, but the old "w" and "l" prefixes will remain
forever.
-
Obscure film references
Use constant names like
LancelotsFavouriteColour instead of blue and assign it hex value of
$0204FB. The color looks identical to pure blue on the screen, and a
maintenance programmer would have to work out 0204FB (or use some
graphic tool) to know what it looks like. Only someone intimately
familiar with Monty Python and the Holy Grail would know that
Lancelot's favorite color was blue. If a maintenance programmer can't
quote entire Monty Python movies from memory, he or she has no
business being a programmer.
Camouflage
The longer it takes for a bug to surface,
the harder it is to find.
Roedy
Much of the skill in writing unmaintainable
code is the art of camouflage, hiding things, or making things appear to
be what they are not. Many depend on the fact the compiler is more capable
at making fine distinctions than either the human eye or the text editor.
Here are some of the best camouflaging techniques.
-
Code That Masquerades As Comments and
Vice Versa
Include sections of code that is commented
out but at first glance does not appear to be.
for(j=0;
j<array_len; j+
=8)
{
total += array[j+0
];
total += array[j+1
];
total += array[j+2
];
total += array[j+6
];
total += array[j+7
];
}
Without the colour coding would you
notice that three lines of code are commented out?
- Arbitrary Names That Masquerade as
Keywords
When documenting, and you need an
arbitrary name to represent a filename use "file ". Never
use an obviously arbitrary name like "Charlie.dat" or "Frodo.txt".
In general, in your examples, use arbitrary names that sound as much
like reserved keywords as possible. For example, good names for
parameters or variables would be: "bank", "blank",
"class", "const ", "constant",
"input", "key", "keyword",
"kind", "output", "parameter" "parm",
"system", "type", "value", "var"
and "variable ". If you use actual reserved words for your
arbitrary names, which would be rejected by your command processor or
compiler, so much the better. If you do this well, the users will be
hopelessly confused between reserved keywords and arbitrary names in
your example, but you can look innocent, claiming you did it to help
them associate the appropriate purpose with each variable.
- Code Names Must Not Match Screen Names
Choose your variable names to have
absolutely no relation to the labels used when such variables are
displayed on the screen. E.g. on the screen label the field
"Postal Code" but in the code call the associated variable
"zip".
- How to Hide Forbidden Globals
Since global variables are
"evil", define a structure to hold all the things you'd put
in globals. Call it something clever like EverythingYoullEverNeed.
Make all functions take a pointer to this structure (call it handle to
confuse things more). This gives the impression that you're not using
global variables, you're accessing everything through a
"handle". Then declare one statically so that all the code
is using the same copy anyway.
- Long Similar Variable Names
Use very long variable names or class
names that differ from each other by only one character, or only in
upper/lower case. An ideal variable name pair is swimmer and swimner.
Exploit the failure of most fonts to clearly discriminate between
ilI1| or oO08 with identifier pairs like parselnt and parseInt or D0Calc
and DOCalc. l is an exceptionally fine choice for a variable name
since it will, to the casual glance, masquerade as the constant 1. In
many fonts rn looks like an m. So how about a variable swirnrner.
Create variable names that differ from each other only in case e.g.
HashTable and Hashtable.
- Similar-Sounding Similar-Looking
Variable Names
Although we have one variable named
xy_z, there's certainly no reason not to have many other variables
with similar names, such as xy_Z, xy__z, _xy_z, _xyz, XY_Z, xY_z, and
Xy_z.
Variables that resemble others except
for capitalization and underlines have the advantage of confounding
those who like remembering names by sound or letter-spelling, rather
than by exact representations.
Documentation
Any fool can tell the truth, but it requires
a man of some sense to know how to lie well.
Samuel Butler (1835 - 1902)
Incorrect documentation is often worse than
no documentation.
Bertrand Meyer
Since the computer ignores comments and
documentation, you can lie outrageously and do everything in your power to
befuddle the poor maintenance programmer.
- Lie in the comments
You don't have to actively lie, just
fail to keep comments as up to date with the code.
- Document the obvious
Pepper the code with comments like however, never document wooly stuff like the
overall purpose of the package or method.
- Avoid Documenting the
"Obvious"
If, for example, you were writing an
airline reservation system, make sure there are at least 25 places in
the code that need to be modified if you were to add another airline.
Never document where they are. People who come after you have no
business modifying your code without thoroughly understanding every
line of it.
- On the Proper Use Of Documentation
Templates
Consider function documentation
prototypes used to allow automated documentation of the code. These
prototypes should be copied from one function (or method or class) to
another, but never fill in the fields. If for some reason you are
forced to fill in the fields make sure that all parameters are named
the same for all functions, and all cautions are the same but of
course not related to the current function at all.
- On the Proper Use of Design Documents
When implementing a very complicated
algorithm, use the classic software engineering principles of doing a
sound design before beginning coding. Write an extremely detailed
design document that describes each step in a very complicated
algorithm. The more detailed this document is, the better.
In fact, the design doc should break
the algorithm down into a hierarchy of structured steps, described in
a hierarchy of auto-numbered individual paragraphs in the document.
Use headings at least 5 deep. Make sure that when you are done, you
have broken the structure down so completely that there are over 500
such auto-numbered paragraphs. For example, one paragraph might be:
(this is a real example)
1.2.4.6.3.13 - Display all impacts for
activity where selected mitigations can apply (short pseudocode
omitted).
then... (and this is the kicker) when
you write the code, for each of these paragraphs you write a
corresponding global function named:
Do not document these functions. After
all, that's what the design document is for!
Since the design doc is auto-numbered,
it will be extremely difficult to keep it up to date with changes in
the code (because the function names, of course, are static, not
auto-numbered.) This isn't a problem for you because you will not try
to keep the document up to date. In fact, do everything you can to
destroy all traces of the document.
Those who come after you should only be
able to find one or two contradictory, early drafts of the design
document hidden on some dusty shelving in the back room near the dead
286 computers.
- Gotchas
Never document gotchas in the code. If
you suspect there may be a bug in a class, keep it to yourself. If you
have ideas about how the code should be reorganised or rewritten, for
heaven's sake, do not write them down. Remember the words of Thumper
in the movie Bambi "If you can't say anything nice, don't say
anything at all". What if the programmer who wrote that code saw
your comments? What if the owner of the company saw them? What if a
customer did? You could get yourself fired. An anonymous comment that
says "This needs to be fixed!" can do wonders, especially if
it's not clear what the comment refers to. Keep it vague, and nobody
will feel personally criticized.
- Documenting Variables
Neverput a comment on a variable. Facts
about how the variable is used, its bounds, its legal values, its
implied/displayed number of decimal points, its units of measure, its
display format, its data entry rules (e.g. total fill, must enter),
when its value can be trusted etc. should be gleaned from the
procedural code. If your boss forces you to write comments, lard
method bodies with them, but never comment a variable, not even a
temporary!
- Disparage In the Comments
Discourage any attempt to use external
maintenance contractors by peppering your code with insulting
references to other leading software companies, especial anyone who
might be contracted to do the work. e.g.:
| | | | |