Нервный программист
123 subscribers
53 photos
1 video
1 file
30 links
Эмоционально об айти и программировании
Download Telegram
Итак, все кто проголосовал, те проголосовали 🤓правильный ответ: будет показана инфа об обеих ошибках, причем сначала будет показана инфа о shallow error. А вот deep error будет помечена как ошибка, которая случилась во время обработки shallow error.

During handling of the above exception, another exception occurred

При этом сама ошибка в блоке except с логгером будет именно deep error, но в логах все выглядит так, будто именно shallow error основная ошибка 🤡

Я столкнулся с этим поведением, когда решил, что мне нужно отлавливать все исключения в fastapi в одном месте. Там же я решил и логировать исключения. Я написал middleware с try except - все как завещал Deepseek. Каково же было мое удивление, когда в логах обнаружил сначала каких-то два внутренних эксепшна fastapi, а только потом уже мой эксепшн. И это типа норма 🤷 А в логах образовалась гора ненужного мусора 😢

А к чему я это все говорю? К тому, что стандартизация и консенсус важны в разработке. Они формируют ожидания и привычки, которые экономят мыслетопливо при работе, позволяют быстрее учиться. Хороший пример здесь async - await. Эта конструкция работает схожим образом, что в js, что в c#, что в kotlin, что в python. Детали могут отличаться (Promise vs Deferred vs Future), но принцип один.

Исключения в python, на мой взгляд, ломают "консенсус" исключений в ЯП. python не предлагает принципиально отличающийся механизм обработки ошибок. Это все те же исключения. Но при этом реализация будто не соответствует ожиданиям. И приходится переучиваться, что в моем возрасте уже чревато перенапряжением 😢
👍3👾1
Олд скулы свело
😁6🙈2
Написал еще одну небольшую статейку об обработке ошибок в Fastapi 😊

Стоит отметить, что подобные задачи возникают в других языках и микрофреймворках. Например, такой же вопрос можно поднять для expressjs, fastify, slim, open swoole и прочих. И обычно все решается либо кастомизацией обработчика, либо написанием middleware. Как и в Fastapi. А ответ чаще всего прямо написан в документации к фреймворкам.

Но отличие состоит в том, что в fastapi этого нет в документации python мы можем влиять на размер бэктрейса в логах. А раз так, то почему бы нам этим не воспользоваться. И я считаю это микро-концептуальным моментом 🤓 Можно бомбить на то, что бэктрейс в python ведет себя, как тварь. А можно обернуть это себе в пользу, почистив логи от ненужного мусора ☝️

Штош. На этом я пока что заканчиваю сезон python на канале. Надеюсь, было интересно 😘
👍51
Так блэт! 🤬

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

Во-вторых, я несколько дней писал статью! И пока я писал, я гуглил, спрашивал Deepseek и ChatGPT, экскрементировал эксперементировал!

И вот только что я чисто случайно!!! наткнулся на решение проблемы в документации. Я вообще попал на эту страницу по другому поводу. Я бы даже сказал, я не должен был туда попадать. Потому что эта страница находится в разделе How To - Recipes. А называется она Custom Request and APIRoute class.

Ну вот нахера бы мне вообще Custom Request писать, скажите пожалуйста. У меня обычная апишка. Смысл вообще мне туда заглядывать! И находится эта страница в какой-то жепе, уже после основной документации. Явно же факультативное чтиво! 😤

Но вот именно Custom APIRoute решает все проблемы! там даже сука пример есть с отловом ошибки, но он опять же в другом контексте показан. И написано рядом "лучше пользоваться exception hadnlerами, это чисто демонстрация".

Я вам клянусь, интернет не в курсе этого способа. Всех устраивает, что логи полны говна.

Буду править статью, но выбесився конечно😡 С другой стороны, раз никто об этом еще не писал, в этом может быть больше ценности. Главное все проверить еще раз, чтоб снова не обосраться!
😁321
дополнил, исправил. можно читать!
2
В связи с тем, что я тут немного подобосрался выше, возник вопрос "а нужно ли читать всю документацию от и до?" И как всегда в программировании не существует однозначного ответа на этот вопрос🤷

Проще всего парировать ответ "да, нужно". Вот допустим, какой-нить зарвавшийся фронтенд разработчик говорит вам, что необходимо читать ВСЕ. Так ты его сразу тогда и спроси "братик, а ты всю спецификацию ECMAScript прочитал?". ставлю очко, что этот фронтендщик ни разу ее не открывал даже (ну может пару раз, чисто, чтоб закрыть). Непонятно, как он код пишет вообще. А если в дискуссии участвует бэкендщик, то спроси его, читал ли он всю документацию по Postgres

Сложнее с теми, кто говорит, что читать всю документацию точно не нужно. Тут еще проблема в определении "всю". Практически в любой серьезной документации есть реально узкоспециализированные темы, которые даже душнилы пропускают. Но я бы определил слово всю в соответствии с принципом Парето - если прочитал более 80 процентов документации, то значит прочел "всю". И есть ситуации, когда такое необходимо. Например, наш любимый React вполне подходит сюда.

На сколько сильно стоит закапываться в документацию зависит от следующих факторов:
* Размера документации (маленькую доку можно всю просмотреть) и ее качества
* Задачи
* Опыта

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

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

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

Одна и та же документация, может быть прочтена по диагонали или полностью в зависимости от задачи. Если вы core contributor в ЯП, то вам скорее всего придется очень хорошо прочитать документацию по языку и не раз. Иначе вы даже не сможете составить адекватный RFC. Но если вы просто пользователь языка, то вам такой щепетильности не требуется.

В комментах делитесь, согласны или нет с такой позицией? читаете ли вы документацию всегда полностью?
👍5😁1
У меня не scalable microservices микромонолиты
😁6👍1
На днях просто сгорела жепа🤬 Все дело в том, что я послушал вот этот подкаст (только в аудио). Потом глянул, че там за гость и понял, что видел его уже в другом ролике, который не смог дослушать до конца.

Основной тезис гостя, Андрея Бреслава: скоро мы вообще перестанем писать прикладной код на привычных ЯП. Вместо этого, у нас будет обычный язык (английский), но мы будем писать требования в определенном формализованном виде. И вот это как раз будет наш НОВЫЙ язык программирования будщего!!! А "интерпретировать" наш язык будет, сюрприз-сюрприз, LLM.😱

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

Но при этом игнорируются фундаментальные различия между предыдущими шагами развития ЯП и подходом, который предлагает Андрей. Я предлагаю поговорить об этих различиях.
* Отсутствие детерминизма в работе LLM. На один и тот же input ты можешь получить кучу ouputов. Соответственно, нельзя относиться к такому ЯП как к исходникам.
* Продукт "транспиляции" такого ЯП - другой существующий высокоуровневый ЯП (js, python и тд). Предполагается возможность "поправить" результат прям там, если что-то пойдет не так. Это уже сразу признак херовой абстракции. Если у тебя сейчас что-то не так с байткодом, ты не пойдешь править байткод. Ты пойдешь править компилятор.
* Отсутствие понимания процесса отладки в таком ЯП. Как это могло бы выглядеть хотя бы в теории не знает сам гость.
* Как вообще валидировать результат? Тесты написать? Так их же тоже нагенерят. Непонятно, куда в таком процессе человеку в целом воткнуться.

Это я по самым верхам прошелся. Тут еще и вопросы, связанные с развитием "артефактных" ЯП вроде js и python, как обучать llm новым версиям, если они станут не нужны. Также вопрос по скорости работы и цене такого решения. А что насчет безопасности🤷

Гость натягивает свою аналогию ровно так, как это нужно натягивать, чтоб получить первый раунд инвестиций (о чем прямо говорит). И это бесит. Потому что нет здесь никакого визионерства, нет теории, нет ответов на вопросы. Есть просто желание попилить бабки.

А я вот тоже хочу позаниматься визионерством!

Когда я только начинал работать, то были такие IDE, как Netbeans и Eclipse (олды тут?). Там был бэйсик автокомплит без каких-то особых возможностей форматирования, рефакторинга и прочего. А до этого, вообще люди в блокноте писали, код, где даже такого не было. Затем ворвались Jetbrains с Idea. Они подняли удобство написания кода на новый уровень. Статический анализ, рефакторинг крупных модулей, более качественный атокомплит - это все появилось там и эволюционировало с течением времени. И другие IDE тоже старались не отставать в этом. Короче говоря, за 15 лет индустрии процесс кодирования существенно изменился, его удобство повысилось на порядки.

Мне кажется, настало время кардинально улучшить наши IDE. LLM позволит вывести их на НОВЫЙ уровень!

Как вам идея? думаете стоит к инвесторам с ней пойти, чтоб сделать IDE БУДУЩЕГО, где LLM будет заниматься автокомлитом и рефакторингом? Это будет РЕВОЛЮЦИЯ по аналогии с той, что Jetbrains произвели в своей время.

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

В заключении хотелось бы сказать, что аналогия может быть хороша для объяснения явления. Но никакой аналогии недостаточно для принятия решения☝️
33👍2
Отключил динамическую типизацию ночью - прод упал! 😢
4
Кстати про динамическую типизацию. Возникла тут задачка у меня одна. На вход приходит строка, которая может содержать int или float. Например, "4" или "3.14". И надо сконвертировать эту строку либо в int, либо во float соответственно. А пишу я код на php. И я че то забыл с этими питонами и (особенно) jsами, на сколько пхп хорош в своем приведении типов. Этот код

<?php

$a = '4';
$b = '3.14';

function test(int|float $t): void {
var_dump($t);
}

test($a);
test($b);


выдаст корректные

int(4)
float(3.14)


Если на вход придет неприводимая строка, то php выбросит TypeError, который можно поймать.

Обычно в коде я не использую strict_types, за что многие могут меня поругать. Поэтому код выше работает без проблем. Но на самом деле статический анализатор и так требует от меня соответствие типов, а там, где он выключен намеренно, приведение типов в php только радует. Но все же, что делать, если strict_types включен или анализатор ругается. Даже в этом случае php остается верен своим корням. Магический + 0 сделает свое дело.

<?php
declare(strict_types=1);

$a = '4';
$b = '3.14';

function test(int|float $t): void {
var_dump($t);
}

test($a + 0);
test($b + 0);


"Но это же кринге!" скажете вы. А я прошепчу вам на ушко в ответ "Покажи писькуЭто строгая динамическая типизация, братик. А кринге - это тип number в js 🤡". И мы разойдемся так и не узнав, что в голэнг придется ввести структуру с 4мя полями, чтоб решить эту задачу🥲
😁32
Приехал в Иннополис. Вижу возможность открытия школы golang.
3🔥2😁2
Послушал тут подкаст с Егором Бугаенко. Много что мог бы сказать по этому поводу, и люди в комментах там активно это делают. Но вместо этого, я бы хотел зацепиться за один конкретный кейс, который привлек мое внимание прям сразу во время прослушивания. Его кстати даже не Егор ввел, а ведущий Кирилл.

Говоря про концепцию null в ЯП, и ее плохость, Кирилл предложил пример с объектом юзер, который нужен в куче мест в коде. И по его мнению, проверки типа if user === null появляются повсеместно, что сильно огорчает. Поэтому Кирилл предлагает всегда иметь инстанцированный объект user, и в случае если пользователь не аутентифицрован, это будет какой-то guest user с пустыми значениями.

И я тут че-то не совсем согласен с Кириллом. Хотя я знаю, что такая концепция с дефолтным юзером много где используется.

Поскольку на бэкенде все завязано на циклах запрос-ответ, то возможные варианты развития событий можно классифицировать по типам ендпоинтов
* ендпоинты с обязательной аутентификацией
* ендпоинты без аутентификации
* ендпоинты с опциональной аутентификацией
Не благодарите вашего капитана🤡

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

Тем не менее, для 3й группы ендпоинтов мы обсуждаем 2 варианта, как действовать.
* Мы передаем default user и притворяемся, что у нас всегда "все хорошо"
* Мы передаем null, и тогда нам нужны проверки на null, которые так бесят Кирилла

И на эти два варианта я бы посмотрел не с точки "лучше-хуже", а с точки зрения цикломатической сложности и абстракции. Если у вас цикломатическая сложность вытарчивает прямо в ендпоинтском коде, т.е. у вас путь выполнения кардинально отличается в зависимости от того, аноним юзер или нет, то if-ов вам не избежать. И какая разница, будет это if user === null или if user.id === 0. Для читающего код, это примерно одинаковая когнитивная нагрузка.

Если же у вас цикломатическая сложность не видна сразу, то, кажется что default user - лучший вариант. Реально, избавляемся от чертовых if-ов прям тут. Ура! 🥳

Но в программировании ничего не бывает бесплатно. Так чем же мы платим тут? 🤔

А платим мы тут подтекающей, как старая жопа, абстракцией. Проблема в том, что авторизованный юзер и неавторизованный юзер в реальности не эквивалентны! Вот есть у вас метод public save(User user): void у репозитория. И как только вы завели в вашей системе дефолтного юзера, готовы ли вы поставить очко, что дефолтный юзер никогда не попадет в этот метод?

Обратите внимание, что это не про "злой null". Вместо null здесь мог бы быть тип AnonymousUser и юнион тип. Это просто очередной вопрос компромиссов, повсеместно встречающийся в разработке.

Че думаете? Как у вас с юзерами на проектах?
3👍1
This media is not supported in your browser
VIEW IN TELEGRAM
К недавнему посту о чтении доки)
😁5👍1🤣1
сук, я не хотел вообще этот пост писать. Наоборот, у меня контент по плану было кое-что позитивное. Но у меня щас просто разбомбило сракатан в пух и прах!😡

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

Вот у меня есть generic класс

class Result[T]:
pass

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

def transform(self, transformer):
data = transformer(self.__data)
return Result(data)


Давайте поможем Даше найти, как расставить типы в объявлении метода!

ну что вы напишите в этом случае? ну наверное что-то типа

def transform[V](self, transformer: Callable[[str], V]) -> Result[V]:
pass

Обратите внимание, как же сука говяно описываются колаблы. Ну это ладно. Этот код впринципи некорректный!

А знаете какой код корректный? Вот такой

def transform[V](self, transformer: Callable[[str], V]) -> "Result[V]":
pass


Задача: найти два отличия.

Ответ: левая кавычка и правая кавычка.

Причем, сука задаешь этот вопрос дипсику и perplexity, так он тебе старый способ объявления дженериков в ответ ставит, который еще более уебский с пежней типа


T = TypeVar('T')

class Result(Generic[T]):
pass


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

Извинити, просто реал бомбануло
👍4😁4
Тут на меня навалилось всякое, и я что-то не нашел времени отметить знаменательное событие. Ведь на прошлой неделе самому умирающему языку программирования исполнилось 30 лет🥳 с днем рождения php!

Какой бы топ ЯП вы не взяли, php стабильно где-то в жепе! Наверное трудно найти какой-то другой язык, который хейтили бы больше, но который также активно использовался бы в продакшне. А между тем множество из хейтеров вкатывались в АЙТИ именно через php. Но ощущение такое, что php развивался, а хейтеры нет. Ведь за последнюю декаду в языке появилось столько всего, что удобство разработки и качество кода перешло на новый уровень. Перечислю список новых фич за последние 10 лет, которые, на мой взгляд, оказали наибольшее влияние на процесс разработки:
* строгая типизация в рантайме
* enums
* JIT компиляция
* атрибуты
* стрелочные функции
* first-class callable
* readonly свойства и классы, проперти хуки
* распаковка массивов, деструктурирующее присваивание (прям как в этом вашем js)
* файберы

И это я перечислил только то, что мне пришло на ум, и то, чем я пользуюсь. Там еще до хера всего. И вы можете сказать "но постой, это есть во всех других современных языках". Так в этом и соль, что php - это нормис. Это хороший современный язык! И он отлично справляется с работой, для которой он и создавался, а именно веб приложения. Его "умирающая" модель исполнения идеально подходит для веба, где большая часть взаимодействия построена по принципу request-response.

Кстати про умирающую модель. И здесь у пхп большие подвижки. На момент написания этого поста у пхп есть уже 2 продакшн реди решения (roadrunner и frankenphp), где используется воркерная модель, когда php процессы живут "вечно". А еще есть асинхронные библиотеки openswoole и reactphp. Так что даже в этом плане php такой же нормис.

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

Короче говоря за 10 лет php проделал огромный путь. У меня ощущение, что php 5 и php 8 это два разных языка - уж на столько их возможности отличаются. Будем надеяться, что следующая декада пройдет не менее активно. Нам тут уже обещают дженерики и async-await, но в целом, я был бы рад если бы в язык встроили дебаггер 🤡

Короче говоря, не умер и ладно!
🔥53
Посчу шутки про AI, пока он меня не заменил😅
😁71
Как не зайдешь в эти ваши интернеты, везде одни истории успеха😔Один навайбкодил стартап, другой выкатил обнову для своей библиотеки, третий на подкаст пришел и рассказал, какой он крутой. И никто сука не рассказывает о факапах и тупняках. Поэтому об этом буду рассказывать я😊

На работе делаем супер продвинутого ИИ помощника (на питоне, разумеется). Как истинный хороший программист, я решил внедрить статусное программирование (спасибо Кириллу Мокевнину с его докладами). Это означает, что сессия пользователя может находиться только в одном из доступных состояний. Например, есть состояние awaiting, когда помощник готов принимать запрос, или processing, когда происходит обработка запроса пользователя. Переходы между состояниями запрограммированы через finite state machine (fsm).

Однако в какой-то момент потребовалось научиться "ждать" определенного перехода между состояниями. Например, приходит запрос на создание новой сессии, но предыдущая сессия находится в состоянии summarizing, когда llm подгатавливает саммари по ней. И надо дождаться, когда состояние станет finished, прежде, чем продолжать операцию.

И вот трое нас, я, чатгпт и дипсик, родили совершенно потрясающее решение, основанное на Lockах и Eventах. Я даже тесты написал!🥳

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

И тут бы мне остановиться придумывать херню, ведь можно сделать классический поллинг - просто периодически долбить базу с запросом "ну че там, статус поменялся?". Но нет, случился очередной ступор мозговины. Ведь мне нужно КРАСИВОЕ решение.

И тут начинается полезная инфа для вас. Если вы работаете с postgres, то подобную задачу можно решить используя механизм LISTEN / NOTIFY. То есть в одной сессии подпиcываетесь на определенный ивент с помощью команды LISTEN event, а в другой сессии можно уведомить подписоту с помощью NOTIFY event. Еще payload можно передать. Ну круто же!

Но есть одна сложность. Допустим вы прочитали значение из бд, оно вам не понравилось, и вы хотите подождать другого. Если вы просто оформляете подписку после чтения, значение уже может измениться в этот момент. Это классическая гонка данных. Поэтому и listen, и notify важно делать в транзакциях с локами. В частности в транзакции, где у вас notify, нужно сделать select ... for update, а там, где listen должно быть select ... for share.

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

Вывод: не выпендривайтесь, делайте просто! 👆

Из бонусов, я кайфанул от того, что написал тесты, а затем полностью переписал реализацию, а тесты почти не трогал! Почти оргазм! 🥹
💅7