Code That Cannot Be Supported (Part 2)
- Transfer
The continuation of this topic is the second and third chapters of the essay "Unmaintainable Code". Somewhat echoes the first, but the methods described are not so obvious (and some differ in truly devilish ingenuity and no less malice). Oh yeah, the author quietly switched to C / C ++ from the Java prologue.
The art of camouflage — hiding things or disguising them as others — makes up a significant share of the art of writing unsupported code. Most of the techniques in this section are based on the fact that the compiler perceives and processes the code differently than a text editor or a person. The following are selected camouflage tricks:
More sins are committed in the name of efficiency than for any other reason, including stupidity.
The safest way to hide your code obfuscation actions is to pretend that your main goal is to increase the efficiency of the program. For the sake of a lofty goal, all your tricks will be forgiven when they come out.
Comment out the code snippets so that at first glance they seem relevant.
Camouflage
The art of camouflage — hiding things or disguising them as others — makes up a significant share of the art of writing unsupported code. Most of the techniques in this section are based on the fact that the compiler perceives and processes the code differently than a text editor or a person. The following are selected camouflage tricks:
1. Write an “effective” code
More sins are committed in the name of efficiency than for any other reason, including stupidity.
The safest way to hide your code obfuscation actions is to pretend that your main goal is to increase the efficiency of the program. For the sake of a lofty goal, all your tricks will be forgiven when they come out.
2. Comments pretending to be code
Comment out the code snippets so that at first glance they seem relevant.
for ( j=0; j
Without code highlighting, would you notice that the middle line is commented out?
(I can’t imagine what development environment this method is designed for - even Far has a code highlighting plugin. Apparently, it is supposed to combine it with incorrect file extensions for which / * * / is not recognized as a comment - approx. Per.)3. Namespaces
In C, struct / union and typedef struct / union belong to different namespaces; therefore, the same name can (and should) be used in both. It is advisable to make them almost compatible.typedef struct { char* pTr; int lEn; } snafu;
struct snafu { unsigned cNt; char* pTr; int lEn; } A;4. Hidden macros
Hide macro definitions in the thick of meaningless comments; then the maintainer will skip all the comments at once and will not detect the macro. Use such a macro to replace some legal language expression with something very strange:#define a=b a=0-b
(I don’t know what the author meant; this macro in C / C ++ code generates an error if the a variable appears in the code and is safely ignored, if there isn’t such a variable. C / C ++ wasn’t fools either :-) - approx.5. Pretend to be busy
Use macros to create fictional “functions” that do absolutely nothing.#define fastcopy(x,y,z) /*xyz*/
:
fastcopy(array1, array2, size);6. Override functions
Create “functions” duplicates of standard ones (macros with parameters) that do something completely unexpected. (Special maniacs can thus redefine a dozen functions that a person who makes corrections may want to use. For example, in conjunction with the #define abs (a) (-2 * a) macro, you can write your bulky module calculation function and use it, demonically laughing at the partner’s attempts to replace it with a normal module - approx. per.)7. Transfers in variable names
Remember that the C preprocessor treats the \ character at the end of a line as a line break and glues it with the next? Excellent! Be creative in breaking long variable names into strings to make it harder to find all calls to these variables with a simple search.8. Choose reserved words for arbitrary examples.
If in the documentation you need an arbitrary example for a file name, use “file”, and not an obviously arbitrary name like “charlie.dat”. In general, choose arbitrary examples that are as close as possible to the reserved words, or these words themselves (if the compiler refuses to accept it - all the better, this is just documentation). When used correctly, readers hopelessly get confused about which parts of the example can be replaced with other words and which cannot. If you get caught, you can innocently prove that you were just trying to help the reader understand the purpose of each variable.9. Names in the code vs names in the interface
Make sure that the variable names have nothing to do with the names in the documentation and interface. For example, the Postal Code field can be stored in a zip variable.10. Say no to rename
To synchronize two pieces of code, do not rename the variables; better redefine them with #define. Create more synonyms for the same variable in different files to prevent text search.11. Bypass the ban on global variables
To do this, define a static global structure containing all the variables you need and name it EverythingYoullEverNeed. Pass this structure to the function (or rather, a pointer to it called handle) to create the illusion of the absence of global variables.12. Overload operators
The remarkable property of operator overloading allows you to create your own operators +, -, /, *, which are completely not related to arithmetic. After all, if Straustrup uses shift operators (>> and <<) for streaming I / O, what are you worse at?13. #define & #ifdef
As can be seen from the preceding paragraphs, the preprocessor directive deserves a separate ode of grateful disguises; hardly anything else allows you to make so much mess in the code. #Define expressions are perfectly masked as functions and variables. The creative use of #ifdef allows you to use different versions of the function, depending on in which order and in what quantities the header files are included. Try to figure out what the following code does:#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 // DONE14. Compiler Directives
Another thing that is convenient for changing the behavior of the code is that it is why they were created.15. Distracting maneuvers
Spice up your code with unused variables and unrecognizable methods - just don't delete the code that is no longer needed. The justification for this may be that this code may still come in handy. Bonus for names similar to the code used - the maintainer will definitely mix them up.Documentation
Any fool can tell the truth, but only an intelligent person can lie well.
Compilers ignore comments and do not see documentation, so you can do anything in them to confuse the poor companion.1. Lie
Well, you can not lie directly, just forget to update the comments when you update the code.2. Document the obvious
Season the code with comments like / * increment i * /, while avoiding commenting on dubious places - the purpose of the method or the meaning of the actions performed.3. “What”, not “why”
Comment only on the actions of your program, but not its purpose and purpose.4. Avoid documenting the “obvious”
If you assume that your code will change in a certain way, in no case do not mention what you need to do for this. The people who come after you have nothing to climb to edit your code without understanding it in detail.5. On the issue of documentation templates
Think about the proper use of prototypes for documenting functions: carefully copy templates from function to function, but never fill in template fields. If you are forced to fill them out, make sure that the parameters of all functions are called the same, and their decryption does not have the slightest relation to the function itself.6. On the issue of project documentation
Let's say you need to implement some kind of complex algorithm. Use the classic principle of software development: first, create a project for this algorithm. Write a very detailed (the more detailed, the better) document that would describe it step by step, in the form of a multi-level (minimum 5 levels) hierarchy of nested items with automatic numbering. Make sure you end up with at least 500 points. So, an example of one point might be1.2.4.6.3.13 - Display all impacts for activity where selected mitigations can apply (short pseudocode omitted).
After that (yes, the focus is right here) write the code, and for each such item, create a separate method Act1_2_4_6_3_13 (). No, do not write comments in the method code - after all, there is project documentation for this! Since the numbering in the original document changes automatically when editing the document, it would be difficult to keep the static name of the methods up to date. However, for you this will not amount to any difficulty - after all, you do not strive to keep the document up to date. In fact, you should make every effort to destroy not only the document itself, but also all sorts of traces of its existence. However, you can leave one or two drafts carefully hidden in the pantry under the old computers.7. Units
Never document the units of measurement of variables and parameters - this is not so important in the calculations, but it is extremely important in engineering work. Likewise, never comment on the constants used in the transforms, or the way you get the values. A primitive but effective way is to decorate the code with comments that indicate incorrect units of measure. If you are in a particularly gloomy mood, invent your own unit: name it after yourself or someone special and never define it explicitly. If you find fault with this, explain that you use it to restrict yourself to integer arithmetic and not to mess around with fractional numbers.8. Bugs
Never document bugs in someone else's code. If you suspect a bug has been hidden somewhere, leave this to yourself. If you have ideas for reorganizing the code, for God's sake, do not write them down. Think about what will happen if your comment is seen by the author of the code, management, client? Yes, they can fire you! However, the anonymous comment “It needs to be fixed” works wonders, especially if it is not clear what it refers to.9. Description of variables
Never - hear, never! - Do not document variables when declaring them. Facts about the use of a variable, its boundaries and acceptable values, accuracy, units of measurement, output format, input rules, etc. should be obvious from the code. If you are required to write comments, flood them with the body of the procedures, but in no case declaration of variables.10. Humiliating comments
Cut down any attempts to attract employees of other companies, generously decorating your code with comments that offend their honor and dignity: If possible, place such comments in important pieces of code, mixing them with semantic comments so that attempts to neutralize them are as complicated as possible.* Этот прием слишком сложен для олухов из Software Services Inc.; они, пожалуй,
* потратили бы в 50 раз больше памяти и времени с использованием тупых методов из11. Comment as if it were COBOL on punch cards
Discard the capabilities of development environments, do not believe the rumors that the definitions of functions and variables are always one click away from their use, and expect that the code written in Visual Studio 6.0 will be accompanied by edlin or vi. Draconic commentary requirements are a great way to bury code.12. Monty Python style comments
The comment on the makeSnafucated method must consist of exactly one line: / * make snafucated * /. Never and never define what snafucated is - any fool knows that.13. Outdated code
Never rely on version control systems to restore outdated code - just don't delete it. Never comment on whether the new code should replace the old one or supplement it, why the old code didn’t work for someone, whether it worked at all, etc. Comment on the old code as / * * / instead of // on each line - this way it is easier to take for the current one and spend effort on its support.