Зомби-код. Код, живущий своей жизнью

Предлагаю читателям «Хабрахабра» перевод статьи «Zombie Code When Maintainability Goes Out Window».

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

Да, скажешь ты? Почему?

Ты не задумывался, что твой код устареет через год? Разве тебя не волнует, что какой-то будущий разработчик испортит нетронутую красоту твоего тщательно обработанного решения? Разве ты не восхищался случайно встреченным, незаменимым, но совершенно устаревшим комком грязи? Хотел бы ты скорее написать зомби-код, который никто кроме тебя не сможет понять, код, который будут расценивать со страхом и трепетом, код, который будет вечно жить в компании, потому что никто не осмелится его изменить?



Это не легко, но ты можешь это сделать. С небольшим усилием, ты можешь развить свои навыки и дисциплину, которые дадут тебе возможность не только немедленно написать код, но и превзойдут уровень «так себе» и ты напишешь окончательный, оставшийся в живых – зомби-код, который продолжит «ходить» по земле и после того, как ты уйдешь.

А теперь как же это сделать:

Ничего не проверяй


Тесты – пустая трата времени. Они дают иллюзию надежности и могут показать, как работает твой код кому-то еще. Если ты должен провести тесты, сделай это в последнюю очередь и заяви, что они верны. Разумеется, не проводи тестирование вначале и не используй тесты для помощи кому-то — такая разработка простой путь к пониманию и изменению.

Никакого рефакторинга


Во-первых, если код работает, не исправляй его. Во-вторых, ни у кого нет времени разрабатывать изящность и простоту. Разработка через тестирование (TDD), при необходимости, допускается, но только, если ты не можешь без этого обойтись. Да и в конце концов «технический долг» заключается в продаже средств для измерения характеристик ПО.

Пиши код, который невозможно понять


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

— Используй обобщенные названия, к примеру, «Buffer», «Temp» и «Х», как можно чаще;
— Используй аббревиатуры и сокращения для названия интерфейсов;
— Используй родовую структуру не только для экономии времени и усилий, но и, чтобы спутать информацию о типах данных;
— Используй типизированные структуры в нетипизированных коллекциях;
— Записывай все подряд в массивы;
— Копируй и вставляй полусвязную структуру по всему коду, потом изменяй ее, чтобы она соответствовала различным данным и ситуациям, но не переименовывай значения и не изменяй комментарии;
— Иногда (но не слишком часто) давай переменным имена, подразумевающие неверные типы, например, обозначь число с плавающей точкой «MessageText»;
— Передавай по значению параметры типа Object или String, и никогда не инкапсулируй значимые типы;
— Если ты должен ввести константы, давай им такие же имена, как их значения. Или используй греческие буквы;
— Комментарии – возможность добавить шум и неверное направление. Лучшие комментарии объявляют, что делает код, и даже после изменений кода остаются прежними;
— Помни, что человек, читающий твой код, должен видеть только то, что код делает, а не как он это делает: открытое соединение, получение результатов, обработка записей, возврат результатов, и т.д. И ни при каких обстоятельствах не используй базовую терминологию по делу.

Пиши нечитаемый код


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

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

Модульность – это ограничение. Ты можешь хранить все решение у себя в голове, так зачем распространять их через многочисленные классы и методы?

Сложность – это прекрасно, а излишняя сложность и того лучше. Намного лучше использовать последний функционал языка, который позволит тебе написать две непонятных строчки кода, а не пять очевидных. Если же кто-то спросит, заяви, что это улучшит эффективность и/или посмейся над их незнанием тайных особенностей языка, которые ты использовал.

Пиши несгибаемый код


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

Не делись информацией


Никогда не объясняй, почему твой зомби-код что-то делает. Избегай своих сокомандников или разговоров о проекте, над которым ты работаешь. Твои товарищи по команде могут предполагать и досаждать советами, или ты можешь случайно показать то, что поймут твои сокомандники. А это нежелательно.

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

Если тебе приходится работать с кем-то по электронной почте. Жди не менее двух дней между ответами и часто меняй свой почтовый ящик.

Обойди или нарушь, так называемые, «политику», «стандарты» или «процессы», которые могут помешать твоим злодейским планам. Они обещают тебе «улучшение качества» и «экономию времени», но вранье.

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

Новые разработчики в команде, особенно новички, должны метаться из огня да в полымя без ориентации, образования или поддержки. Удостоверься, что их вопросы проигнорированы или высмеяны что на них давят, пытаясь от них избавиться. Если ты действительно хочешь заставить заплатить их по счетам, заблокируй доступы к Google и Stack Overflow.

Никакой DevOps. Никогда


DevOps методов и специалистов следует избегать. Настрой все вручную, так как тебе нравится и меняй все, когда тебе этого захочется. С подозрением относись к системам управления – представь свой код свету, только когда все будет готово. Обязательно используй псевдоним и ничего не раскрывай в сообщениях.

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

Если ты должен документировать, делай это ужасно.

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

Не ищи готовые решения


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

Пиши код для зомби-апокалипсиса


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

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

Просто не подписывайся своим именем.

Средняя зарплата в IT

110 500 ₽/мес.
Средняя зарплата по всем IT-специализациям на основании 7 138 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 42

    0
    Страшный сон разработчика. Аж передернуло.
      +5
      Про зомби есть более достойный труд: http://habrahabr.ru/post/203276/
        +2
        Лучшие комментарии объявляют, что делает код, и даже после изменений кода остаются прежними;

        Неправда! Лучшие комментарии — те, которые пишутся при разработке кода (например, для упрощения какого-нибудь выражения, или заметки по логике программы), и потом не удаляются, даже если написанный далее код получился с использованием совсем другого подхода.
        Намного лучше использовать последний функционал языка, который позволит тебе написать две непонятных строчки кода, а не пять очевидных.
        Лучше использовать древний функционал, оставленный для совместимости или каких-нибудь редких случаев. Вроде union в С++.
          0
          Тогда уж надо вкладывать умные указатели в union, и обращаться таким образом к внутренним переменным. Это гарантирует, что код никогда не переведут на более свежую версию языка, и даже ключи компиляции будут менять с осторожностью :)
            +1
            Тогда достаточно работать с таблицами виртуальных функций. Подменяя в них функции для выбора поведения объектов.
            +1
            union — функционал древний но не устаревший)
            даже goto — древний но не устаревший.

            с goto пример наизусть не помню, но где то была статья по поводу выгоды goto по сравнению с другими конструкциями

            с union ом могу привести пример где он не испортит ничего и будет стабильно работать на всех платформах и под всеми компилерами.

            например:

            class CVariant
            {
            union
            {
            int i;
            double d;
            short s;
            long l;
            } data;

            enum DataType
            {
            INT,
            DOUBLE,
            LONG,

            }

            DataType m_type;

            public:
            // Главное правильно написать конструктор
            // и методы добавления/извлечения
            };
              0
              Я специально избегал слова «устаревший». Главное, чтобы он был непопулярным, почти забытым и небезопасным при неаккуратном использовании. А если у него ещё синтаксис будет похожим на что-то знакомое (как у union) — то вообще здорово.
              while(parrot.view1.x>target) parrot.view2.y--;  // select best bits 4:7
              
                0
                #define union struct

                :-)

                И потом гден-ть #undef
                  0
                  Я тут недавно предложил #define i (*p)
                  Мне сказали «спасибо, попробую».
                  Но переопределять ключевые слова или общеупотребительные константы это слишком грубо. Тоньше надо быть, ласковее… И в действиях должен присутствовать конструктивный смысл — даже если это будет экономия нажатий на кнопки или места на экране.
                    0
                    Нежнее и ласковее — это, например static i386_insn i где-нибудь на 479й строчке здоровенного файла.

                    И ничего — люди справляются, никто не умер…
                      0
                      Хорошо, что i386_insn — не простой тип, а union. Иначе могли бы забыть описать переменную i в функции — и нечаянно испортить глобальную переменную. Но да, ход мыслей авторов этого хода мне нравится.
                0
                Боюсь, что если использовать этот класс по назначению — брать из него short, когда там лежит long (потому что нужны только младшие биты), сравнивать положительные double по их long представлению (потому что на данном процессоре так быстрее), пользоваться тем, что старшие биты long не изменились, когда в него положили short — то на некоторых платформах он может начать работать слегка по-другому.
                  0
                  Тогда это будет ошибка в компиляторе и его исправят. Подобное использование явным образом разрешено в GCC.
                    +1
                      x.l=-5;
                      y=x.s;
                    

                    Обычно мы получим y=-5. Но на процессоре в режиме big-endian будет y=-1. С альтернативными способами представления отрицательных чисел — ещё хуже. И при этом — никаких ошибок, просто работает по-другому.
                      0
                      Тем не менее — это поведение, определяемое реализацией и в определённых проектах вполне может быть допустимо. Например если вы работаете только и исключительно на процессорах little-endian с «обычными» отрицательными числами. Жизненный пример: JIT-компилятор из x86 кода в ARM или наоборот. Да, она на каком-нибудь 68060 не запустится. Ну и что? Зачем вам запускать его там? Что вы там с ним будете делать?

                      А вот если на неопределённое поведение «наступить» — тогда совсем другое дело.
                        0
                        А сдвиг отрицательного числа к какому из трёх типов относится?
                          –1
                          Вы иронизируете про запуск на устаревшем 68060, но ведь не можете знать, на каком процессоре код, который вы пишете, рискнут запустить в будущем. Срок жизни кода умеет удивлять.
                      0
                      Если брать short, когда в нем лежит long, то тогда надо проверять его m_type на тот тип который в нем хранится (long) и этот тип явно преобразовывать к short'у Типа так:
                      short CVariant::as_short()
                      {
                          short result;
                          switch(m_type)
                          {
                           case LONG:
                              result = (short)data.l;
                              break;
                           case DOUBLE:
                              result = (short)data.d;
                              break;
                           ...
                          }
                          return result;
                      }
                      
                      


                      Таким образом возлагая все эти аппаратно-зависимые штуки на компилятор.
                      –1
                      Goto вполне себе используется и очень много, особенно при компиляции новомодных конструкций. Async / await в шарпе; F# — тысячи их. Кто-нибудь хоть раз дизассемблировал код, генерируемый F#? :) А типы там вообще называются Discriminated Union.
                        +1
                        Goto в языке высокого уровня и branch/jump в ассемблере или байт-коде это, всё-таки, немного разные конструкции. С разной статистикой использования и влиянием на поддерживаемость использующего их кода. Фрагмент ассемблерного кода без переходов понимается несколько сложнее, чем тот же кусок с переходами, хотя выполняется эффективнее.
                    0
                    и пропусти несколько важных шагов – ты должен был стараться, чтобы понять код, значит остальные тоже должны попотеть.
                    Что-то в этой фразе не так. Ты не должен «понимать код», ведь ты его пишешь. Наверное, имелось в виду что-то другое?
                      0
                      Пожалуй. Корректнее было бы: «ты должен был постараться написать непонятный код, значит остальные должны потрудиться, чтобы расшифровать его»
                        0
                        Я понял так, что «ты должен был постраться, чтобы их [важные шаги] найти, пусть и остальные потрудятся».
                          0
                          Автор имеет ввиду написание документации к чужому коду же, не?
                      +7
                      Эх, ещё один пост советов «от противного» в стиле задолба.ли. Как по мне, так интереснее было бы читать что-то, более приближенное к реальности.
                      И, да, языки меняются и нередки ситуации, когда код, который идеально десять лет назад, по сегодняшним меркам будет тянуть на троечку, а просто нормальный код будет выглядеть говнокодом.
                        +18
                        Имхо сей опус, перенасыщенный очевидностями и гиперболами, покрытый легким налетом пафоса и содержащий жалкие попытки сарказма, выглядит довольно нелепо.
                          0
                          А ведь найдутся «доброжелатели», которые последуют этим советам.

                          Меня прям передергивает от гнева.
                          Я бы расстреливал за это.

                          «С другой стороны, если тебе пришлось руководить другими разработчиками, ты можешь выпускать свою политику, директивы и вносить изменения в процессы так часто, как ты этого захочешь. Но никогда не объясняй зачем все это.»

                          С вертушки по чайнику такому бы руководителю.

                          Ох, «доброжелатели» и «последователи зомби кода», молитесь, чтобы не встретиться со мной!)
                            +4
                            Ох, «доброжелатели» и «последователи зомби кода», молитесь, чтобы не встретиться со мной!)

                            А вы молитесь, чтобы не встретиться с нашим кодом!
                            +6
                            Во-первых, если код работает, не исправляй его.

                            На самом деле иногда не такой уже и плохой совет )
                              +3
                              Два рефакторинга равны одному пожару.
                                0
                                Поэтому рефакторинг должен быть один. Но идти постоянно.
                                  0
                                  А еще можно прямо перед релизом, когда все экстренно фиксят баги, залить какой-нибудь особо мощный рефакторинг, включающий самую важную для релиза фичу. :-)
                                  И все будут плакать.
                                    +3
                                    Главное — делать это в пятницу вечером.
                                    0
                                    NSA тремя руками за! Поскольку при таком подходе не нужно платить за вставление «закладок» и рисковать тем, что «спалишься». Очень удобно, да. Но вам-то это зачем?
                                      0
                                      Нет, платить придётся неоднократно. Вдруг их отрефакторят? Ещё и следить придётся всё время.
                                        +1
                                        Отрефакторят — новых насажают, вы их найдёте и «внесёте в список», делов-то. Одно дело когда у вас одна-две «закладки» в вылизанном и выутюженном коде, другое — когда у вас коллекция из десятков и сотен «готовых к примерению» дыр в коде, который постоянно рефакторят.

                                        Только не нужно мне рассказывать про то, что тесты, обязательный аппровал кода ревьюером, секьюрити ревьюэры, фаззеры и статические анализаторы спасут вашу задницу. Участвовал я в одном проекте, где всё это было — а ещё там были (и есть) дыры от рефакторинга. Увы и ах, но «код, который постоянно рефакторят» ⊂ «код, в котором куча дыр».

                                        Единственный способ выловить (и исправить) ошибки в коде — это прекратить его рефакторить. Нужно оно вам или нет — это другой ворос.

                                        Именно поэтому я на 200% уверен что в современных OS нет никаких закладок от NSA. Зачем они там? Современные методы написания кода гарантируют наличие огромного количества дыр, так зачем же «палить контору» закладками?
                                          0
                                          А что делать? Неожиданно понадобилось ускорить программу в 4 раза — иначе она на микрокомпьютере не успевает. 2.5 раза я уже нашёл, осталось ещё 1.6. Без перетряхивания архитектуры не обойтись. «Тесты, обязательный аппровал кода ревьюером, секьюрити ревьюэры, фаззеры и статические анализаторы»? — не знаю, не слышал, так что не спасут :) Радует ещё, что в другой части проекта тоже идёт активный рефакторинг. В общем, весело.
                                            +2
                                            А это уже другой вопрос. Если вам нужна надёжность и безопасность — то вы сделаете этот компонент «высеченным в камне», за несколько лет отловите все ошибки — и в таком виде и будете использовать. И никакого рефакторинга, тем более «постоянно идущего». В качестве примера можно привести гипервизор в приставках — его именно так делают.

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

                                            Просто нужно отдавать себе отчёт в том, что ты делаешь, вот и всё. Рефакторинг, тесты и прочее — это всего-навсего инструменты. Иногда ими нужно пользоваться, а иногда — не стоит. Всё зависит от задач.

                                            P.S. Хотя про вариант совсем без тестов я как-то не думал. Это уж как-то слишком страшно для меня…
                                +1
                                Да, примерно половина, если не больше, признаков во внедряемом у нас продукте имеются… и нам потом его поддерживать.
                                  0
                                  На мой вкус это вторичная статья к гайдлайну “How To Write Unmaintainable Code”
                                  https://thc.org/root/phun/unmaintain.html

                                  >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.

                                  Этому гайдлайну до сих пор многие следуют, создавая так массу рабочих мест.
                                    0
                                    С этим надо бороться
                                    +1
                                    Ну как перевести это «that will enable you… to write legacy code immediately»? Красота же! А в перевод не попало :(

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

                                    Самое читаемое