Как говорится, если тебе не стыдно за свой старый код, значит, ты не растешь как программист — и я согласна с таким мнением. Я начала программировать для развлечения более 40 лет назад, а 30 лет назад и профессионально, так что ошибок у меня набралось очень много. Будучи профессором информатики, я учу своих студентов извлекать уроки из ошибок — своих, моих, чужих. Думаю, пришло время рассказать о моих ошибках, чтобы не растерять скромность. Надеюсь, кому-то они окажутся полезны.

Третье место — компилятор C от Microsoft


Мой школьный учитель считал, что «Ромео и Джульетту» нельзя считать трагедией, потому что у героев не было трагической вины — просто они вели себя глупо, как и положено подросткам. Тогда я с ним не согласилась, но сейчас вижу в его мнении рациональное зерно — особенно в связи с программированием.

К моменту окончания второго курса MIT я была молода и неопытна, как в жизни, так и в программировании. Летом я проходила практику в Microsoft, в команде компилятора C. Поначалу я занималась рутиной вроде поддержки профилирования, а потом мне доверили работу над самой веселой (как я считала) частью компилятора — оптимизацией бэкенда. В частности, я должна была улучшить код x86 для операторов ветвления.

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

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


Вынесенный урок


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

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

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

Мне нужно было позвать опытного разработчика и вместе с ним подумать, какие были общие случаи, и разобраться конкретно с ними. Я написала бы меньше кода, но это даже хорошо. Как писал основатель Stack Overflow Джефф Этвуд, злейший враг программиста — сам программист:

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

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


Второе место: реклама в соцсетях


Когда я работала в Google над рекламой для соцсетей (помните Myspace?), я написала на C++ что-то вроде этого:

for (int i = 0; i < user->interests->length(); i++) {
  for (int j = 0; j < user->interests(i)->keywords.length(); j++) {
      keywords->add(user->interests(i)->keywords(i)) {
  }
}

Программисты, возможно, сразу увидят ошибку: последним аргументом должен быть j, а не i. Модульное тестирование не выявило ошибку, не заметил ее и мой проверяющий. Был проведен запуск, и как-то ночью мой код отправился на сервер и обвалил все компьютеры в дата-центре.

Ничего страшного не произошло. Ни у кого ничего не сломалось, потому что перед глобальным запуском код тестировался в пределах одного дата-центра. Разве что SRE-инженеры ненадолго бросили играть в бильярд и сделали небольшой откат. Наутро я получила email с аварийным дампом, исправила код и добавила модульных тестов, которые выявили бы ошибку. Поскольку я следовала протоколу — иначе мой код бы просто не прошел запуск — других проблем не возникло.


Вынесенный урок


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

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

Вот что рассказывают о Томасе Уотсоне, легендарном главе IBM:

Был объявлен госзаказ на сумму около миллиона долларов. Корпорация IBM — а точнее, лично Томас Уотсон-старший — очень хотел его получить. К сожалению, торговый представитель не смог этого сделать, и IBM проиграла тендер. На следующий день этот сотрудник пришел в кабинет к мистеру Уотсону и положил ему на стол конверт. Мистер Уотсон даже не стал в него заглядывать — он ждал сотрудника и знал, что это заявление об уходе.

Уотсон спросил, что пошло не так.

Торговый представитель подробно рассказал о ходе тендера. Он назвал допущенные ошибки, которых можно было избежать. Наконец, он сказал: «Мистер Уотсон, спасибо вам за то, что дали мне объясниться. Я знаю, насколько мы нуждались в этом заказе. Я знаю, как он был важен», — и собрался уходить.

Уотсон подошел к нему у двери, посмотрел ему в глаза и вернул конверт со словами: «Как я могу дать тебе уйти? Я ведь только что вложил миллион долларов в твое образование.

У меня есть футболка, на которой написано: «Если на ошибках и правда учатся, то я уже магистр». На самом деле по части ошибок я доктор наук.

Первое место: App Inventor API


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

Чем хуже, тем лучше


Я читала эссе Ричарда Гэбриела об этом подходе в девяностые, будучи аспирантом, и оно мне так нравится, что я задаю его своим студентам. Если вы плохо его помните, освежите память, оно небольшое. В этом эссе желание «сделать как надо» и подход «чем хуже, тем лучше» противопоставляются по многим параметрам, включая простоту.

Как надо: дизайн должен быть простым в реализации и интерфейсе. Простота интерфейса важнее простоты реализации.

Чем хуже, тем лучше: дизайн должен быть простым в реализации и интерфейсе. Простота реализации важнее простоты интерфейса.

Забудем об этом на минутку. К сожалению, я забыла об этом на долгие годы.

App Inventor


Работая в Google, я входила в команду App Inventor, онлайновой среды разработки с поддержкой перетаскивания объектов для начинающих Android-разработчиков. Шел 2009 год, и мы спешили вовремя выпустить альфа-версию, чтобы летом провести мастер-классы для учителей, которые могли бы пользоваться средой при обучении уже осенью. Я вызвалась реализовать спрайты, ностальгируя по тому, как в свое время я писала игры на TI-99/4. Для тех, кто не в курсе: спрайт — это двумерный графический объект, который может перемещаться и взаимодействовать с другими программными элементами. Примеры спрайтов — космические корабли, астероиды, шарики и ракетки.

Мы реализовали объектно-ориентированный App Inventor в Java, так что там просто куча объектов. Поскольку шарики и спрайты ведут себя очень похоже, я создала абстрактный sprite-класс со свойствами (полями) X, Y, Speed (скорость) и Heading (направление). Они обладали одними и теми же методами выявления столкновений, отскока от границы экрана и т.д.

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


Когда спрайты заработали, я решила, что можно реализовать объекты-шарики очень небольшим количеством кода. Проблема была лишь в том, что я пошла самым простым путем (с точки зрения реализатора), указав x- и y-координаты верхнего левого угла контура, обрамляющего шар.


На самом деле нужно было указать x- и y-координаты центра круга, как этому учит любой учебник математики и любой другой источник, упоминающий круги.


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

Я наконец-то запатчила эту ошибку лишь недавно, десять лет спустя. «Запатчила», а не «исправила», ведь как говорит Джошуа Блох, API вечны. Не имея возможности внести изменения, которые повлияли бы на существующие программы, мы добавили свойство OriginAtCenter со значением false в старых программах и true во всех будущих. Пользователи могут задать закономерный вопрос, кому вообще пришло в голову расположить точку отсчета где-то, кроме центра. Кому? Одному программисту, который десять лет назад поленился создать нормальный API.

Вынесенные уроки


Работая над API (что иногда приходится делать почти каждому программисту), вам стоит следовать лучшим советам, изложенным в видео Джошуа Блоха «Как создать хороший API и почему он так важен» или в этом кратком списке:

  • API может принести вам как огромную пользу, так и огромный вред. Хороший API создает постоянных клиентов. Плохой становится вашим вечным кошмаром.
  • Общедоступные API, как и бриллианты, вечны. Выложитесь на все сто: другого шанса сделать все, как надо, больше не представится.
  • Наметки для API должны быть краткими — одна страница с сигнатурами классов и методов и описаниями, занимающими не больше строчки. Это позволит вам легко реструктуризировать API, если с первого раза он выйдет не идеальным.
  • Распишите сценарии использования, прежде чем реализовывать API и даже работать над его спецификацией. Таким образом вы избежите реализации и спецификации полностью нефункционального API.

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

Название эссе Ричарда Гэбриела «Чем хуже, тем лучше» указывает на преимущество, которое получает тот, кто первым вышел на рынок — пусть даже с несовершенным продуктом — пока кто-то другой целую вечность гонится за идеалом. Размышляя над кодом спрайтов, я понимаю, что мне даже необязательно было писать больше кода, чтобы сделать все, как надо. Как ни крути, я грубо ошиблась.

Заключение


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

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

Комментарии (108)


  1. Fen1kz
    14.11.2019 17:57

    -del


  1. Fen1kz
    14.11.2019 18:04
    +1

    Я конечно не работал в этих ваших микрософтах с гуглами, но мне кажется самое ужасное в последнем примере это boolean originAtCenter.


    ИМХО, надо либо не делать origin point вообще (как она и сделала), либо делать уже кастомным значением. Для полного ублажения пользователей можно сделать процентным :)


    А так — ни то, ни сё и если кому-нибудь понадобится делать взаимодействие шарика с origin в центре и кубика с origin не в центре — он убьется.




    Пока писал, вспомнил что моей самой позорной ошибкой было сделать неверный расчет цены на определенные штуки в некой билетной системе на день (из-за чего потом базу откатывали), так что может она и права, а мне ей советы давать не пристало, хз хз


    1. Ndochp
      15.11.2019 08:54
      -1

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


    1. Yoooriii
      16.11.2019 00:23

      В iOS (macOS) есть свойство anchor (CALayer class). anchor(0.5, 0.5) — это центр, соответственно anchor(0, 0) и anchor(1.0, 1.0) — это углы. Математика — простейшая. И кстати все координаты это float(double) а не int.


  1. slg
    14.11.2019 18:06

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

    И там ошибки в синтаксисе. Похоже, автора это ничему не научило.


    1. MikeLP
      14.11.2019 19:38

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


      1. alan008
        14.11.2019 21:59
        +1

        Вот эта история:
        https://habr.com/ru/post/417435/


      1. SepiS
        15.11.2019 16:37

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


        1. mayorovp
          15.11.2019 16:48

          А можно подробности? Ни разу не видел подобного эффекта.


          1. SepiS
            18.11.2019 09:35

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


    1. Source
      16.11.2019 01:44

      Я рассчитывал, что в выводах она хотя бы упомянет, что не стоит называть переменные i, j, особенно, если речь в коде не про математику.


    1. devalone
      16.11.2019 01:47

      for (int i = 0; i < user->interests->length(); i++) {
        for (int j = 0; j < user->interests(i)->keywords.length(); j++) {
            keywords->add(user->interests(i)->keywords(i)) {
        }
      }
      


      И там ошибки в синтаксисе


      Где? Если ты про скобочки, то там может быть перегружен оператор круглых скобочек.


      1. Praksitel
        17.11.2019 01:35

        Когда пишешь реальный код для реального применения, то нужно зарубить на носу и всегда, повторяю, ВСЕГДА, писать так:

        for (int interest = 0; interest < user->interests->length(); interest++) {
          for (int keyword = 0; keyword < user->interests(interest)->keywords.length(); keyword++) {
              keywords->add(user->interests(interest)->keywords(interest)) {
          }
        }


        Думаю, понятно, почему?


        1. devalone
          17.11.2019 12:29

          так я не топлю за i, j, k, я спрашивал, где синтаксические ошибки. Тогда уж лучше range based for, читабельнее и, в зависимости от реализации interests(), может быть быстрее


        1. mayorovp
          18.11.2019 10:36

          Такая схема именования всех запутает. interest должен быть элементом коллекции interests, а не индексом в ней.


  1. astianKor
    14.11.2019 20:13
    +1

    У меня самая страшная ошибка это когда я 2 ночь подряд писал API на сервак и дошло дело до файлов. Так как время поджимало перешел на продакшн, что делать конечно нельзя. И перешел в папку с помощью cd где остались от тестирования папки, которые хотел удалить и прописал rm -rf / Более я на продакшн не ходил.


    1. ReklatsMasters
      15.11.2019 00:29

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


      1. Sabubu
        15.11.2019 04:44

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


        1. Forum3
          15.11.2019 07:39
          +1

          Консольный mc или WinSCP. С WinSCP плохо, что можно случайно нажать удаление (вроде в настройках правится)


          1. hatari90
            15.11.2019 09:59

            У WinSCP были проблемы с производительностью, если в директории много файлов. При этом тот же Far с NetBox справлялся на ура. Ну и вообще там полно косяков было, когда в одной из сессий проблема с подключением (например, хост не отрезолвился) — блокировалась работа приложения полностью, и кнопка отмены не работала. Рекурсивная раздача прав/смена овнера чудесным образом пропускала часть файлов.
            Но это у меня опыт с версией года 13-14 был, так что уже не актуально, скорее всего.


          1. Sabubu
            15.11.2019 11:14
            -3

            Не выношу консольную имитацию GUI. Консоль хороша для ввода команд и скриптов, а пытаться имитировать GUI сеткой из одинаковых символов — не лучшая идея. Настоящий GUI выглядит лучше, позволяет использовать шрифты разных размеров, лучше управляется мышью, поддерживает Ctrl + C/Ctrl +V, итд.


            Плюс, в mc какие-то сочетания клавиш из каменного века и не работает нормально кнопка Esc.


            1. Temtaime
              16.11.2019 01:06

              За что минусы? Не труЪ линуксоид?


            1. devalone
              16.11.2019 01:49

              > поддерживает Ctrl + C/Ctrl +V

              Так и консольная имитация GUI поддерживает.


            1. Busla
              16.11.2019 16:54

              Если вспомнить историю, то всё было ровно наоборот. Сначала появилась эта самая «консольная имитация GUI» — операторам первых ЭВМ были доступны физические кнопки и сетка из однотипных индикаторов сгруппированных по назначению.


    1. razielvamp
      15.11.2019 03:50

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


      А вообще из подобных косяков, хотел поменять права для скрытых файлов, начинающихся с точки командой типа: cd /var/myfolder; chown -R usr:usr .*
      Команда быстренько пробежалась по всем папкам, включая "..".
      Хорошо что это была чистая ОС и сервер только готовился к использованию — сервис не пострадал, но вот ssh вырубился намертво, и пришлось подрывать инженеров в ДЦ ехать (у самого пропуска не было). На следующий день задарил им по ананасу в качестве извинения.


      1. arheops
        15.11.2019 07:53

        Не вижу чем вам это поможет.
        rm -f все делают пару раз в жизни. А локальные файлы и без судо можно удалить без проблем.


        1. razielvamp
          15.11.2019 17:20

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


      1. tommyangelo27
        15.11.2019 13:00

        У меня такая же хрень была — сделал chown root:root / на проде.

        SSH отрубился, и пришлось физически восстанавливать доступ, а сервер был где-то в Азии… короче не знаю как админ это решал.
        Но приложение (веб-сервис) крутилось с недельку само по себе, без возможности вносить изменения.

        А, самое эпичное было то, что это была ночь перед моей свадьбой, и сидели с шефом примерно до 2 ночи, пытаясь что-то сделать (у меня была незавершенная сессия в WinSCP), но в итоге ничего не добились.


        1. razielvamp
          15.11.2019 17:34

          Прочитав ваш коммент, вспомнил еще одну историю, когда редактировал вручную /etc/passwd и в начале первой строки «root:root:...» поставил нечаянно какой-то символ.
          В итоге юзера root не стало и sudo перестало работать. На сервер под user заходить мог, но никаких изменений вносить, естественно, не получалось. И в ближайшие сутки все обещало обрушиться.
          Но в этом случае сервер был виртуальный, и удалось удаленно через гипервизор загрузиться с live cd, замаунтить раздел и вернуть root:root. Это был мой лучший урок «чем виртуальные машины отличаются от железных».


          1. mayorovp
            15.11.2019 18:30

            А для железных тоже есть аналогичные решения. Если только их безопасники не запретили...


      1. Source
        16.11.2019 01:52

        переучиваю себя работать под обычным юзером с беспарольным sudo

        Эм, а где вы научились работать под рутом? Сколько я помню Linux (с года этак 2006-го), открывать root-сессию без жёсткой на то необходимости, всегда считалось чем-то от чего в мире умирает один котёнок. Как, впрочем, и беcпарольный sudo.


        1. Busla
          16.11.2019 17:04

          Парольный sudo не очень сочетается с аутентификацией по ключам в ssh.


    1. RiseOfDeath
      15.11.2019 11:26

      rm -rf /


      Я когда с каким-то ембедедом работал, хотел снести с SD карты xloader с u-boot (чтобы залить свежепересобранный) и вместо sudo rm -rf ./boot (что было бы папкой /mount/boot) написал sudo -rf /boot

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


    1. Nikobraz
      15.11.2019 11:52

      hahaha, classic

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

      rm -rf ./
      в консоли оказалось
      rm -rf /


      1. devalone
        16.11.2019 01:51

        Можно было писать

        rm -rf .
        , тогда ничего страшного не случилось бы


  1. Artem_Mironov
    14.11.2019 20:13

    интересно было почитать, особенно понравилось второе место, с этой ошибкой столько проблем было при изучении с++, эх времена


    1. padmitriy
      14.11.2019 23:10

      Это традиционная описка, проверку на которую должны делать все ide


    1. devalone
      16.11.2019 01:53

      Кстати, почему принято для счётчиков использовать i и j? Их же во многих шрифтах легко перепутать и не заметить.


      1. jaguard
        16.11.2019 02:06

        Из математики еще пошло.
        А шрифты — это как раз хороший тест, если можно перепутать, шрифт не подходит для записи кода.


        1. devalone
          16.11.2019 02:11

          Может всё-таки не подходят однобуквенные переменные, которые легко перепутать?)


          1. jaguard
            16.11.2019 02:15

            Одно другому не мешает же.


          1. Busla
            16.11.2019 17:13

            Дело не в количестве букв, какой-нибудь оhfcjlocmpqw ситуацию не улучшит. Правильнее будет рекомендовать использовать термины предметной области, а там как раз зачастую те самые i да j и использованы.


            1. devalone
              16.11.2019 18:22

              Правильнее использовать логичные названия, типа keywordIndex, а не i или j


  1. MSC6502
    14.11.2019 21:27
    -3

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


    1. GreedyIvan
      14.11.2019 22:37
      +2

      Чем там гордиться? Тем, что такую специализированную оптимизацию поддерживать — запредельное зло?


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


      Смотришь на такое и понимаешь: "работает — не трожь". Слишком долго и дорого это менять


  1. mjr27
    14.11.2019 23:04
    +5

    Тю.


    Ни выливания новой версии софта с крешем на старте и поломанным обновлением.


    Ни забытого where в delete на проде


    Ни "ой, я удалил settings.php на проде, никто доступ к базе не помнит?"


    Ни "а давайте закомментим эти 50 тестов, потому что все совсем поменялось, а времени их править нет"


    Какие-то ошибки в стране единорогов, блюющих радугой.


    Тю


    1. Alek_roebuck
      15.11.2019 04:52
      +3

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


      1. begemotv2718
        15.11.2019 12:51

        Да Федор Михайлович еще когда…

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


      1. Wizard_of_light
        15.11.2019 13:04
        +1

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


        1. vedenin1980
          15.11.2019 13:13

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


    1. krankenstein
      15.11.2019 16:37

      Только вот описанные ошибки могли помешать или даже помешали миллионам людей.
      А ваши примеры — ни о чем, ну может кроме первой. Да и первая ни о чем.


  1. panteleymonov
    14.11.2019 23:53
    +4

    Как говорится, если тебе не стыдно за свой старый код, значит, ты не растешь как программист — и я согласна с таким мнением.
    Как же так, вы прожили жизни в написании кода и получается что теперь вам за него стыдно? Мне кажется «стыдно» тут не правильное слово. Или вы на работе дурака валяли, поэтому за такой код вам стыдно? Мне НЕ стыдно за свой старый код, поскольку я знаю сколько труда в него вложено. Даже если я сейчас знаю как сделать лучше — это еще одна возможность приложить свои усилия к работе. А вот стыдно пускай будет (должно быть) тем кто на govnokod.ru выкладывает чужой код и стебется над ним.


    1. kai3341
      15.11.2019 14:23

      А вот стыдно пускай будет (должно быть) тем кто на govnokod.ru выкладывает чужой код и стебется над ним.
      Серьёзно? Идея говнокода вовсе не в том, чтобы просто стебаться.
      PS: О, его отмодерировали


      1. panteleymonov
        16.11.2019 11:07

        Претензии не к идеи govnokod.ru, а к тому как его используют. И по статье разница между, «у тебя стиль кода невыдержан» или «глаза кровоточат глядя на это» все таки существенная. Тем кого устраивает такой нравственный облик, сложно что либо объяснять.
        Были случаи когда туда «заготовку с листочка» выкладывали.


  1. bogolt
    15.11.2019 00:49

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


    1. ZetaTetra
      16.11.2019 00:38

      Вообще странный баг, если софтом пользуется большое количество пользователей, то первая линия была-бы завалена feature request'ами, а тут только через 10 лет фикс...


    1. devalone
      16.11.2019 02:01

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


      В отличие от моих прошлых ошибок, от этой пострадали не только мои коллеги, но и миллионы пользователей App Inventor. Многие из них были детьми или совсем новичками в программировании.


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


    1. nikolayv81
      18.11.2019 09:01

      С этими шариками и квадратиками (было ещё в какой-то программе crop от центра, не помню), постоянная проблема, т.к. люди разрабатывающие ПО и пользующиеся им часто живут в сильно разных мирах.
      Т.е. по сути feedback не дошёл...


  1. kozar
    15.11.2019 00:54
    +2

    for (int i = 0; i < user->interests->length(); i++) {
      for (int j = 0; j < user->interests(i)->keywords.length(); j++) {


    Давно отказался от i/j/k для счетчиков, и тем более, всяких s/l/n для переменных. Только осмысленные названия, вроде interestCount и interestIndex.
    Всё-таки не на бумаге пишем, и мониторы давно не 40 и даже не 80 колоночные. Читаемость важна, и если не делать 150 уровней вложенности и 20 операторов в строке, места для человекочитаемых названий хватает. А после компиляции или минификации вообще все равно, какая длина имени была.

    Кстати, насчет вложенности. На самой заре программерской жизни, написал как-то вложенные циклы — for i, for j, for k, а потом, когда начало верхнего цикла уже было тремя экранами выше, снова for i. Вот это было веселье для новичка отлаживать! Поведение совершенно дикое получилось. На всю жизнь запомнил, что так глубоко циклы вкладывать не стоит.

    P.S. Хотя, это же упрощенный пример, вполне возможно, в коде автор совсем по-другому называла переменные.


    1. DrZlodberg
      15.11.2019 09:05
      +1

      Как-то раз попалась такая рекомендация (которй сейчас и пользуюсь при необходимости)

      for(i..)
         for(ii..)
            for(iii..)
      

      От опечаток, конечно, не спасёт, но сразу видеть глубину цикла удобно.


      1. viktprog
        15.11.2019 11:41
        +11

        А после iii идет iiii или iv?


        1. DrZlodberg
          15.11.2019 11:52

          Дело вкуса. Если нет слишком большой глубины, то по мне — проще iiii. Или сразу i0..i3. Но больше 3 — это уже обычно очень хитрые случаи. Даже 3 попадалось только для работы с 3D-текстурами. Для разных задач проще разделить. Например i..iii для итерации по масиву и j..jjj для вычислений по конкретной ячейке. При большой разнице в грубине перепутать становится сложнее.


          1. Source
            16.11.2019 14:17

            Например i..iii для итерации по масиву и j..jjj для вычислений по конкретной ячейке.

            Да уж. Это настолько толсто, что даже кажется тонким xD


        1. devalone
          16.11.2019 02:05

          ix4


      1. DrGluck07
        15.11.2019 14:14

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


        1. DrZlodberg
          15.11.2019 14:17

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


          1. Rsa97
            15.11.2019 15:35

            Тут же разница заметна даже просто по длинне, что достаточно неплохо бросается в глаза.

            Вот вы лишнюю букву в слове «длина» напечатали, и ничего, в глаза на бросилось :).


      1. devalone
        16.11.2019 02:04

        но сразу видеть глубину цикла удобно

        man табуляция


        1. DrZlodberg
          18.11.2019 08:21

          И как она поможет различить переменные из разных уровней?


    1. vionoche
      15.11.2019 13:54

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


      1. ZetaTetra
        16.11.2019 00:42

        В таких случаях главное в стек не упереться ;)
        И в зависимости от языка может быть медленно. Всякие там паковки и пр.


        1. devalone
          16.11.2019 02:11

          Добро пожаловать в 2019, компиляторы уже давно умеют оптимизировать код


          1. ZetaTetra
            16.11.2019 09:58
            -1

            Такое «удобство» неминуемо стрельнёт себе в ногу.
            Особенно если надеяться на умный компилятор:
            ideone.com/ilKvzM


        1. Alcpp
          16.11.2019 17:17

          Use «inline», Luk.


          1. ZetaTetra
            17.11.2019 11:56

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


    1. PsyHaSTe
      15.11.2019 14:51

      Именованные переменные это конечно хорошо, но что насчет


      for (auto interest: user->interests) {
          for (auto keyword: interest->keywords) {
              keywords->add(keyword);
          }
      }

      ?


      Ну а в идеале декларативно конечно:


      keywords.extend(
          user.interests
              .into_iter()
              .flat_map(|x| x.keywords),
      );

      Как на cpp это выражается сходу не скажу.


    1. Darth_Malok
      15.11.2019 17:45

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

      UPD: Выше уже написали. Был невнимателен. Но очищать коммент не буду. Всякие «del» и «удалено» меня смущают больше, чем дублирующая информация.


    1. devalone
      16.11.2019 02:03

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


  1. McBernar
    15.11.2019 02:53

    Как же я люблю эту картинку!
    Простите.


  1. Psychosynthesis
    15.11.2019 03:49

    Нууу нее знаааюю…

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

    Что касается кода который я писал несколько лет назад… Ну да, бывает такое, что благодаря опыту сейчас некоторые вещи выглядят диковато, но также полно мест, на которые я смотрю сейчас и думаю «Вау, нихрена себе, это я написал?». Походу мы с автором как-то очень по разному мыслим.


  1. spax555
    15.11.2019 08:39
    -1

    Да уж, позорно


  1. man41k
    15.11.2019 09:00
    +1

    Предлагаю под этим комментарием поделится своими «успехами» в денежном эквиваленте, мой суммарный урон который нанесен работодателю за 10 лет: 1200$


    1. nekt
      15.11.2019 14:18

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


      1. man41k
        15.11.2019 14:28

        Урон рассчитан по себестоимости использованных материалов + временные затраты + утилизация ненужного изделия. Репутация работодателя не пострадала. Исправлялась ошибка с горящим стулом, бесплатно )


    1. devalone
      16.11.2019 02:13

      Буквально уронил сервер?)


  1. Andrey_Rogovsky
    15.11.2019 09:11
    +1

    Чем оптимальнее код, тем сложнее его поддерживать
    Чем проще код, тем его проще поддерживать
    Простой код не оптимален, а значит требует больше ресурсов
    Отсюда вывод — чем больше ресурсов потребляет код, тем более он поддерживаемый
    Исключение из правил — MS Windows


    1. Tanriol
      15.11.2019 20:45

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


  1. vin2809
    15.11.2019 10:23

    Меня больше удивляет, что такой код (никем не тестированный и написанной сырой программисткой) попадает в прод. А мы потом ругаемся на качество ОС Windows или студию. Так все же закономерно: просто разработчик сэкономил на тестерах и на кошечках, т.е. на нас, пользователях, проверяют новую версию...


  1. Sergei_Erjemin
    15.11.2019 11:33
    +1

    На самом деле нужно было указать x- и y-координаты центра круга, как этому учит любой учебник математики и любой другой источник, упоминающий круги.

    Но надо оперировать не только шариками, но и звездочками, линиями, и вообще произвольными растровыми изображениями. Специальный паттерн описания круга — разрушает целостность подхода к управлению спрайта! Почему было не сделать маску (тень) объекта — которая определяла бы где объект спрайта, а где уже нет? Причем, эту маску можно было бы вычислять автоматически (если пользователь ее не задал) или использовать преопределенную пользователем! Мне кажется, что как раз выпуск патча, описывающий круг как специальный объект, — ошибка. А действительно полезный патч, был бы добавление маски к спрайту. Причем он бы не ломал совместимости со старыми приложениями, ведь если маски нет — ее создают автоматически.


  1. sydorenko-vd
    15.11.2019 11:43
    +1

    Не совершает ошибок тот, кто ничего не делает.


  1. olekl
    15.11.2019 11:51

    А используй они PVS Studio, на одну ошибку было бы меньше…


  1. jetcar
    15.11.2019 11:51

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


    1. Peacemaker
      15.11.2019 16:07

      Есть ошибки на которых многие не учаться.

      Правописание "*т(ь), *т(ь)ся", кстати, тоже к ним относятся.


      1. jetcar
        15.11.2019 19:16

        если с "ь" и без текст работает одинаково, то ничего менять не нужно ))


        1. jaguard
          16.11.2019 02:12

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


          1. jetcar
            16.11.2019 09:43

            Kак вам повезло что тут ресурс для программистов и кроме знаний русского и немного программирования больше ничего не нужно показывать, но в школьном курсе есть другие предметы и вот тут не факт что вы выучили больше чем я :P


  1. alphasigma
    15.11.2019 12:06
    +1

    В метрополитене лет 15 назад, когда вводили бесконтактные билеты заметил, что если приложить билет одновременно: я к одному турникету, другой пассажир в другой турникет, то проходит только один, у второго ошибка возникала и нужно было либо еще раз приложить, либо подождать 7 минут, если билет безлимитный. Наверное, программист чувствовал в тот момент, когда обнаружили ошибку: анти-Робин Гудом, можно сказать все пассажиры вложились в его обучение.


  1. v1000
    15.11.2019 13:24
    +1

    была интересная история про то, как в большой компании выбирали между Linux и Windows для постройки инфраструктуры с нуля. Выбрали Windows, потому что в тестах Oracle на ней стартовала на 30 секунд быстрее. А через пару лет админ залез в скрипт запуска базы на Linux и нашел строчку sleep 30.


  1. kursornn
    15.11.2019 13:53

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


    1. nekt
      15.11.2019 15:07

      devops, 3 года опыта. Звучит неплохо на первый взгляд.


  1. progman_rus
    15.11.2019 14:05

    У меня самая ацкая ошибка была в 2006м. Я для одной конторы, которая занималась гэмблингом, писал пакет игр типа рулетка.
    И для отладки выигрышей забил вместо ГСЧ жесткую последовательность из 10 шаров после которой шли случайные.
    И естественно, по закону подлости, это все в прод утекло — админ поторопился выложить. Хотел похвастаться перед заказчиком как мы быстро все делаем. В продакшене около часа оно провисело. Было очень «весело» потом.
    Как ни странно финансовых убытков не было. Те кто выиграл за час потом все и спустили.


    1. Gorthauer87
      15.11.2019 16:12
      +1

      Блин, а ведь это не баг а фича, чуваки расслабились и втянулись, а потом закономерно все слили.


  1. BojackHorseman
    15.11.2019 16:37

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


  1. hasanraza
    15.11.2019 16:37
    -1

    Хорошая статья, я полагаю, что самой большой ошибкой программиста было бы прекращение процесса самообучения, как самого студента-программиста, один важный совет, который я хотел бы дать всем новым программистам / разработчикам программного обеспечения, состоит в том, чтобы сделать это привычкой следуя официальным блогам лучших в мире фирм по разработке программного обеспечения, таких как DCS, GoodCore Software и т. д., поскольку такие источники блогов регулярно предлагают самые последние обновления из области разработки программного обеспечения, также такие блоги действительно помогли мне понять последние процессы разработки программного обеспечения. Надеюсь, это тоже поможет. еще раз спасибо


  1. it-architector
    15.11.2019 17:43
    -1

    Среда либо забивает программиста, создавая в нём комплексы стыда, либо отважно помогает, как вы! Спасибо! А на остальных плевать.)


  1. Rhoads
    16.11.2019 00:41

    От статьи ощущение, что автор про эти свои «ошибки» HR'у рассказывает при приеме на работу.

    Это все равно что на вопрос «назовите свои худшие качества» ответить — я трудоголик и перфекционист! (вместо лентяй и рукожоп)
    Нет чтоб написать — неправильно поделила 4 на 2 и это из-за меня взорвался «Челленджер».

    ЗЫ Меня в микрософт работать не взяли, поэтому ошибки поэпичнее: неправильно проинициализировал выходные регистры в регуляторе тока — в результате сгорели все огни ВПП на аэродроме. Потом через несколько лет из-за косяка стерлись все видео у какого-то телеканала )


  1. willmore
    16.11.2019 12:03

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


  1. Dreddik
    16.11.2019 12:18

    Одной из позорнейших моих ошибок было изменение кода поля ввода пароля в приложении ВК для iOS. Тогда началась мода на то, чтобы можно было показывать пароль в закрытых полях (понять, что ты ввёл вообще), и я решил «ну а что бы нет», хотя это делать было вообще не нужно («работает, не трожь», да да) и задачи тоже не стояло.
    Я запилил красивый глазик рядом с полем пароля, протестил и всё было ок. Влил код в dev ветку, затем он ушёл на прод. И началось… Приложение крашилось тысячами раз. Куча людей просто не могли зайти в вк из-за меня. Трейс краша, однако, был совсем ничем не намекающий на проблему.
    На моем девайсе, конечно же, работало, как и на девайсах людей вокруг. Думали, что iOS как-то в этом замешана, людям помогал сброс настроек телефона.
    Когда мы нашли девайс, на котором воспроизводится, я обнаружил, что ошибка была невероятно тупейшей. Дело в том, что Objective-C код использует подсчёт ссылок. Он может быть ручной или автоматический. Тогла приложение ВК ещё использовало ручной, тк проект уже было довольно большой, мигрировать все было немного опасно и проблематично.
    Так вот, когда я добавлял функциональность, в одном месте мне нужно было вернуть значение поля пароля, строку (я слегка хачил и наследовался от класса поля ввода, нужно было переопределить некоторые методы). Код был примерно такой
    -(NSString*) text { return _textValue; }
    Короче, из-за ручного подсчета ссылок ось ожидала получить «дополнительно retained объект», и делала этой строке release (счётчик -1). Дальше строка высвобождалась и всё становилось плохо, sigsev все дела.
    Решение было простое, использовать [_textValue copy], чтобы вернуть «новую строку» (на самом деле, возвращается та же строка, просто счётчик +1).
    Было очень обидно, потому что сначала я искал проблему в другом месте, и мы даже выпустили промежуточную версию с «теоретическим» исправлением бага, который ничего не исправлял :(