Детектив kesn всегда готов помочь!
Детектив kesn всегда готов помочь!

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

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


❯ Функция не выполняется

Попросил меня как-то клиент отладить его скрипт. Говорит, не работает. Невероятно!

Я, когда клиент говорит, что ничего не работает
Я, когда клиент говорит, что ничего не работает

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

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

Смотрим в содержимое функции. Как и положено, это полотно кода на пару экранов, сходу так и не поймёшь, что она делает. Повсюду return что-то там, ветвления всякие итд. То глупое чувство, когда клиент тебя ждёт, а ты ничо не понимаешь и косплеишь рыбу.

Осложнялось всё тем, что отлаживать через клиента — ну такое. Он может запустить скрипт, но вот отладчик для него - страшное слово, и максимум, на что можно рассчитывать — это поставить print() в нужных местах. Разгадка оказалась проста: где-то в середине функции, там, где это было менее всего заметно, вместо return клиент написал yield. А в питоне yield — это магическое слово, которое превращает функцию в генератор, а все return ... - в как бы raise StopIteration(...), и вместо результата возвращается итератор, и выполнение кода останавливается до следующего обращения... Короче говоря, всего-навсего одним ключевым словом клиент полностью раздолбал логику своей программы. Маэстро!

❯ Как ловить эксепшн из генератора

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

Вот, например, кусок кода:

try:
	quota_chunks = quota_cache.apply(quota_chunks)
except InconsistentQuotaCache:
	log.error('Something went wrong')
	raise

first_quota_chunks, quota_chunks = spy(quota_chunks, 1)  # more_itertools.spy, кстати, хорош - загуглите!

Тут у меня есть функция cache.apply(), которая берет quota_chunks, делает с ними какой-то вжух-вжух и возвращает новые quota_chunks. Я нарисовал диаграмму, чтобы изобразить этот процесс в более понятной форме.

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

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

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

Именно это тут и случилось. Я вызвал этот генератор и проверил, что он отработал без ошибок, но на самом деле генератор отработал совершенно в другом месте — там, где вызывается spy() — и именно там он и упал.

А знаете как я это отловил? В тестах. Поэтому пишите тесты.

❯ Строго по инструкции

Клиенты бывают разные: какие-то умеют немножко в HTML и frontend, а некоторые из наших клиентов умеют в backend. Один из таких клиентов часто сам писал backend логику и давал нам её на проверку, чтобы мы ему исправили баги, а может быть где-то сделали рефакторинг или code review.

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

И вот клиент взял нашу инструкцию и начал следовать тому, что там написано, слово в слово. Надо понимать, что разработчики обычно пытаются понять, что они делают (по крайней мере я на это надеюсь). Соответственно, те, кто читал этот скрипт, понимали, что должно быть сделано, и в случае, если у них, например, вместо pip используется poetry, а вместо apt-get у них pacman (i use arch btw), то они заменяли соответствующие команды.

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

«Изи фикс» — подумал я, созвонился с клиентом объяснил, что нужно делать, и хотел отключаться... когда возникла ещё одна ошибка. Оказывается, в инструкции был косяк, и при выполнении команды шелл делал подстановку, когда видел $SOMETHING — то есть не было экранирования. Мы исправили и это, и буквально через несколько секунд всплыл ещё один косяк. А потом ещё. И ещё.

Где-то через час я сказал клиенту, что пусть он всё бросит и я задеплою всё сам, а потом мы обновим ридми. Было стыдно.

❯ Ответочка

Когда-то я работал на интернет-магазин, и мы заметили, что у нас появляются фейковые заказы каждые утро и вечер. Сначала мы не смекнули, что к чему, но потом поняли фишку: идентификаторы заказа у нас были обычные IDшки из Postgres, поэтому конкурент мог сделать заказ утром (номер заказа 10), сделать заказ вечером (номер заказа 15) и просто вычесть второй номер заказа из первого и получить количество заказов, которые мы получили за день (15 - 10 = 5). Я до сих пор часто нахожу эту ошибку во многих проектах, и примерно могу оценить размер этих проектов.

Эту ошибку легко исправить: достаточно заменить последовательные ID на случайные — например, вместо номера заказа использовать timestamp или UUID.

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

Мой девиз — «кто ищет тот всегда найдёт» (посмотрите мои статьи про уязвимости на хабре - 1, 2). Так и тут, я искал и обнаружил, что конкурент выкладывает розничные прайсы публично, а вот оптовые — только для зарегистрированных и проверенных партнёров. Сам файл он раздаёт nginx'ом с адреса вроде http://some-site.com/files/розничный_прайс.xls. А если так, то, скорее всего, никакой аутентификации при помощи бэкенда для самого файла нет, а значит, можно попробовать найти оптовый прайс.

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

❯ Скрапинг со скоростью света

В одном из моих проектов я использовал api ВКонтакте, чтобы анализировать кожаные мешки. Там не нужна была супер-скорость, поэтому я не полез в async, а просто написал функцию и распараллелил её по потокам при помощи ThreadPoolExecutor.

Программа начала просто летать! Вот как это делают сеньоры! Саенс, бич!

Слева направо: саенс, бич
Слева направо: саенс, бич

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

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

❯ Бог рефакторинга

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

Ну я такой про себя «опять клиент что-то сломал, бывает», полез туда смотреть. Глядел-глядел, глаз вообще ни за что не цеплялся. Ошибок в sentry не было. Потом нашёл вот такой код:

class StripeWebhookView(View):
    @csrf_exempt
    def post(request, *args, **kwargs):

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

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

И вот я смотрю, а этот код написал... я сам. Вот так я примерно выглядел:

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

❯ Детектив kesn и тайна ssh

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

Я человек простой, мне сказали задеплоить — я и задеплою, хоть на AWS, хоть на тапок.

Запускаю я скрипт, он всё делает, и теперь я хочу зайти на сервер и вручную проверить, что всё работает. И тут всё заверте...

Сначала пробую ssh -i ключ root@ip. Не работает. Потом вспоминаю, что юзер в AWS обычно ec2-user, поэтому пробую ssh -i key ec2-user@ip. Не работает. Может, там авторизация не по ключу? Пробую ssh ec2-user@ip. Не работает. Сделал dig, попробовал подключиться не напрямую, а через load balancer. Согласен, тупая затея.

Пошел в дэшборд AWS смотреть настройки файрволла. Вижу два странных айпишника. Очень странно. Беру первый, проверяю геолокацию по ip. По локации понимаю, что это, кажется, статический ip девопса. Какого хрена? У нас же есть бастион, и все соединения должны проходить через него... Проверяю второй ip из файрволла. О, так это же и есть бастион. Ну отлично, теперь делов-то — добавить всю эту конфигурацию с бастионом в .ssh/config, чтобы в будущем было легко подключиться. Лезу в конфиг, а там уже есть эта конфигурация.

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

❯ Ошибка платежа

На sentry прилетел отчёт об ошибке, попросили посмотреть. Стал разбираться. Мой код двухгодичной давности.

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

Единственное, что я не учел — что клиент может просто всё забросить, ничего не отменяя. И вот на протяжении года наш сельдерей-разнорабочий (celery worker) запускался, пытался списать у клиента деньги, получал отлуп, жаловался в sentry, и засыпал, чтобы назватра всё повторилось, и так каждый день, без конца и края.

❯ Лёгким движением руки сэкономить кучу денег

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

  • Чувак хостил видео на aws s3 и раздавал через амазоновский CDN. Выходило $655 в месяц. Потом нашёл BunnyCDN, я перенастроил приложение (заменил где-то 4 строчки минуты за две), и внезапно с новым CDN в месяц стало уходить только $70. Ну не эпично ли за пару строчек кода?

  • Клиент платил сотни долларов за жирный инстанс Elasticsearch на AWS. Почему — я хз. Потом он заподозрил неладное. Мы замерили реальную нагрузку и перенесли Elastic на одну из самых дешёвых машин в digital ocean, за которую клиент теперь платит $24 в месяц. Профит!

  • У клиента было много файлов на s3, платил он тоже много. Потом перенесли всё на b2, там даже делать почти ничего не надо — у них интерфейс совместим с s3. Получили экономию раза в 4.

❯ От судьбы не уйдёшь

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

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

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

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

Ага, меня.

❯ Детектив kesn и поиски пароля

Настраивал я как-то инстанс elasticsearch. Там была отдельная машина, я на ней с помощью docker разворачивал ElasticSearch. Сначала делал всё в ручном режиме, проверял, потом писал скрипт для автоматизации. Для начала просто запустил сервер без всего, потом начал разбираться с авторизацией.

В эластике есть специальный скрипт — elasticsearch-setup-passwords — он настраивает пароли. Ну я его запустил, он мне выдал списки паролей для apm_system, kibana_system, kibana, logstash_system, beats_system, remote_monitoring_user и, собсна, elastic. И хотя мне показалось, что паролей было слишком мало и вообще-то для приличной поисковой системы их должна быть хотя бы сотня, но пароль для elastic был, я его забил в систему автоматизации и пошёл дальше настраивать. Дальше было SSL — не знаю, почему это не встроено (наверно, потому что если не будет https, то и взламывать elastic будет сложнее, а куда без этого!). Ну я пошёл в гугол и говорю: пацаны, сертификаты для эластика привезли? Когда я заикнулся про letsencrypt, они мне сказали, что у нас тут не загнивающий запад и мы сами сертификаты делаем, свои собственные. Короче, прям на официальной странице лежит огроменный docker-compose.yml, в котором вжух-вжух, сертификаты настраиваются, конфиги генерируются. Я его скопировал, применил, всё заработало, и я добавил это в автоматизацию.

Через несколько дней (когда я ужё наполовину всё забыл) мне вдруг понадобилось всё снести и настроить заново (спасибо, digital ocean, за то, что не умеешь даунскейлить диски!). Я запустил скрипт автоматизации, всё развернулось, и тут я вспомнил, что вроде как пароль генерируется сам и его можно узнать, если запустить elasticsearch-setup-passwords. Ну я полез на машину, чтобы запустить эту команду — а она не работает! Сначала был не тот url инстанса, пришлось узнать, что есть опция --url. Окей, теперь не хочет подключаться, т.к. кастомные сертификаты. Как добавить сертификаты? Прописать их в elasticsearch.yml. Читаю доки и там говорится:

All of these settings can be added to the elasticsearch.yml configuration file, ...

ура! так просто!

... with the exception of the secure settings

ffuuuuuuuuu...

which you add to the Elasticsearch keystore. For more information about creating and updating the Elasticsearch keystore, see Secure settings

Ну я полез читать, что за Elasticsearch keystore и зачем он нужен, и даже прочитал про bootstrap password и keystore passphrase. Мне показалось, что ещё чуть-чуть, и я дойду до чтения про большой взрыв и основы зарождения вселенной, а ведь я просто хотел узнать пароль от эластика!

Тут я бросаю взгяд на docker-compose.yml, и вижу, что там везде мелькает $ELASTIC_PASSWORD, и оказывается всё это время пароль был у меня в настройках и я сам его задавал!

Сказочный... эээ... патруль!

❯ Как дропнуть продакшен-базу

Клиенты любят нанимать фрилансеров или брать сотрудников в штаты, чтобы они работали над фичами — потому что нанимать нашу компанию достаточно накладно >:)

Ну и вот как-то клиент нанял стороннего разработчика, чтобы он перенёс систему поиска с эластика на postgres full-text search. Он сделал это именно так, как делал я лет 7 назад. Следите за руками:

  • Огромная ветка с кучей коммитов

  • В коммитах смешались изменения в БД и рефакторинг логики нескольких почти не связанных приложений

  • Миграции не откатывались

  • Тестов не было

  • Бэкапов перед деплоем сделано не было (хотя это одна команда)

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

  • Не было оговорено временное окно для безопасного деплоя

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

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

Я откатил git revision на сервере на рабочий коммит, а СЕО зашёл в админку Digital Ocean и восстановил снэпшот базы данных, назвав его production-db-backup-Mar-24. Всё запустилось. Из-за использования снэпшота мы потеряли немного новых данных, но ничего критичного.

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

Через много месяцев (да, много историй именно после этого и начинаются) клиент говорит: а чё это за production-backup-Mar-24, давайте её удалим. Как же здорово, что он спросил у нас. Потому что программист клиента на самом деле ничего не починил, а просто свалил в закат, и вся инфра осталась в этом «пофикшенном» состоянии. И база использовалась резервная. Поэтому удалять нужно было сломанную БД с названием production, а рабочей была именно production-backup-Mar-24.

Такие дела.

❯ Детектив kesn и загадочные тормоза

Серьёзно, я уже подумываю написать книгу про похождения детектива kesn'а.

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

Я кэшировал функции, пропускал ненужные фрагменты данных, уменьшал циклы. Сначала стало быстрее, но потом чем больше я отлаживал, тем медленнее код работал. Может, мой рефакторинг упустил какую-то важную деталь, и поэтому я делаю что-то совсем не то? Я начал логгировать и отлаживать даже самые маленькие функции. В конце концов дошло до того, что я, кажется, всерьёз начал задумываться об оптимизации скорости словарей в питоне (sic!), и в то же время моя версия работала медленнее, чем оригинальный код.

Потом до меня допёрло.

Чем больше я добавлял отладочной инфы, тем больше был оверхед. То есть я делал программу быстрее, но отладочная инфа делала программу медленнее.

Ха-ха. Я выключил отладочную инфу, и всё залетало. Ну и дурак!


Если вам понравилась эта статья, то посмотрите вот эту, она тоже весёлая: Погромист. Мои самые эпичные провалы за всю карьеру.

Если вам понравился я лично, как умная и образованная гиена, то вот моя тележка: Блог погромиста

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

Поэтому если вам нужен недорогой сервер в России — кликайте по картине ниже. Пацаны ваще ребята!

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


  1. Samhuawei
    00.00.0000 00:00
    +5

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


    1. twid
      00.00.0000 00:00
      +1

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


    1. iRumba
      00.00.0000 00:00
      +3

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


  1. VladimirFarshatov
    00.00.0000 00:00
    +13

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

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

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

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

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

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

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


    1. pda0
      00.00.0000 00:00
      +17

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


  1. a-burlakov
    00.00.0000 00:00
    +22

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

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


  1. Taraflex
    00.00.0000 00:00

    del


  1. Daddy_Cool
    00.00.0000 00:00
    +4

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

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


    1. Panov_Alexey
      00.00.0000 00:00
      +2

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


      1. BugM
        00.00.0000 00:00

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


        1. MountainGoat
          00.00.0000 00:00

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

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


      1. blind_oracle
        00.00.0000 00:00

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


    1. lgorSL
      00.00.0000 00:00

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


      1. mayorovp
        00.00.0000 00:00
        -1

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


    1. grayrat
      00.00.0000 00:00
      +1

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


  1. PrinceKorwin
    00.00.0000 00:00
    +77

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

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

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

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

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


    1. iBljad
      00.00.0000 00:00

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

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


  1. qbertych
    00.00.0000 00:00
    +1

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

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


    1. kesn Автор
      00.00.0000 00:00
      +2

      Если вы читаете результат, то 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())
      
      


      1. mayorovp
        00.00.0000 00:00
        +1

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


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


  1. leotsarev
    00.00.0000 00:00
    +12

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


    1. blind_oracle
      00.00.0000 00:00
      +2

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


    1. danilovmy
      00.00.0000 00:00
      +18

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

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


  1. Ogoun
    00.00.0000 00:00
    +10

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

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


  1. Dr_Faksov
    00.00.0000 00:00

    del


  1. VladimirFarshatov
    00.00.0000 00:00
    +3

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

    ;)

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


    1. iig
      00.00.0000 00:00
      +2

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

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


      1. aleksandy
        00.00.0000 00:00
        +2

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


      1. VladimirFarshatov
        00.00.0000 00:00

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


  1. Sharikov-T
    00.00.0000 00:00
    +1

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

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


  1. RomeoGolf
    00.00.0000 00:00
    +7

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

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


  1. Tyusha
    00.00.0000 00:00
    +16

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

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

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

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


  1. DrGluck07
    00.00.0000 00:00
    +7

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

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


  1. DrGluck07
    00.00.0000 00:00
    +1

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

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

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


    1. blind_oracle
      00.00.0000 00:00
      +2

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