Признаки плохого программиста

:

Неспособность рассуждать о коде


«Рассуждать о коде» значит понимать порядок исполнения инструкций («исполнять программу в голове»), зная, каково предназначение кода.
Симптомы

  • Наличие «волшебного», «вуду» кода или кода, который не имеет никакого отношения к целям программы, но всё равно тщательно поддерживается (например, инициализация переменных, которые никогда не используются, вызов функций, не имеющих отношения к цели, создание выходных данных, которые не используются, и т.д.).
  • Многократные вызовы идемпотентных функций (например, вызов save() по нескольку раз, «чтобы уж точно»).
  • Исправление ошибок написанием избыточного кода, который замещает данные, полученные при исполнении неисправного кода.
  • «Йо-йо код», который конвертирует значения в различные представления, а потом конвертирует их обратно ровно в то же представление, с которого начинали (например, преобразование десятичного числа в строку, а потом обратно в десятичное число, или padding строчки с последующим trim'ом).
  • «Бульдозерный код», который создает впечатление рефакторинга посредством разбития кусков кода на процедуры, которые, правда, затем невозможно использовать где-либо еще (высокая когезия).


Лечение

Чтобы справиться с этим пороком, программисту следует пользоваться встроенным в IDE debugger'ом как помощником в случае неспособности «шагать» по коду построчно. В Visual Studio, например, можно установить breakpoint в начале «проблемной» зоны и затем шагать, нажимая F11, параллельно следя за значениями переменных, до тех пор пока не станет ясно, что именно код делает. Если в целевом окружении нет такой возможности, следует практиковаться в том, в котором это возможно.

Цель — достичь такого уровня, когда вам больше не нужен debugger, чтобы исполнять код «в голове», когда вы достаточно терпеливы для того, чтобы думать о том, как код изменяет состояние программы. В качестве награды вы получите возможность определять избыточный и ненужный код, а также умение находить ошибки в существующем коде без реализации всего алгоритма заново.

Плохое понимание модели языка программирования


Объектно-ориентированное программирование — пример модели языка программирования, равно как и функциональное, и декларативное программирование. Все они существенно отличаются от процедурного или императивного программирования, точно так же как и процедурное программирование существенно отличается от ассемблера или программирования с помощью GOTO-инструкций. Также существуют языки, которые следуют какой-то распространённой модели программирования (скажем, объектно-ориентированному программированию), но вносят в неё какие-то усовершенствования, как например генераторные выражения (list comprehensions), обобщенное программирование (generics), утиная типизация (duck typing) и т.п.
Симптомы

  • Использование любого необходимого синтаксиса для того, чтобы «вырваться» из предлагаемой языком модели, и написание оставшейся части программы в императивном/процедурном стиле.
  • (ООП) Попытки вызова не статических функций или присвоения переменных в неинстанциированных классах, проблемы с пониманием, почему такие конструкции не компилируются.
  • (ООП) Наличие огромного количества «....Manager» классов, которые содержат все методы манипуляции с объектами, которые, в свою очередь, совсем не содержат (или содержат мало) методов.
  • (Реляционное) Обращение с базой данных как с хранилищем объектов, исполнение всех JOIN'ов и проверки целостности на клиентской стороне.
  • (Функциональное) Создание многих версий одного и того же алгоритма для обработки разных типов или операторов вместо передачи функций высшего порядка обобщенному алгоритму.
  • (Функциональное) Ручное кэширование результатов детерминистических функций на платформах, которые делают это автоматически (SQL или Haskell).
  • (Функциональное с «чистыми»(pure) функциями) Использование копипасты из чужого кода для того, чтобы побороть I/O и монады.
  • (Декларативное) Установление индивидуальных значений в императивном коде вместо использования связывания данных (data binding).
Лечение

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

Шаг 1: «ООП — это просто структуры с методами».
Шаг 2: «Методы в ООП — это просто функции, исполняющие мини-программу со своим собственным набором глобальных переменных».
Шаг 3: «Глобальные переменные называются полями, некоторые из них приватные и невидимы снаружи мини-программ».
Шаг 4: «Сама идея наличия приватных и публичных элементов — скрыть детали реализации и выставить наружу чистый интерфейс, и называется это инкапсуляция».
Шаг 5: «Инкапсуляция означает, что моя бизнес-логика не должна засоряться деталями реализации».

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

Шаг 1: «Функциональное программирование — это когда всё делается через цепочки детерминистических функций».
Шаг 2: «Когда функции детерминистические, их не нужно вычислять до тех пор, пока это не потребовалось в выходных данных, и вычислять необходимо только ту часть, которая действительно потребовалась. Это называется ленивыми и частичными вычислениями».
Шаг 3: «Для того чтобы поддерживать ленивые и частичные вычисления, компилятор требует, чтобы я писал функции в терминах трасформации одного параметра, иногда делая результатом таких преобразований другую функцию. Это называется каррированием (currying)».
Шаг 4: «Когда все функции каррируемы, это позволяет компилятору выбрать наилучший план исполнения с помощью constraint solver'а».
Шаг 5: «Позволяя constraint solver'у самому решать все неважные детали, я могу писать программы, описывая, что я хочу, а не как именно это получить».

Дефицит исследовательских навыков / Хронически плохое знание возможностей платформы

Современные языки и фрэймворки теперь поставляются с удивительно огромным количеством встроенных команд и возможностей. Некоторые технологии (Java, .Net, Cocoa), пожалуй, даже слишком велики, чтобы ожидать от какого-либо программиста, даже очень хорошего, что он изучит их быстрее, чем за несколько лет. Но хорошие программисты будут искать встроенные функции, которые делают то, что им нужно, прежде чем они начнут писать свои. А отличные программисты сумеют разбить свои задачи на части, выявить абстрактные проблемы, а затем найти фрэймворки, паттерны, модели и языки, которые могут быть применены.

Симптомы

Перечисленные ниже симптомы действительно указывают на проблему, только если продолжают проявляться в работе программиста достаточно долгое время после того, как он начал осваивать новую платформу.
  • Изобретение или создание механизмов, которые уже встроены в язык, например, event'ы и handler'ы, регулярные выражения.
  • Изобретение классов и функций, которые уже есть в фрэймворке (например, таймеры, коллекции, алгоритмы сортировки и поиска).*
  • Сообщения «Напишите мне код, плиз!» в различных форумах.
  • «Окольный код», который выполняет свою задачу с гораздо большим количеством инструкций, чем требуется (например, округление числа через преобразование к строке и обратно).
  • Постоянное использование старомодных техник, даже если новые лучше в текущей ситуации (например, создание полноценных именованных функций там, где нужно лямбда-выражение «на раз»).
  • Нереального размера «зона комфорта», когда человек готов пройти экстремально длинный путь, решая проблему примитивными средствами.

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

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

Неспособность понять указатели


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

  • Неспособность написать связный список, или написать код, который вставляет/удаляет узлы из связного списка без потерь данных.
  • Выделение произвольных больших массивов для данных переменного размера вместо связных списков или других динамических структур данных.
  • Неспособность найти или исправить ошибки, связанные с арифметикой над указателями.
  • Изменение разыменованных значений из указателей, переданных в функцию, без понимания, что это измененит значения за пределами области действия (scope) функции.
  • Создание копии указателя, изменение разыменованного значения через копию с ожиданиями, что оригинальный указатель по-прежнему указывает на старое значение.
  • Сериализация указателя, в то время как сериализовано должно быть разыменованное значение.
  • Сортировка массива указателей сравнением самих указателей, а не значений.

Лечение

Мой друг Джо жил в том же отеле, что и я, но я не знал, в каком именно номере. Однако я знал, где остановился его знакомый Фрэнк. Так что я пошел туда, постучал в дверь и спросил Фрэнка: «Где остановился Джо?». Фрэнк не знал, но он знал, в какой комнате живет Теодор, коллега Джо. Так что я пошел в комнату Теодора и спросил, где остановился Джо, и Теодор сказал мне, что Джо в комнате 414. Там-то я его и нашел!

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

Сложности с рекурсией


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

  • Ужасно сложные итеративные алгоритмы для решения проблем, которые могут быть решены рекурсивно (например, обход дерева файловой системы).
  • Рекурсивные функции, которые проверяют одно и то же условие до и после рекурсивного вызова.
  • Рекурсивные функции, которые не проверяют основное условие.
  • Рекурсивные процедуры, которые конкатенируют/накапливают значения в глобальной переменной или переносят выходную переменную, и не реализуют хвостовую рекурсию.
  • Путаница с тем, что передавать в качестве параметров при рекурсивном вызове, или рекурсивные вызовы, которые передают неизменные параметры.
Лечение

Закатайте рукава и будьте готовы к переполнениям стэка. Начните с написания функции с одним основным условием и с одним рекурсивным вызовом, который использует тот же, не изменённый аргумент, что и был получен на входе. Остановитесь, даже если вам кажется, что этого недостаточно, и запустите. Посмотрите на переполнение стэка, вернитесь обратно и добавьте изменение аргумента при рекурсивном вызове. Опять переполнение? Чрезмерный вывод? Тогда еще несколько итераций, переключаясь между изменениями в условии и в вызове, до тех пор, пока вы не начнете чуять, как функция трасформирует входные данные. Сопротивляйтесь желанию использовать более чем одно основное условие или более чем один вызов, если вы не знаете, зачем вы это делаете.

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

Неспособность мыслить коллекциями


Переход от императивного программирования к функциональному и декларативному потребует от вас способности думать об операциях над наборами данных. Коллекции станут вашим примитивом вместо скалярных значений. Это требуется, если вы используете SQL и реляционные базы данных, если вы разрабатываете программы, которые должны маштабироваться пропорционально количеству ядер процессора, или когда вы пишете код, который будет запускаться на SIMD-чипах (как, например, современные графические карты и игровые приставки).
Симптомы

Эти симптомы имеют значение, только если они проявляются в работе с платформой с декларативными или функциональными возможностями, о которых программист должен бы знать.
  • Исполнение атомарных операций над элементами коллекции внутри цикла for или foreach.
  • Map или reduce функции, содержащие циклы для итерации по набору данных.
  • Получение большого набора данных с сервера и вычисление сумм на клиенте, вместо использования агрегирующих функций в запросе.
  • Написание функций бизнес-логики с трагическими сайд-эффектами, вроде изменения пользовательского интерфейса или выполнения I/O функций.
  • Классы сущностей, которые открывают свои собственные соединения с базой данных или получают файловые дескрипторы и держат их активными всё время жизни каждого из объектов.
Лечение

Представляйте себе дилера в казино, который сдвигает колоду карт и перемешивает две части вместе пролистыванием, это может подтолкнуть вас к размышлениям о коллекциях и о том, как вы можете оперировать ими. Другие стимулирующие визуализации:
  • въезд на платную дорогу, оборудованный несколькими пунктами оплаты (параллельная обработка);
  • ручейки, которые соединяются в потоки, которые соединяются в рукава, из которых получается река (парралельное исполнение reduce / агрегирующие функции);
  • застёжка-молния (простые join'ы);
  • траспортная РНК, собирающая аминокислоты и соединяющаяся с информационной РНК внутри рибосомы, чтобы стать протеином (многостадийные funtion-driven join'ы);
  • то же самое, происходящее в миллиардах клеток в апельсиновом дереве, чтобы превратить почву, воду и солнечный свет в апельсиновый сок (map/reduce в больших распределенных кластерах);

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

Отсутствие критического мышления


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

  • «Движки бизнес-правил».
  • Толстые классы со статическими утилитами, или библиотеки, решающие множество разных задач, но имеющие одну область имён (namespace).
  • Архитектуры, которым начали требоваться эпициклы.
  • Добавление в таблицы колонок для не имеющих прямого отношения к этой таблице данных.
  • Несоблюдение однородности в именовании переменных и методов.
  • Ментальность «мужика с кувалдой», или изменение условий задачи так, чтобы она могла быть решена с помощью одной конкретной технологии.
  • Программы, которые своей сложностью затмевают задачу, которую они должны решать.
  • Патологическое и излишнее «защитное» программирование (defensive programming), «энтэрпрайзненький код».

Лечение

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

Когда вы привыкнете к тому, что другие люди изучают, проверяют ваши идеи, начните всесторонне изучать свои идеи самостоятельно. В дополнение, вам необходимо развить чувство пропорции (чтобы понимать, насколько дизайн подходит размеру задачи), привычку проверять предположения (чтобы вы не переоценивали размер задачи), и здоровое отношение к неудачам (Исаак Ньютон был неправ про гравитацию, но мы всё равно любим его, и его предположения очень важны для нас).

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

Пинбол-программирование

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

Симптомы

  • Один try-catch блок, включающий в себя всё тело Main(), рестартующий программу.
  • Использование строк или целых чисел для значений, которые могли бы иметь более подходящий им тип в языке со строгой типизацией.
  • Упаковка сложных данных в разделённые строчки и их парсинг в каждой функции, где эти данные используются.
  • Неспособность использовать утверждения или контракты методов на функциях, которые делают предположения о своих аргументах.
  • Использование sleep() для ожидания пока другой thread закончит свою работу.
  • Инструкции Switch над неперечисляемыми значениями без блока «иначе».
  • Использоватие automethod'ов или reflection'ов для вызова методов, исходя из неподготовленного пользовательского ввода.
  • Использование глобальных переменных для возврата из функции более одного значения.
  • Классы с одним методом и набором полей, которым необходимо установить значения, по сути, для передачи параметров в метод.
  • Обновление связанных данных в БД без использования транзакций.
  • Попытки восстановить состояние базы данных без транзакций и rollback.

Лечение

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

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

  • Те, что останавливают исполнение программы до того, как причинён какой-либо ущерб тем, что произошло что-то неожиданное, а потом помогают вам определить, что именно пошло не так (системы типов, assert'ы, exception'ы).
  • Те, что направляют программу по пути, который наилучшим образом обрабатывает неожиданности (try-catch блоки, мультиметоды (multiple dispatch), событийно-ориентированное (event-driven) программирование).
  • Те, что приостанавливают thread до достижения необходимого состояния (инструкции WaitUntil, мьютексы и семафоры, SyncLock'и).

Есть ещё и четвёртый механизм — unit тестирование, которое вы используете во время проектирования и разработки.

Использование этих механизмов должно быть для вас так же естественно, как и расстановка запятых в предложении. Чтобы достичь этого, пройдитесь по перечисленным в скобках механизмам и отрефакторьте какую-нибудь старую программу так, чтобы она использовала их везде, где только сможете их пристроить, даже если это окажется неприемлемым (особенно если это окажется неприемлемым, только так вы начнёте понимать почему).

Незнание принципов безопасности


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

  • Хранение важной информации (имена, номера кредитных карт, пароли) без какого-либо шифрования.
  • Хранение важной информации с использованием неэффективных средств шифрования (симметричные коды с ключами, встроенными в саму программу; простые пароли; шифры подстановки; самостоятельно разработанные и непроверенные коды).
  • Программы, которые не ограничивают собственные привилегии перед тем, как принимать сетевые соединения или интерпретировать входные данные из ненадёжных источников.
  • Невыполнение проверок границ и других проверок входных данных, особенно в неуправляемых окружениях.
  • Создание SQL-запросов конкатенацией строк с необработанными входными данными.
  • Код, который пытается предотвратить использование уязвимости, пытаясь найти характерные особенности применения этой уязвимости.
  • Номера кредитных карт и пароли хэшируются без соли.
Лечение

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

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

Далее, проверьте запросы к базам данных, которые конкатенируют неизменённые входные данные в SQL запрос, и перепишите этот код с использованием параметризированных запросов, если это позволяет ваша платформа. Ну, или добавьте фильтрацию/экранирование всех входных данных, если нет. Это чтобы предотвратить атаки с SQL-инъекциями.

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

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

И последнее: вам необходимо изучить основы шифрования, начиная с принципа Керкгоффса. Он может быть сформулирован как «безопасность должна быть заключена в ключе». Также существует несколько интересных выводов, основанных на этом принципе.

Во-первых, вы не должны доверять коду или другому криптографическому примитиву, если он не опубликован открыто и не был проанализирован и опробован сообществом. Нельзя достичь безопасности через неясность (security through obscurity), через проприетарность или через новизну. Реализации пусть даже заслуживающих доверия криптографических примитивов могут иметь изъяны, потому избегайте реализаций, которые не были тщательно изучены. Все новые криптографические системы попадают в «трубопровод» исследований длиною в 10 лет или более, и вам хорошо бы использовать те из них, что уже показались с другой стороны этой «трубы» невредимыми.

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

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

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


Следующие «болезни» неизлечимы. Так что если вы всё еще страдаете ими после школьного курса программирования, вам лучше попробовать себя в другой профессии.

Неспособность определить порядок исполнения программы

Симптомы

a = 5
b = 10
a = b

print a

Вы смотрите на этот код и не уверены, какое именно число будет напечатано.

Альтернативные профессии

  • Электрик
  • Слесарь
  • Архитектор
  • Инженер-строитель

Неспособность мыслить абстрактно

Симптомы

  • Сложности с пониманием различия между объектами и классами.
  • Сложности реализации шаблонов проектирования в ваших программах.
  • Сложности написания функций с низкой когезией.
  • Некомпетентность в регулярных выражениях.
  • LISP для вас непостижим.
  • Не можете понять тезис Чёрча-Тьюринга.
Альтернативные профессии

  • Уполномоченный по согласованию контрактов
  • Актёр

Синдром Плюшкина

Симптомы

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

Альтернативные профессии

  • Торговец антиквариатом
  • Бездомный с огромной тележкой хлама

Нарушение причинно-следственных связей

Симптомы

  • Вы всерьез рассматриваете вариант злого умысла как причину того, что компилятор отклоняет вашу программу.
  • Когда нужно исправить ошибку в программе, вы пробуете молитвы.
  • Вы считаете само собой разумеющимся наличие скрытых переменных и не задумываясь валите любые проблемы с программой на них!
  • Вы считаете, что присутствие кода в программе может изменить её поведение, даже если он никогда не вызывается.
  • Ваш репертуар методик отладки включается в себя протирку счастливого мячика для гольфа, вращение вашего обручального кольца, похлопывание игрушечной собачки с качающейся головой. И если вдруг что-то не так, то, по всей видимости, вы либо что-то пропустили, либо исполнили свои ритуалы не в том порядке.

Альтернативные профессии

  • Завсегдатай заведений с игровыми автоматами

Безразличие к результату


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

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

* — чаще всего появляются под давлением начальства, а не по инициативе программиста. Всё равно оставим их тут ради самопроверки, и заметим, что одному из них следовало бы поискать новую работу, а другому вернуться в бизнес-школу и подучить пару менее разрушительных способов извлечения прибыли.
Альтернативные профессии

  • Специалист по взысканию задолженности
  • Телемаркетинг