Как стать автором
Обновить

Байки погромиста. Если кто-то скажет, что программирование — это скучно

Уровень сложностиПростой
Время на прочтение12 мин
Количество просмотров54K
Всего голосов 177: ↑172 и ↓5+167
Комментарии56

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

У нас для этих целей была Knowledge Base и первым делом при исследовании проблемы все глядели в неё. А последним - заносили открывшуюся информацию в knowledge base. Избавило от множества грабель описанных в этой статье.

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

С Knowledge base есть одна проблема. Со временем она по масштабам превращается в гугл (если сравнивать поиск по это базе и по всему интернету). Только поисковик гугл поумнее будет чем поисковик по внутренней системе что бы там она из себя ни представляла :)

Никогда не запоминал курьезные случаи, но напомнили один:

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

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

.. Это был один из первых ботов-автоответчиков, и между ними состоялся примерно такой "диалог":

-"У нас есть заявка на покупку ..."

-"Ваше письмо очень важно для нас"

... каждые 5 минут, как часы. :)

Знаменитые войны почтовых роботов. Ещё в старом FIDO такое бывало...

В почтовой системе был обратный адрес, который назывался типа mail-robot@domain.ru

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

Была поставлена задача - отвечать на такие письма сообщением, в котором БОЛЬШИМИ КРАСНЫМИ БУКВАМИ было написано что-то вроде: "ВЫ ПЫТАЕТЕСЬ ОБЩАТЬСЯ С РОБОТОМ! ДЛЯ ОБЩЕНИЯ С ЖИВЫМ ЧЕЛОВЕКОМ ОТКРОЙТЕ ПЕРВОЕ СООБЩЕНИЕ И ПЕРЕЙДИТЕ ИЗ НЕГО ПО ССЫЛКЕ В СИСТЕМУ".

Через 2 недели после внедрения было выявлено, что в ответ на письмо с БОЛЬШИМИ КРАСНЫМИ БУКВАМИ приходит чуть более 10 ответов в неделю от пользователей, которые пытаются продолжить общение - просят переслать письмо "нормальному человеку", разворачивают свой вопрос из первого письма, решив что почтовый робот не понял с первого раза, и т.п.

Была поставлена задача на такие повторные сообщения отвечать ЕЩЁ БОЛЕЕ БОЛЬШИМИ КРАСНЫМИ БУКВАМИ текстом, содержание которого доносило бы до пользователя всю тщету его стараний достучаться до бездушной машины.

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

В ответе было всего одно слово: "Спасибо".

Если уж ЕЩЁ БОЛЕЕ БОЛЬШИМИ , то наверняка они должны были быть и ЕЩЁ БОЛЕЕ КРАСНЫМИ?))

Иногда вспоминаю прошлое и думаю, чего было больше: моих личных страшных историй, когда я копался в чужом легаси, или чужих страшных историй, когда кто-то копался в моём легаси?

Вопрос почти философский.

Хм. Когда я уже был готов поверить в магию.

Записываю значение в массив.
Считываю значение из масcива.
И... считывается не то, что я толкьо что записал!
Как так? Перепутал числа в объявлении двумерного массива, дело происходит на Си.

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

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

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

То это Питон. Из-за опечатки в одну букву ORM весело и молча создаёт второй объект с новым ID и пишет данные в него. А нормальный так и остаётся по нулям. Питону нужен опционально обязательный keyword по типу let - иначе постоянно боюсь опечататься при присваивании переменной и не знаю как защититься.

Хуже когда во всяких небезопасных языках пишешь за конец массива и меняется какая-либо глобальная переменная :) это тебе не Го, который спаникует, тут ищи сам :)

Я когда-то менял поле структуры в массиве в C# и не мог понять, почему ничего не меняется.

Поле структуры в массиве меняется прекрасно, у вас скорее всего была проблема со списком (List<>, расширяемый какбымассив).

Для тех кто не верит: https://dotnetfiddle.net/nuIa8X


struct Foo
{
    public int bar;
}

// ...

        var array = new[] { 
            new Foo { bar = 1 },
            new Foo { bar = 2 },
            new Foo { bar = 3 },
        };
        array[1].bar = 42;
        Console.WriteLine(array[1].bar); // 42

О, с массивами когда учился, был шедевральный случай.
Писал тогда на ТурбоPascal5
написал тестовую процедуру перемножения двух матриц.
Загоняю тестовые данные, получаю выходную матрицу и решил проверить, все правильно?
Какое же было мое изумление, когда в последних строках полученной матрицы оказался мусор, а все остальные данные правильные...
Потратил день, с дебагом, мистика какая то.
Пока не обратил внимание, что Выходная матрица объявлена без var.
То есть, при вызове процедуры, выделяется в stack область для результатов.
Так как вывод тестовых результатов имеет те же параметры что и основная функция, то при его вызове он получает те же участки памяти, для тех же переменных! В результате получает правильные данные, за исключением хвоста, где остался мусор от вызова служебных функций...
Зато отлично стал понимать разницу в передаче параметров по ссылке и по значению :)

Вспомнился один случай с Украинским банком. У них был CRM. Изначально для b2b клиентов было поле fullName. Но потом разделили ФИО отдельные поля.

А поле fullName операторы со временем стали использовать как comment поле описывая свои отношения с этим клиентом (ну не попросили сделать отдельное поле).

В какой-то момент начальство решило сделать большую маркетинговую рассылку и полетели письма во все стороны:

  • Уважаемый этот поц не отвечает на звонки! Предлагаем вам ...

  • Уважаемый матерится и вообще не приятный тип! Предлагаем вам ...

Как-то в одной конторе не справились с конвертацией булевых значений из/в VB при добавлении нового поля и всем клиентам проставилась галочка "есть судимость".

Ещё из категории черного юмора: добавили в карточку клиента поле "дата смерти", а при ее сохранении появлялось все то же стандартное диалоговое окно подтверждения с вопросом "клиент подписал заявление? [на смену персональных данных]"

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

А есть ли способ передать такой exception в основную ветку без особых костылей?

Если вы читаете результат, то exception всплывёт. В своём проекте мне результат не нужен был (ничего не возвращалось), и поэтому exception не было.

from concurrent.futures import ThreadPoolExecutor
def fn():
    raise ValueError("")

pool = ThreadPoolExecutor()
futures = [pool.submit(fn) for _ in range(10)]  # тут нет исключения
for future in futures: print(future.result())  # а вот тут есть

# ValueError                                Traceback (most recent call last)
# Cell In[5], line 1
# ----> 1 for future in futures: print(future.result())

Даже если вам нечего возвращать — вам всё ещё нужно дождаться завершения поставленной задачи, для чего result как раз отлично подходит.


Но тут возникает "проблема", что у метода shutdown в ThreadPoolExecutor есть такой "удобный" параметр wait, да и процесс всегда ожидает завершения всех своих потоков…

Все 4 кейса с поиском ошибок в коде решаются типами.

Люди плакали, кололись, но продолжали душить питона :)

есть mypy и pydantic

Все 4 кейса с поиском ошибок в коде решаются типами.

Именно, а один из этих типов и написал эту статью.

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

Что лучше использовать для бэкенда?

Опций не так много на сегодня: дотнет, спринг, го (не знаю их веб фреймворков). В некоторых случаях можно выбрать Раст.

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

По каким параметрам оценивали, что питон - лучшее?

По возможностям ХХИВП питон вне конкуренции. Готовых либ вагон, абстракции красивенькие

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

Что такое ХХИВП? Оно не гуглится ни на русском, ни на английском (HHIVP).

Классика же :)

Я — инженер, и моя голова
Сразу забывает бесполезные слова
Аджайл скрам? Фак ю, айм рашн!
Хуяк-хуяк — и в продакшен

https://youtube.com/watch?v=F2skk6RFFos

Спасибо.

Использовать методологию ХХИВП можно на любом языке.
Питон только даёт возможность разрабатывать быстрее и проще, за что его ценят в том числе и последователи ХХИВП, но далеко не они одни.


А то, что оно потом разваливаться начнёт

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

Ну, на вскидку:


  1. Простота разработки.
  2. Синтаксис, который очень хорошо подходит как раз для ясного описания бизнес-логики.
  3. Развитая экосистема библиотек и фреймворков.
  4. Доступность разработчиков.
  1. Очень субъективно.

  2. Очень субъективно.

  3. Как и у остальных топ 10 языков.

  4. Как и у остальных топ 10 языков (миллионы в каждом).

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

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

Ещё вспомнился случай. Понадобилось доработать свой же пакет через пару лет.. расширение функционала. Полез смотреть .. нашел странное место, явный косяк (какой дурак мог такое написать?!?). Так, ща поправим, кто-то полазил в моем коде.. поправил - сломалось. Ага, тогда так. Сломалось. Странно, кто-то явно тут что-то менял ещё, надо заглянуть в историю Гита.. Открываю историю, 3 коммита, ровно с теми же правками, но в обратном порядке. Последний коммит с комментом: "Работать будет только так! НЕ ТРОГАТЬ!" .. все мои же. И совсем недавний коммит, коллега чистил комменты, снес за компанию.

;)

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

коллега чистил комменты

Но зачем, Холмс?

У самурая нет цели, только путь.

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

Мы спросили об этом коллегу, но он от комментариев отказался *бадумтсс*

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

Часто помогает такой «взгляд со стороны»: написал, прошла неделька, и заходишь посмотреть свой код (главное комментировать, чтобы не забыть важных деталей 😁). Зачастую таким образом мне удавалось находить какие либо ошибки, или улучшить код.

Прошло много месяцев, и угадайте, кого они наняли, чтобы разрешить все конфликты и залить их ветку в мастер?

Но есть нюанс. Если бы не успел залить свою ветку в мастер, то пришлось бы делать то же самое, но, на секундочку, бесплатно. А тут — наняли, это все-таки разница.

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

Программа (решение задачи о прецессии тела сложной формы) была проверена и перепроверена многими часами и днями вдоль и поперёк, но никак не работала. Дело же оказалось в перемножении матриц. Было написано, что-то типа

C[i, j] =A[i, k]*B[k, j]

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

На днях два орла несколько дней пытались понять почему микроконтроллер не может связаться с устройством по интерфейсу HDQ (это такой однопроводной интерфейс от TI, если не ошибаюсь). Применяли осциллограф, настраивали пины разным способом, в какой-то момент вокруг стояли два сениора и два схемотехника. На четверый день Зоркий Джо заметил, что они подключили TX и RX наоборот от нарисованного на схеме. На вопрос "зачем" ответили, что думали что это TX и RX устройства, хотя на проводах было буквально написано к каким пинам МК их подключать.

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

Одновременно с предыдущим случаем мы выяснили, что в микроконтроллере GD32F130 (китайский клон STM) при передаче последнего байта через I2C генерируется несколько десятков прерываний с флагом "буфер передачи свободен". Вот эту фигню мы не знаем как побеждать, пока решили оставить как есть, благо в этот момент МК всё равно ждёт конца передачи по I2C. Я на 128% уверен, что это не должно так работать, но китайцы такие китайцы, я не очень удивился.

Сначала проект должен был быть на GD32E230, но выяснилось, что библиотека, которая идёт к этому контроллеру, вообще не собирается. Какие ещё чудесные открытия нас ждут...

p.s. Китайский же клон ST-LINK под названием, естественно, GD-LINK тупо роняет Keil, поэтому работать с ним невозможно. Импортозамещение оно такое, да.

Это чтобы вопросов не было 'а почему STM32 дороже в три раза?'

Байки сисадмина. Редакция 21 века.

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

Я такой процесс зову "Виртуальный голландский штурвал"

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

Вспомнилось тоже вот. Давным-давно, ещё на самой первой работе, делал небольшой рефакторинг плюсового кода. Даже не рефакторинг, а так, code-cleanup. И внезапно перестала вызываться виртуальная функция у наследника. Всё перепроверил, ну не может быть такого же! Вот - объект базового типа, сконструированный по типу наследника. Вот логика - тут виртуальные функции корректно вызываются от наследника, а вот именно эта одна - не вызывается, перекидывает в базовый класс. Оказалось, что в порыве энтузиазма я добавил в базовом классе этой функции модификатор const, а в наследниках - не добавил. Словом override я тогда ещё не приучен был пользоваться.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий