Pull to refresh

Код, который невозможно поддерживать (часть 2)

Reading time7 min
Views3.4K
Original author: Roedy Green
Продолжение этого топика — вторая и третья главы эссе «Unmaintainable Code». Несколько перекликается с первой, но описанные методы уже не так очевидны (а некоторые отличаются поистине дьявольской изобретательностью и не меньшей злокозненностью). Ах да, с заявленного в прологе языка Java автор незаметно переключился на C/C++.

Камуфляж



Искусство камуфляжа — сокрытия вещей или маскировки их под другие — составляет значительную долю искусства написания неподдерживаемого кода. Большинство приемов этого раздела основаны на том, что компилятор воспринимает и обрабатывает код иначе, чем текстовый редактор или человек. Ниже приведены избранные приемы камуфляжа:

1. Пишите «эффективный» код

Во имя эффективности совершается больше грехов, чем из-за любой другой причины, включая глупость.

Самый безопасный способ скрыть свои действия по обфускации кода — сделать вид, что ваша главная цель — повышение эффективности программы. Во имя высокой цели вам простят все ваши фокусы, когда они выплывут наружу.

2. Комментарии, притворяющиеся кодом

Комментируйте фрагменты кода так, чтобы на первый взгляд они казались актуальными.
for ( j=0; j<array_len; j+ =5 )
   {
   total += array[j+0];
   total += array[j+1]; /* Тело цикла развернуто для ускорения работы
   total += array[j+2]; */
   total += array[j+3];
   total += array[j+4];
   }

Без подсветки кода вы бы заметили, что средняя строка закомментирована?
(Не представляю, на какую среду разработки рассчитан этот метод — даже в Far есть плагин подсветки кода. Видимо, предполагается сочетать его с неправильными расширениями файла, для которых /* */ не распознается как комментарий — прим.пер.)

3. Пространства имен

В C struct/union и typedef struct/union принадлежат к разным пространствам имен; следовательно, одно и то же имя можно (и нужно) использовать в обоих. Желательно делать их почти совместимыми.

typedef struct { char* pTr; int lEn; } snafu;

struct snafu { unsigned cNt; char* pTr; int lEn; } A;


4. Спрятанные макросы

Прячьте определения макросов в толще бессмысленных комментариев; тогда сопровождающий проскочит все комментарии разом и не обнаружит макрос. Используйте такой макрос для замены какого-нибудь легального выражения языка на что-нибудь очень странное:

#define a=b a=0-b

(Не знаю, что имел в виду автор; этот макрос в коде на C/C++ порождает ошибку, если в коде появляется переменная a, и благополучно игнорируется, если такой переменной нет. C/C++ тоже все-таки не дураки писали :-) — прим.пер.)

5. Притворитесь занятыми

Используйте макросы для создания вымышленных «функций», которые ровным счетом ничего не делают.

#define fastcopy(x,y,z) /*xyz*/
:
fastcopy(array1, array2, size);


6. Переопределяйте функции

Создавайте «функции»-дубли стандартных (макросы с параметрами), которые делают что-то совсем неожиданное. (Особые маньяки могут таким образом переопределить десяток функций, которые может захотеть использовать человек, вносящий исправления. Так, например, в пару к макросу #define abs(a) (-2*a) можно написать свою громоздкую функцию вычисления модуля и использовать ее, демонически смеясь над попытками напарника заменить ее на нормальный модуль — прим.пер.)

7. Переносы в именах переменных

Помните, что препроцессор C расценивает символ \ в конце строки как перенос строки и склеивает ее со следующей? Отлично! Творчески подойдите к разбиению длинных имен переменных на строки, чтобы максимально затруднить нахождение всех обращений к этим переменным простым поиском.

8. Выбирайте зарезервированные слова для произвольных примеров

Если в документации вам нужен произвольный пример для имени файла, используйте «file», а не очевидно-произвольное имя типа «charlie.dat». В целом выбирайте произвольные примеры, которые максимально близки к зарезервированным словам, или сами эти слова (если компилятор откажется это принимать — тем лучше, это всего лишь документация). При правильном использовании читатели безнадежно запутаются в том, какие части примера можно заменять на другие слова, а какие нельзя. Если вас застукают, можно с невинным видом доказывать, что вы всего лишь пытались помочь читателю понять назначение каждой переменной.

9. Имена в коде vs имен в интерфейсе

Проследите, чтобы имена переменных не имели ничего общего с названиями в документации и интерфейсе. Например, поле «Postal Code» можно хранить в переменной zip.

10. Скажите переименованиям «нет»

Для синхронизации двух фрагментов кода не переименовывайте переменные; лучше переопределите их при помощи #define. Создавайте побольше синонимов для одной и той же переменной в разных файлах, чтобы воспрепятствовать текстовому поиску.

11. Обходите запрет на глобальные переменные

Для этого определите статическую глобальную структуру, содержащую все переменные, которые вам понадобятся, и назовите ее EverythingYoullEverNeed. В функции передавайте эту структуру (а лучше указатель на нее под названием handle), для создания иллюзии отсутствия глобальных переменных.

12. Перегружайте операторы

Замечательное свойство перегрузки операторов позволяет создавать свои операторы +, -, /, *, совершенно не относящиеся к арифметике. В конце концов, если Страуструп использует операторы сдвига (>> и <<) для потокового ввода-вывода, чем вы хуже?

13. #define & #ifdef

Как видно из предыдущих пунктов, директивы препроцессора заслуживает отдельной оды благодарных маскировщиков; едва ли что-то другое позволяет внести столько беспорядка в код. Выражения #define замечательно маскируются под функции и переменные. Творческое применение #ifdef позволяет использовать разные версии функции в зависимости от того, в каком порядке и в каких количествах включены заголовочные файлы. Попробуйте разобраться, что делает следующий код:

#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


14. Директивы компилятора

Еще одна вещь, удобная для изменения поведения кода — ведь это то, зачем они создавались.

15. Отвлекающие маневры

Приправьте ваш код неиспользуемыми переменными и невызываемыми методами — просто не удаляйте код, ставший ненужным. Оправданием для этого может быть то, что этот код может еще пригодиться. Бонус за имена, похожие на используемый код — сопровождающий обязательно их перепутает.

Документация



Любой дурак может сказать правду, но только умный человек может хорошо солгать

Компиляторы игнорируют комментарии и не видят документации, поэтому в них можно делать что угодно, чтобы сбить с толку беднягу сопровождающего.

1. Лгите

Ну ладно, можете не лгать прямо, просто забывайте обновить комментарии, когда обновляете код.

2. Документируйте очевидное

Приправьте код комментариями типа /* increment i */, в то же время избегайте комментирования сомнительных мест — назначения метода или смысла выполняемых действий.

3. «Что», а не «зачем»

Комментируйте только действия вашей программы, но не ее цель и назначение.

4. Избегайте документирования «очевидного»

Если вы предполагаете, что ваш код будет меняться определенным образом, ни в коем случае не упоминайте, что нужно для этого сделать. Людям, пришедшим после вас, нечего лезть править ваш код, не понимая его во всех деталях.

5. К вопросу о шаблонах документации

Задумайтесь о правильном использовании прототипов документирования функций: тщательно копируйте шаблоны из функции в функцию, но никогда не заполняйте поля шаблона. Если вас вынуждают их заполнять, убедитесь, что параметры всех функций называются одинаково, а их расшифровки не имеют ни малейшего отношения к собственно функции.

6. К вопросу о проектной документации

Допустим, вам нужно реализовать некий сложный алгоритм. Используйте классический принцип разработки ПО: первым делом создайте проект этого алгоритма. Напишите очень детальный (чем детальнее, тем лучше) документ, который бы описывал его пошагово, в виде многоуровневой (минимум 5 уровней) иерархии вложенных пунктов с автоматической нумерацией. Убедитесь, что в итоге получилось не менее 500 пунктов. Так, примером одного пункта может быть

1.2.4.6.3.13 - Display all impacts for activity where selected mitigations can apply (short pseudocode omitted).

После этого (да, фокус именно здесь) пишите код, и для каждого такого пункта создайте отдельный метод Act1_2_4_6_3_13(). Нет, не пишите комментарии в коде методов — ведь для этого существует проектная документация! Поскольку нумерация в исходном документе изменяется автоматически при редактировании документа, то было бы сложно поддерживать статичное наименование методов в актуальном состоянии. Впрочем, для вас это не составит ровным счетом никакого труда — ведь вы и не стремитесь поддерживать документ в актуальном состоянии. Фактически, вам следует приложить все усилия для уничтожения не только самого документа, но и всяческих следов его существования. Впрочем, можете оставить один-два черновика тщательно спрятанными в кладовке под старыми компьютерами.

7. Единицы измерения

Никогда не документируйте единицы измерения переменных и параметров — это не так важно при подсчетах, но крайне важно в инженерной работе. Аналогично, никогда не комментируйте константы, использующиеся при преобразованиях, или способ получения величин. Примитивный, но действенный способ — украсить код комментариями, в которых указаны неправильные единицы измерения. Если у вас особенно мрачное настроение, изобретите свою собственную единицу: назовите ее в честь себя или кого-то особенного и никогда не определяйте ее в явном виде. Если к этому придерутся, объясните, что используете ее, чтобы ограничиться целочисленной арифметикой и не возиться с дробными числами.

8. Баги

Никогда не документируйте баги в чужом коде. Если вы подозреваете, что где-то там скрылся баг, оставьте это при себе. Если у вас есть идеи по реорганизации кода, бога ради, не записывайте их. Подумайте, что случится, если ваш комментарий увидит автор кода, руководство, клиент? Да вас ведь могут уволить! Впрочем, анонимный комментарий «Это нужно исправить» творит чудеса, особенно если неясно, к чему он относится.

9. Описание переменных

Никогда — слышите, никогда! — не документируйте переменные при их объявлении. Факты об использовании переменной, ее границах и допустимых значениях, точности, единицах измерения, формате вывода, правилах ввода и т.д. должны быть очевидны из кода. Если вас обязали писать комментарии, наводните ими тело процедур, но ни в коем случае не объявления переменных.

10. Унизительные комментарии

Зарубите на корню любые попытки привлечь сотрудников других фирм, щедро украшая ваш код комментариями, оскорбляющими их честь и достоинство:

* Этот прием слишком сложен для олухов из Software Services Inc.; они, пожалуй,
* потратили бы в 50 раз больше памяти и времени с использованием тупых методов из <math.h>


По возможности размещайте такие комментарии в важных кусках кода, перемешивая их со смысловыми комментариями, чтобы попытки обезвредить их были максимально усложнены.

11. Комментируйте так, будто это COBOL на перфокартах

Отказывайтесь от возможностей сред разработки, не верьте слухам о том, что определения функций и переменных всегда в одном клике от от их использования и рассчитывайте на то, что код, написанный в Visual Studio 6.0, будет сопровождаться средствами edlin или vi. Драконовские требования к комментариям — отличный способ похоронить код.

12. Комментарии в стиле Monty Python

Комментарий к методу makeSnafucated должен состоять из ровно одной строки: /* make snafucated */. Никогда и нигде не определяйте, что такое snafucated — это же любой дурак знает.

13. Устаревший код

Никогда не полагайтесь на системы контроля версий для восстановления устаревшего кода — просто не удаляйте его. Никогда не комментируйте, должен ли новый код заменить старый или дополнить его, почему старый код кого-то не устроил, работал ли он вообще и т.д. Комментируйте старый код как /* */ вместо // на каждой строке — так его легче принять за действующий и тратить силы на его поддержку.
Tags:
Hubs:
+42
Comments57

Articles