How To Write Unmaintainable Code
Camouflage
©1997-2003 Canadian Mind Products.
You are here : home : Java
Glossary : unmaintainable code : 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?
-
namespaces
: Struct/union and typedef struct/union are different name spaces in C (not in
C++). Use the same name in both name spaces for structures or unions. Make them,
if possible, nearly compatible.
-
Hide Macro Definitions
: Hide macro definitions in amongst rubbish comments. The programmer will get
bored and not finish reading the comments thus never discover the macro. Ensure
that the macro replaces what looks like a perfectly legitimate assignment with
some bizarre operation, a simple example:
-
Look Busy
: use define statements to make made up functions that simply comment out their
arguments, e.g.:
#define fastcopy(x,y,z)
...
fastcopy(array1, array2, size); /* does nothing */
-
Use Continuation to hide variables
: Instead of using
break up "xy_z" onto two lines:
#define local_var xy\
_z // local_var OK
That way a global search for xy_z will come up with nothing for that file. To
the C preprocessor, the "\" at the end of the line means glue this
line to the next one.
-
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".
-
Don't Change Names
: Instead of globally renaming to bring two sections of code into sync, use
multiple TYPEDEFs of the same symbol.
-
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.
-
Hide Instances With Synonyms
: Maintenance programmers, in order to see if they'll be any cascading effects
to a change they make, do a global search for the variables named. This can be
defeated by this simple expedient of having synonyms, such as
#define xxx global_var // in file std.h
#define xy_z xxx // in file ..\other\substd.h
#define local_var xy_z // in file ..\codestd\inst.h
These defs should be scattered through different include-files. They are
especially effective if the include-files are located in different directories.
The other technique is to reuse a name in every scope. The compiler can tell
them apart, but a simple minded text searcher cannot. Unfortunately SCIDs
in the coming decade will make this simple technique impossible. since the
editor understands the scope rules just as well as the compiler.
-
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.
-
Overload and Bewilder
: In C++, overload library functions by using #define. That way it looks like
you are using a familiar library function where in actuality you are using
something totally different.
-
Choosing The Best Overload Operator
: In C++, overload +,-,*,/ to do things totally unrelated to addition,
subtraction etc. After all, if the Stroustroup can use the shift operator to do
I/O, why should you not be equally creative? If you overload +, make sure you do
it in a way that i = i + 5; has a totally different
meaning from i += 5; Here is an example of elevating
overloading operator obfuscation to a high art. Overload the '!' operator for a
class, but have the overload have nothing to do with inverting or negating. Make
it return an integer. Then, in order to get a logical value for it, you must use '!
!'. However, this inverts the logic, so [drum roll] you must use '! ! !'. Don't
confuse the ! operator, which returns a boolean 0 or 1, with the ~ bitwise
logical negation operator.
-
Overload new
: Overload the "new" operator - much more dangerous than overloading
the +-/*. This can cause total havoc if overloaded to do something different
from it's original function (but vital to the object's function so it's very
difficult to change). This should ensure users trying to create a dynamic
instance get really stumped. You can combine this with the case sensitivity
trick: also have a member function, and variable called "New".
-
#define
: #define in C++ deserves an entire essay on its own to explore its rich
possibilities for obfuscation. Use lower case #define variables so they
masquerade as ordinary variables. Never use parameters to your preprocessor
functions. Do everything with global #defines. One of the most imaginative uses
of the preprocessor I have heard of was requiring five passes through CPP before
the code was ready to compile. Through clever use of defines and ifdefs, a
master of obfuscation can make header files declare different things depending
on how many times they are included. This becomes especially interesting when
one header is included in another header. Here is a particularly devious example:
#ifndef DONE
#ifdef TWICE
// put stuff here to declare 3rd time around
void g(char* str);
#define DONE
#else // TWICE
#ifdef ONCE
// put stuff here to declare 2nd time around
void g(void* str);
#define TWICE
#else // ONCE
// put stuff here to declare 1st time around
void g(std::string str);
#define ONCE
#endif // ONCE
#endif // TWICE
#endif // DONE
This one gets fun when passing g() a char*, because a different version of g()
will be called depending on how many times the header was included.
-
Compiler Directives
: Compiler directives were designed with the express purpose of making the same
code behave completely differently. Turn the boolean short-circuiting directive
on and off repeatedly and vigourously, as well as the long strings directive.
-
Red Herrings
Pepper your code with variables that are never used and methods that are never
called. This is most easily done by failing to remove code that is no longer
used. You can save the code on grounds someday it may need to be revived. You
get bonus points if these drone variables and methods have names similar to
actual worker ones. The maintenance programmer will inevitably confuse the two.
Changes made to drone code will happily compile, but have no effect.
You can get an updated copy of this page from http://mindprod.com/unmaincamouflage.html