На чем программируете? (Можно выбрать несколько)
Anonymous Poll
20%
HTML и CSS 😅
42%
JavaScript, TypeScript
29%
Python, Ruby, PHP
28%
Java, Kotlin, C#
6%
Objective C, Swift
20%
C, C++, Rust, Go
6%
Что-то функциональное / другое
7%
Не программирую
5%
Смотрю результаты
🔥5
Поразил один респондент 🤯
Мы сейчас проводим исследование о том, как, когда и почему люди покупают курсы. В более широком смысле, как, когда, чему и зачем учатся.
Плюс-минус по JTBD (jobs to be done, есть такой фреймворк для описания проблем и их решений в формате "работ"), но, конечно, направить диалог в правильное русло не всегда удаётся. Да и, наверное, в общем случае не нужно. Нужно понять людей, их ситуации, чувства, мысли - а потом решения и результаты.
Меня поразил один респондент. Парень, хорошо за 30, взялся изучать PHP фактически с нуля и - внимание! - сам решил брать от курса всё: выполнял все практические задания "со звёздочкой", не жалея времени и не оглядываясь на продолжительность курса. Интуитивно или с чьей-то подсказкой, он решил набивать руку, решая много однотипных задач.
Когда на собеседовании ему не удалось правильно ответить/решить что-то по SQL, он не побежал "читать теорию", а нашёл тренажёр и написал какое-то количество запросов от простого к сложному. Набил руку, выработал привычку, развил мышечную память - и лучше разобрался в теме.
Это чертовски правильный подход! 🔥
Без практики знания - не знания. Я всю жизнь "знал" SQL, но, пока не начал писать его почти каждый день, эта информация просто дремала где-то на задворках подсознания. И, конечно же, была практически бесполезной. Когда мне нужно было написать какой-то запрос в несколько таблиц, что-то хитро сгруппировать или поджойнить, сделать даже несложный отчет, я просто бежал в гугл.
А вот несколько месяцев активной практики, когда я целенаправленно хотел набить руку в SQL, очень помогли. Хотя, конечно, я бы в идеале и впрямь условный SQLite написал с поддержкой какого-то подмножества ANSI SQL, чтобы понять, как это работает.
По теме: вот, например, классная бесплатная книжка по SQL для совсем начинающих и куда более интересная платная.
Возвращаясь к нашему респонденту, сразу заметны его результаты и уровень. Со своим практически нулевым опытом человек реально умеет писать код, делать несложные веб-сервисы, ориентироваться в сложных для новичка концепциях и абстракциях. Главное, что он уже умеет решить проблему самостоятельно, когда её понимает.
Пятисотил у него сервак, а дебаг-логи в настройках php были отключены. Он сходу не разобрался и не понял, куда копать, но после моей подсказки про уровень логирования разобрался, сам нашёл нужную настройку и включил. Не сходу пофиксил, конечно, но и не застрял насовсем. Не отвалился в прокрастинацию.
Я даже не понимаю, что это за чудо такое: в дополнение ко всему, он рассказал, что ему очень нравится бэкенд и заходит писать логику, "я бы API-шки делал и горя не знал". Хотя обычно, и это как будто бы предсказуемо и очевидно, начинающих больше привлекает фронтенд, ведь там сразу видно результат. Когда ты ещё не привык к абстрактному мышлению (а это лежит в самой сути программирования), фронтенд даётся проще. Первые шаги и результаты так точно.
В общем, на мой взгляд, парень этот с таким подходом просто обречён на профессиональный успех.
А вы проходили онлайн-курсы?
У нас уже больше 70 ответов в форме, но нужно больше тех, кто уже работает и продолжает повышать свой профессиональный уровень. Заполняйте анкету - я выберу ещё минимум человек 10 для интервью, в конце которого разберу любой запрос и дам устную консультацию по теме с письменной рекомендацией 🔥
Мы сейчас проводим исследование о том, как, когда и почему люди покупают курсы. В более широком смысле, как, когда, чему и зачем учатся.
Плюс-минус по JTBD (jobs to be done, есть такой фреймворк для описания проблем и их решений в формате "работ"), но, конечно, направить диалог в правильное русло не всегда удаётся. Да и, наверное, в общем случае не нужно. Нужно понять людей, их ситуации, чувства, мысли - а потом решения и результаты.
Меня поразил один респондент. Парень, хорошо за 30, взялся изучать PHP фактически с нуля и - внимание! - сам решил брать от курса всё: выполнял все практические задания "со звёздочкой", не жалея времени и не оглядываясь на продолжительность курса. Интуитивно или с чьей-то подсказкой, он решил набивать руку, решая много однотипных задач.
Когда на собеседовании ему не удалось правильно ответить/решить что-то по SQL, он не побежал "читать теорию", а нашёл тренажёр и написал какое-то количество запросов от простого к сложному. Набил руку, выработал привычку, развил мышечную память - и лучше разобрался в теме.
Это чертовски правильный подход! 🔥
Без практики знания - не знания. Я всю жизнь "знал" SQL, но, пока не начал писать его почти каждый день, эта информация просто дремала где-то на задворках подсознания. И, конечно же, была практически бесполезной. Когда мне нужно было написать какой-то запрос в несколько таблиц, что-то хитро сгруппировать или поджойнить, сделать даже несложный отчет, я просто бежал в гугл.
А вот несколько месяцев активной практики, когда я целенаправленно хотел набить руку в SQL, очень помогли. Хотя, конечно, я бы в идеале и впрямь условный SQLite написал с поддержкой какого-то подмножества ANSI SQL, чтобы понять, как это работает.
По теме: вот, например, классная бесплатная книжка по SQL для совсем начинающих и куда более интересная платная.
Возвращаясь к нашему респонденту, сразу заметны его результаты и уровень. Со своим практически нулевым опытом человек реально умеет писать код, делать несложные веб-сервисы, ориентироваться в сложных для новичка концепциях и абстракциях. Главное, что он уже умеет решить проблему самостоятельно, когда её понимает.
Пятисотил у него сервак, а дебаг-логи в настройках php были отключены. Он сходу не разобрался и не понял, куда копать, но после моей подсказки про уровень логирования разобрался, сам нашёл нужную настройку и включил. Не сходу пофиксил, конечно, но и не застрял насовсем. Не отвалился в прокрастинацию.
Я даже не понимаю, что это за чудо такое: в дополнение ко всему, он рассказал, что ему очень нравится бэкенд и заходит писать логику, "я бы API-шки делал и горя не знал". Хотя обычно, и это как будто бы предсказуемо и очевидно, начинающих больше привлекает фронтенд, ведь там сразу видно результат. Когда ты ещё не привык к абстрактному мышлению (а это лежит в самой сути программирования), фронтенд даётся проще. Первые шаги и результаты так точно.
В общем, на мой взгляд, парень этот с таким подходом просто обречён на профессиональный успех.
А вы проходили онлайн-курсы?
У нас уже больше 70 ответов в форме, но нужно больше тех, кто уже работает и продолжает повышать свой профессиональный уровень. Заполняйте анкету - я выберу ещё минимум человек 10 для интервью, в конце которого разберу любой запрос и дам устную консультацию по теме с письменной рекомендацией 🔥
👍11🔥3❤1🤔1🤪1
Посты с начала мая
1️⃣ Добавленная ценность
Нужен ли код и программисты в нетехнологическом продукте? А если подумать?
2️⃣ Программист на свободе
Работать, чтобы жить, жить, чтобы работать - или вообще не работать?
3️⃣ Как я по сишке упарывался
Помогут ли десятки собранных segfault вернуть интерес к работе?
4️⃣ Поразил один респондент
Разве на онлайн-курсах программирования реально можно научиться программировать?
А между ними есть ещё опросы. Кто голосует - молодец! 🥳
1️⃣ Добавленная ценность
Нужен ли код и программисты в нетехнологическом продукте? А если подумать?
2️⃣ Программист на свободе
Работать, чтобы жить, жить, чтобы работать - или вообще не работать?
3️⃣ Как я по сишке упарывался
Помогут ли десятки собранных segfault вернуть интерес к работе?
4️⃣ Поразил один респондент
Разве на онлайн-курсах программирования реально можно научиться программировать?
А между ними есть ещё опросы. Кто голосует - молодец! 🥳
👍8
Сложность
У нас в боте для Стильного клуба есть много пока ещё отсутствующих фич. И, конечно же, даже такая маленькая и простая система за такой короткий срок (первый из 277 комитов я сделал в начале марта) уже успела порасти мхом техдолга 🫣
Я то старательно игнорирую этот факт, как настоящий стартапер, то, как программист-перфекционист, принимаюсь всё тщательно переделывать, выдумывая новые и переделывая существующие абстракции.
Есть, например, BaseFlowHandler. Это такой базовый класс, потомкам которого передаётся управление для обработки пользовательских сценариев. Например, сценарий регистрации или сценарий покупки. Их можно комбинировать, и за это отвечает PrivateFlowController, который знает, как реагировать на сообщение или колбэк от пользователя в каждый (ну, почти каждый) момент.
Тут мне понадобилось сделать так, чтобы админы могли делать посты в канал/чат через бота. В основном для того, чтобы добавить им красивые кнопки (до сих пор не понимаю, почему в обычных клиентах телеги нет этой функции), но ещё и для настройки поведения репостов, пинов и т.п.
Помучив это дело несколько дней и написав приблизительно половину кода, я упёрся в свои же абстракции. Все эти базовые классы и контроллеры я делал для того, чтобы обрабатывать один, непрерывный по сути пользовательский сценарий. Там они работают. А вот для публикации множества сообщений, каждое из которых можно отдельно настроить в любой момент, уже нет.
И настолько мне не захотелось всё это добро переписывать, что я просто всё это выкинул и забил 🤪И решил сделать по-другому, в 10 раз проще и во столько же раз быстрее. По-стартаперски. Конечно, настолько же гибко и удобно, как могло бы быть в теории, не получится, но зато наверняка получится проще - а от этого ещё лучше.
Плюс я быстрее освобожусь. Намного интереснее сделать автоматическую цепочку рассылок на email и через самого бота для тех, кто ещё не определился с покупкой, но, например, записался в лист ожидания или покупал что-то в прошлом.
Это включает в себя и систему запланированных а-ля cron задач (celery - кривая, текущая во все стороны и хреново задокументированная библиотека с абсолютно непрозрачным поведением в некоторых ситуациях, и сделать без своего планировщика не получилось), и правильную схему данных, чтобы не ошибаться с отправкой, корректно считать статистику и т.п.
Самое интересное для меня - посмотреть, как это повлияет на продажи. Потому что, теоретически, чем больше "касаний" потенциальных клиентов мы делаем, объясняя им ценность, показывая примеры и отзывы, просто напоминая о себе, тем лучше они будут покупать. Ужасно интересно проверить на практике!
В чём же разница между этими фичами? 🤔
Понятная, достаточно сложная и по-технически увлекательная история про новый способ обработки сценариев с одной стороны. Я более-менее понимаю, как её сделать, но нужно придумать, написать и переписать кучу кода. Сложного кода, с кучей разных состояний. Или более простая, но ещё менее понятная, с потенциально более высоким ROI, система рассылок - с другой.
Кажется, что в первой фиче есть и inherent complexity (т.е. естественная сложность, свойственная самой задаче), и accidental complexity (случайно привнесённая во время реализации, мной). И если с естественной сложностью я сладить ещё могу, случайно привнесённая меня просто бесит! 🤬 Даже в своих проектах, что уж говорить о чужих.
Я убивать был готов, когда в фейсбуке несколько дней провозился с модулем CookieMonster на полтыщи строк, чтобы просто тупо куку поставить и прочесть. Там и конфиг поправь, и типы, и codegen запусти, и во всех этих фабриках разберись. Как будто кто-то специально напихал всё, что можно, чтобы получить ачивку overengineer 80 lvl на ревью.
А кто-то именно ради сложных задач до красноты глаз литкод мучает (я тоже это проходил 3-4 года назад) и в фанги любой ценой пробивается. Причём не ясно, то ли ради естественной сложности, то ли ради того, чтобы в слоях абстракций копошиться сутки напролёт, как палеонтолог.
Вот вам как даётся управление сложностью?
У нас в боте для Стильного клуба есть много пока ещё отсутствующих фич. И, конечно же, даже такая маленькая и простая система за такой короткий срок (первый из 277 комитов я сделал в начале марта) уже успела порасти мхом техдолга 🫣
Я то старательно игнорирую этот факт, как настоящий стартапер, то, как программист-перфекционист, принимаюсь всё тщательно переделывать, выдумывая новые и переделывая существующие абстракции.
Есть, например, BaseFlowHandler. Это такой базовый класс, потомкам которого передаётся управление для обработки пользовательских сценариев. Например, сценарий регистрации или сценарий покупки. Их можно комбинировать, и за это отвечает PrivateFlowController, который знает, как реагировать на сообщение или колбэк от пользователя в каждый (ну, почти каждый) момент.
Тут мне понадобилось сделать так, чтобы админы могли делать посты в канал/чат через бота. В основном для того, чтобы добавить им красивые кнопки (до сих пор не понимаю, почему в обычных клиентах телеги нет этой функции), но ещё и для настройки поведения репостов, пинов и т.п.
Помучив это дело несколько дней и написав приблизительно половину кода, я упёрся в свои же абстракции. Все эти базовые классы и контроллеры я делал для того, чтобы обрабатывать один, непрерывный по сути пользовательский сценарий. Там они работают. А вот для публикации множества сообщений, каждое из которых можно отдельно настроить в любой момент, уже нет.
И настолько мне не захотелось всё это добро переписывать, что я просто всё это выкинул и забил 🤪И решил сделать по-другому, в 10 раз проще и во столько же раз быстрее. По-стартаперски. Конечно, настолько же гибко и удобно, как могло бы быть в теории, не получится, но зато наверняка получится проще - а от этого ещё лучше.
Плюс я быстрее освобожусь. Намного интереснее сделать автоматическую цепочку рассылок на email и через самого бота для тех, кто ещё не определился с покупкой, но, например, записался в лист ожидания или покупал что-то в прошлом.
Это включает в себя и систему запланированных а-ля cron задач (celery - кривая, текущая во все стороны и хреново задокументированная библиотека с абсолютно непрозрачным поведением в некоторых ситуациях, и сделать без своего планировщика не получилось), и правильную схему данных, чтобы не ошибаться с отправкой, корректно считать статистику и т.п.
Самое интересное для меня - посмотреть, как это повлияет на продажи. Потому что, теоретически, чем больше "касаний" потенциальных клиентов мы делаем, объясняя им ценность, показывая примеры и отзывы, просто напоминая о себе, тем лучше они будут покупать. Ужасно интересно проверить на практике!
В чём же разница между этими фичами? 🤔
Понятная, достаточно сложная и по-технически увлекательная история про новый способ обработки сценариев с одной стороны. Я более-менее понимаю, как её сделать, но нужно придумать, написать и переписать кучу кода. Сложного кода, с кучей разных состояний. Или более простая, но ещё менее понятная, с потенциально более высоким ROI, система рассылок - с другой.
Кажется, что в первой фиче есть и inherent complexity (т.е. естественная сложность, свойственная самой задаче), и accidental complexity (случайно привнесённая во время реализации, мной). И если с естественной сложностью я сладить ещё могу, случайно привнесённая меня просто бесит! 🤬 Даже в своих проектах, что уж говорить о чужих.
Я убивать был готов, когда в фейсбуке несколько дней провозился с модулем CookieMonster на полтыщи строк, чтобы просто тупо куку поставить и прочесть. Там и конфиг поправь, и типы, и codegen запусти, и во всех этих фабриках разберись. Как будто кто-то специально напихал всё, что можно, чтобы получить ачивку overengineer 80 lvl на ревью.
А кто-то именно ради сложных задач до красноты глаз литкод мучает (я тоже это проходил 3-4 года назад) и в фанги любой ценой пробивается. Причём не ясно, то ли ради естественной сложности, то ли ради того, чтобы в слоях абстракций копошиться сутки напролёт, как палеонтолог.
Вот вам как даётся управление сложностью?
🔥5👍2❤1
Любите сложные технически задачи?
Anonymous Poll
20%
😍 Люблю, и хорошо справляюсь со сложностью
39%
🫠 Люблю, но мучаюсь в процессе
7%
🤷♂️ Отношусь нейтрально
9%
😐 Не люблю, но справляюсь
6%
🤬 Не люблю, не умею, но приходится
3%
🏝Не люблю и не сталкиваюсь
4%
Вообще не программирую
12%
Смотрю результаты
В комментарии к прошлому посту про сложность Ваня инженер принёс полезный подход. Ниже мой краткий пересказ.
🧱Когда у меня есть задача, которую я не очень понимаю, я стараюсь начать с описания логических блоков: пишу функции без реализации, но с аргументами и возвращаемыми значениями. Затем собираю их, как конструктор, и только в конце приступаю к реализации, когда уже всё сошлось.
Это позволяет выйти из ступора, когда не понимаешь, с чего начать, и выработать только необходимые для бизнеса абстракции. А не понаделать странных вещей, в которые сам потом упрёшься.
Я попробовал, и мне подход понравился 🔥 Напоминает UML-моделирование или задачку по систем-дизайну, где вместо прямоугольничков и стрелочек реальный, но ещё пустой код. При желании можно наверное даже запустить такого франкенштейна, чтобы посмотреть на стектрейсы и прикинуть, правильно ли оно работает.
От себя добавлю, что при конструировании новых программ я обычно делаю что-то из этого (редко всё):
1️⃣ Прототипирую, пока у меня в коде не получится внятная модель реальности-задачи. Это особенно важно, когда у программы или её части много возможных состояний, которые нужно где-то хранить и как-то обрабатывать, и уж тем более, когда состояние изменяется во времени
2️⃣ На сложные и неочевидные части кода, особенно зависящие от внешних систем либо с большим количеством возможных состояний, пишу тесты. Часто заранее, чтобы проверять себя, когда не получается удержать в уме все движущиеся части
3️⃣ Стараюсь двигаться от простого к сложному: начинать с более тривиальной версии задачи и минимального количества кода. Как раз чтобы сократить количество возможных состояний и сначала разобраться, как система будет работать
Последний пункт объясню отдельно, потому даётся мне это не всегда. Да, я не скатываюсь до уровня абстрактных фабрик в личных проектах (да и в большинстве рабочих тоже), но вот нафигачить классов вместо функций, чтобы просто сложить код в одну "коробочку" - это иногда случается.
Но классы поначалу только мешают. Особенно достаётся склонным к перфекционизму романтикам, мечтающим об элегантности системы!
Для них заготовлены килограммы горячих углей за шиворот: а не много ли тут методов, не слишком ли большой получился класс? Вот это положить в инстанс, либо сделать аргументом методов? Не нарушены ли принципы SOLID? А вот тут мне нужен миксин или лучше сделать базовый класс? Какое время жизни у объектов этого класса? А уж если есть строгая типизация и дженерики - держись, перфекционист!
Конечно же, в фабриках, паттернах и уж тем более классах ничего плохого нет. Плохо использовать их в крошечных проектах, либо слишком рано, когда ни требования, ни поведение системы в реальных условиях ещё не понятны. Именно тогда и получается куча надуманных абстракций, которые путаются под ногами.
🧩 Кстати говоря, абстракции из прошлого поста вообще не надуманные: я насочинял их тогда, когда бот уже поработал в продакшене для реальных людей, а я увидел закономерности в его поведении и своих требованиях. Сначала понял, что обычное взаимодействие с ботом - это пошаговый "диалог", потом уже написал BaseFlowHandler и т.п.
Но рефакторинг, чтобы получилось сделать недиалоговые сценарии взаимодействия с ботом, я пока отложил. Занимаюсь более важным и срочным, чем красота кода.
🧱Когда у меня есть задача, которую я не очень понимаю, я стараюсь начать с описания логических блоков: пишу функции без реализации, но с аргументами и возвращаемыми значениями. Затем собираю их, как конструктор, и только в конце приступаю к реализации, когда уже всё сошлось.
Это позволяет выйти из ступора, когда не понимаешь, с чего начать, и выработать только необходимые для бизнеса абстракции. А не понаделать странных вещей, в которые сам потом упрёшься.
Я попробовал, и мне подход понравился 🔥 Напоминает UML-моделирование или задачку по систем-дизайну, где вместо прямоугольничков и стрелочек реальный, но ещё пустой код. При желании можно наверное даже запустить такого франкенштейна, чтобы посмотреть на стектрейсы и прикинуть, правильно ли оно работает.
От себя добавлю, что при конструировании новых программ я обычно делаю что-то из этого (редко всё):
1️⃣ Прототипирую, пока у меня в коде не получится внятная модель реальности-задачи. Это особенно важно, когда у программы или её части много возможных состояний, которые нужно где-то хранить и как-то обрабатывать, и уж тем более, когда состояние изменяется во времени
2️⃣ На сложные и неочевидные части кода, особенно зависящие от внешних систем либо с большим количеством возможных состояний, пишу тесты. Часто заранее, чтобы проверять себя, когда не получается удержать в уме все движущиеся части
3️⃣ Стараюсь двигаться от простого к сложному: начинать с более тривиальной версии задачи и минимального количества кода. Как раз чтобы сократить количество возможных состояний и сначала разобраться, как система будет работать
Последний пункт объясню отдельно, потому даётся мне это не всегда. Да, я не скатываюсь до уровня абстрактных фабрик в личных проектах (да и в большинстве рабочих тоже), но вот нафигачить классов вместо функций, чтобы просто сложить код в одну "коробочку" - это иногда случается.
Но классы поначалу только мешают. Особенно достаётся склонным к перфекционизму романтикам, мечтающим об элегантности системы!
Для них заготовлены килограммы горячих углей за шиворот: а не много ли тут методов, не слишком ли большой получился класс? Вот это положить в инстанс, либо сделать аргументом методов? Не нарушены ли принципы SOLID? А вот тут мне нужен миксин или лучше сделать базовый класс? Какое время жизни у объектов этого класса? А уж если есть строгая типизация и дженерики - держись, перфекционист!
Конечно же, в фабриках, паттернах и уж тем более классах ничего плохого нет. Плохо использовать их в крошечных проектах, либо слишком рано, когда ни требования, ни поведение системы в реальных условиях ещё не понятны. Именно тогда и получается куча надуманных абстракций, которые путаются под ногами.
🧩 Кстати говоря, абстракции из прошлого поста вообще не надуманные: я насочинял их тогда, когда бот уже поработал в продакшене для реальных людей, а я увидел закономерности в его поведении и своих требованиях. Сначала понял, что обычное взаимодействие с ботом - это пошаговый "диалог", потом уже написал BaseFlowHandler и т.п.
Но рефакторинг, чтобы получилось сделать недиалоговые сценарии взаимодействия с ботом, я пока отложил. Занимаюсь более важным и срочным, чем красота кода.
👍9❤5👨💻1
Telegram API и внутренняя/привнесённая сложность
Закончим тему сложности примером inherent (внутренней) и accidental (привнесённой) complexity.
Если у вас есть бот, и этот бот добавлен в канал/чат с определённым набором прав, он будет получать от телеграма обновления. Одно сообщение или событие (пользователь вошёл в чат, вышел, запинили сообщение и т.п.) - одно обновление. Классы немного многословные, иногда нелогичные - прослеживается, что система развивалась итеративно, и теперь все эти новые поля как кольца вековых деревьев.
И вот, делаю я репосты из одного чата в другой (такая механика есть в Клубе). Приходит одно сообщение - вызываешь
Но если сделать так же для сообщений с несколькими фотками или видео, перешлётся только заглавная фотка/видео с подписью. Примерно так же, как если вручную пересылать такие сообщения - нужно бесяче выделять все фотки галочками.
Что же делает Телеграм, когда приходят такие сообщения? Он добавляет в
Можно догадаться, что произошло, когда у Телеграма возникло желание добавить поддержку нескольких фото в сообщении. Олимпиадники покумекали и приняли правильное инженерное решение: оставить схему данных практически нетронутой, реализовав всё это на уровне клиентов. Именно они умеют показывать 10 сообщений как одно, красиво группируя и расставляя фотографии по сетке и делая подпись из первого сообщения общей для всех.
И точно так же вынуждены делать боты. Что ужасно неудобно: теперь вместо одного вызова API в момент получения
Да, даже подпись к фото или видео лежит не в поле
Так совершенно идентичная по смыслу функция системы - пересылка сообщений - стала на порядок сложнее в реализации, понимании и поддержке в случае сообщений с несколькими вложениями. И точно так же бывает в других ситуациях, когда, например, не чья-то чужая система почему-то сделана неочевидно и сложно, а когда сам мир сложен. А уж это встречается повсеместно.
Всё это хорошие примеры inherent complexity задачи "переслать сообщение из чата в чат" для меня, как для пользователя Bot API. И, в то же время, это пример accidental complexity для программистов Телеграма, которые вынуждены были пожертвовать элегантностью системы в пользу простоты реализации и обратной совместимости. Решение скорее всего правильное, но это неудобство с нами (и с ними) теперь уже навсегда.
Закончим тему сложности примером inherent (внутренней) и accidental (привнесённой) complexity.
Если у вас есть бот, и этот бот добавлен в канал/чат с определённым набором прав, он будет получать от телеграма обновления. Одно сообщение или событие (пользователь вошёл в чат, вышел, запинили сообщение и т.п.) - одно обновление. Классы немного многословные, иногда нелогичные - прослеживается, что система развивалась итеративно, и теперь все эти новые поля как кольца вековых деревьев.
И вот, делаю я репосты из одного чата в другой (такая механика есть в Клубе). Приходит одно сообщение - вызываешь
copyMessage
, передавая from_chat_id
, message_id
и chat_id
- бот отсылает такое же сообщение в другой чат. Пока всё просто и логично.Но если сделать так же для сообщений с несколькими фотками или видео, перешлётся только заглавная фотка/видео с подписью. Примерно так же, как если вручную пересылать такие сообщения - нужно бесяче выделять все фотки галочками.
Что же делает Телеграм, когда приходят такие сообщения? Он добавляет в
Update.message
поле media_group_id
и присылает обновление для каждой фотографии или видео! Учитывая то, что в теории эти обновления могут доходить с задержкой или даже не по порядку (чего, к чести олимпиадников Дурова, никогда не происходило), их нужно где-то копить, ждать какое-то время, вытаскивать из каждого обновления file_id
и собирать новый набор параметров для метода sendMediaGroup
. Можно догадаться, что произошло, когда у Телеграма возникло желание добавить поддержку нескольких фото в сообщении. Олимпиадники покумекали и приняли правильное инженерное решение: оставить схему данных практически нетронутой, реализовав всё это на уровне клиентов. Именно они умеют показывать 10 сообщений как одно, красиво группируя и расставляя фотографии по сетке и делая подпись из первого сообщения общей для всех.
И точно так же вынуждены делать боты. Что ужасно неудобно: теперь вместо одного вызова API в момент получения
Update
нужно положить обновление куда-то (у меня в redis), следующие обновления с тем же media_group_id
складывать туда же, подождать получения 10 обновлений, либо какого-то умозрительного TTL - в моём случае, 5 секунд, а когда это время пройдёт, вытащить id файлов из обновлений, не забыть по пути caption
и caption_entities
- и вызвать sendMediaGroup
. Всё это уже в другой фоновой задаче, чтобы не блокировать worker на время ожидания, а значит привет, race conditions и лишний промежуточный state!Да, даже подпись к фото или видео лежит не в поле
text
, а в caption
. А разметка не в entities
, а в caption_entities
. Ещё один пример вековых колец развития системы 🙂Так совершенно идентичная по смыслу функция системы - пересылка сообщений - стала на порядок сложнее в реализации, понимании и поддержке в случае сообщений с несколькими вложениями. И точно так же бывает в других ситуациях, когда, например, не чья-то чужая система почему-то сделана неочевидно и сложно, а когда сам мир сложен. А уж это встречается повсеместно.
Всё это хорошие примеры inherent complexity задачи "переслать сообщение из чата в чат" для меня, как для пользователя Bot API. И, в то же время, это пример accidental complexity для программистов Телеграма, которые вынуждены были пожертвовать элегантностью системы в пользу простоты реализации и обратной совместимости. Решение скорее всего правильное, но это неудобство с нами (и с ними) теперь уже навсегда.
👍7🙈5❤4🤔2
🍓4
Как в продакшене память потекла
Захожу вчера в консоль Digital Ocean и вижу там такое. Глаза по пять рублей сразу - ну как так?! Вроде ж нормально программировал, а оно вон как повернулось.
Собрал тестовый стенд локально, чтобы нафигачить туда десятки тысячи запросов и посмотреть, что с памятью будет.
Как думаете, поможет этот подход обнаружить,где днище протекает? 🤔
Захожу вчера в консоль Digital Ocean и вижу там такое. Глаза по пять рублей сразу - ну как так?! Вроде ж нормально программировал, а оно вон как повернулось.
Собрал тестовый стенд локально, чтобы нафигачить туда десятки тысячи запросов и посмотреть, что с памятью будет.
Как думаете, поможет этот подход обнаружить,
🤔1
Что же случилось с памятью?
Я не забыл про обещанную вторую часть истории про текущую память.
Закопался в делах: и работы привалило (взял классный проект на несколько месяцев), и куча любопытного и интересного приключилось - скоро расскажу! 😍
Итак, увидев 96% съеденной оперативки, я удивился и испугался одновременно. Где я мог настолько сильно накосячить в питоне с его garbage collection? Посмотрел на графики, а там ступеньки. Ещё более таинственно: что такого может приводить к одномоментному скачку в потреблении памяти?
Обычно, если течёт, то течёт постепенно, а тут по 10-15 процентных пунктов за раз. И так уже на протяжении нескольких месяцев, то есть даже с какими-то последними изменениями это не связать 🤯
Я не забыл про обещанную вторую часть истории про текущую память.
Закопался в делах: и работы привалило (взял классный проект на несколько месяцев), и куча любопытного и интересного приключилось - скоро расскажу! 😍
Итак, увидев 96% съеденной оперативки, я удивился и испугался одновременно. Где я мог настолько сильно накосячить в питоне с его garbage collection? Посмотрел на графики, а там ступеньки. Ещё более таинственно: что такого может приводить к одномоментному скачку в потреблении памяти?
Обычно, если течёт, то течёт постепенно, а тут по 10-15 процентных пунктов за раз. И так уже на протяжении нескольких месяцев, то есть даже с какими-то последними изменениями это не связать 🤯
❤4
Решил отлаживать. Поднял локальный стенд в продакшен-режиме, начал туда наливать запросов: 10,000 фейковых запросов от телеграма, 10,000 запросов из страйпа. Хотя там и близко столько не бывает, у нас сотни человек пользуются ботом, а не десятки тысяч.
Не течёт. Чудеса в решете! Коллеги-друзья накидали вариантов, что какие-то древние питонячьи сетевые либы могут течь - но и это, в моём понимании, не очень-то объясняет ступеньки. Плюс у меня-то uvicorn + джанга, вроде как без палеонтологии. Ничего не понимаю.
А потом как понял!
Я много запускаю django shell прямо в проде. Что-то посмотреть, вручную сообщения отправить, поменять какие-то поля в модельках, которые в админке read-only. Каждый запуск - это ipython в веб-консоли (да, пока самый большой минус PaaS от Digital Ocean в отсутствии SSH). И консоль эта крайне кривая: скролл тупит, выделение нормально не работает, зависает ещё постоянно 🤬
Оказалось, что каждый запуск, когда консоль подвисала - а подвисает она совершенно рандомно, не нужно даже комп выключать - ipython оставался висеть как зомби-процесс. Они-то всю память и сжирали.
Во время активной разработки и частых релизов, когда я как раз-таки на графики смотрел, контейнер пересоздавался (там вроде докер в кубере под капотом) и вся эта зомби-дрянь погибала. А когда всё работало, и я просто пользовался шеллом, оно всё копилось и копилось.
Вот так вот просто всё разрешилось. Спасибо хоть чуйка на ступеньки сработала, и кучу времени не потратил!
Как вчера, например, когда час убил на подвисающие миграции в prisma (это такая модная ORM для ноды) - а оказалось, что она просто запускалась в интерактивном режиме и ждала от меня названия миграции. А я этого просто не видел, потому что запускаю всё в докере локально 🤣
А у вас какие банальные ошибки при отладке приключались?
Не течёт. Чудеса в решете! Коллеги-друзья накидали вариантов, что какие-то древние питонячьи сетевые либы могут течь - но и это, в моём понимании, не очень-то объясняет ступеньки. Плюс у меня-то uvicorn + джанга, вроде как без палеонтологии. Ничего не понимаю.
А потом как понял!
Я много запускаю django shell прямо в проде. Что-то посмотреть, вручную сообщения отправить, поменять какие-то поля в модельках, которые в админке read-only. Каждый запуск - это ipython в веб-консоли (да, пока самый большой минус PaaS от Digital Ocean в отсутствии SSH). И консоль эта крайне кривая: скролл тупит, выделение нормально не работает, зависает ещё постоянно 🤬
Оказалось, что каждый запуск, когда консоль подвисала - а подвисает она совершенно рандомно, не нужно даже комп выключать - ipython оставался висеть как зомби-процесс. Они-то всю память и сжирали.
Во время активной разработки и частых релизов, когда я как раз-таки на графики смотрел, контейнер пересоздавался (там вроде докер в кубере под капотом) и вся эта зомби-дрянь погибала. А когда всё работало, и я просто пользовался шеллом, оно всё копилось и копилось.
Вот так вот просто всё разрешилось. Спасибо хоть чуйка на ступеньки сработала, и кучу времени не потратил!
Как вчера, например, когда час убил на подвисающие миграции в prisma (это такая модная ORM для ноды) - а оказалось, что она просто запускалась в интерактивном режиме и ждала от меня названия миграции. А я этого просто не видел, потому что запускаю всё в докере локально 🤣
А у вас какие банальные ошибки при отладке приключались?
🔥15😁8👍5
Ну что, айда в Threads?
https://www.threads.net/@ogrmv
Думаю, может попробовать там контент на английском публиковать полезный 🤔
https://www.threads.net/@ogrmv
Думаю, может попробовать там контент на английском публиковать полезный 🤔
🤡10😁4❤1🗿1
Ужасы отладки
Делаю приложение, которое запускается через pm2. Это такой модный-молодёжный сервис-супервизор,написанный фронтендерами , который следит за приложениями, перезапускает, если надо и т.п.
Все секреты в приложение попадают, как обычно, через env-переменные. Их там уже было N штук, и всё работало.
Процесс такой:
1. Новый код попадает на VPS
2. Собирается
3. Запускается скрипт
4. Внутри читаются из амазоновского хранилища и экспортируются env-переменные
5. Запускается
6.
Добавил пару новых, credentials для амазоновской SQS. Везде прокинул, в скрипте они есть, всё проверил - но приложение на шаге 6 просто падает, жалуясь, что именно новых двух переменных нет.
Крутил скрипты, крутил pm2, запускал руками, дажепеременные переименовывал - то работает, то не работает.
Оказалось! 🤬
У pm2 есть флажок
В итоге час в трубу 🤬
Делаю приложение, которое запускается через pm2. Это такой модный-молодёжный сервис-супервизор,
Все секреты в приложение попадают, как обычно, через env-переменные. Их там уже было N штук, и всё работало.
Процесс такой:
1. Новый код попадает на VPS
2. Собирается
3. Запускается скрипт
prod-run-app-migrations.sh
4. Внутри читаются из амазоновского хранилища и экспортируются env-переменные
5. Запускается
pm2 ./build/server/app.js
6.
app.js
проверяет, все ли переменные на месте, ругается и умирает, если нетДобавил пару новых, credentials для амазоновской SQS. Везде прокинул, в скрипте они есть, всё проверил - но приложение на шаге 6 просто падает, жалуясь, что именно новых двух переменных нет.
Крутил скрипты, крутил pm2, запускал руками, даже
Оказалось! 🤬
У pm2 есть флажок
--update-env
, который надо использовать, чтобы эта поделка подхватила новые переменные из окружения. ЧТО?! И после этого мне говорят, что все эти новые сиюящие утилитки классные, удобные! Вот проверенный годами supervisord
таких проблем не имеет. По крайней мере, я не сталкивался.В итоге час в трубу 🤬
🤔8🤡4😈2
Публично о неудачах
Я редко рассказываю, когда мои идеи заканчиваются ничем. Даже не знаю, почему: вроде бы я и считаю неудачи не "провалами", а просто необходимыми этапами на длинном пути к цели, но то ли публично рефлексировать страшно и непривычно, то ли не вполне понятны выводы из этих неудач.
Например, я задумывал делать Frontend Bay - портал для фронтендеров, заказал дизайн, полтора месяца обсуждал его, а потом перегорел и забил. Забил на исследование зарплат разработчиков, забил на Калькулятор стоимости жизни, несколько опенсорс-проектов - в общей сложности заброшенных проектов уже несколько десятков.
Так же вышло с книжкой про карьеру: написал громадный план - садись и пиши, но запал по пути пропал, а ещё в комментариях встретился с крайне неприятным для себя обесцениванием. И вроде бы я от него отмахнулся, но спустя уже почти год оказалось, что нет - не отмахнулся. Засело глубоко, пришлось вытаскивать из подсознания и прорабатывать.
Образовательного проекта с моим другом Витей не будет
Сегодняшняя, а точнее уже месячной давности "плохая" новость в том, что мы с Витей разбежались. Проинтервьюировали 8 человек, много обсуждали и планировали, но так и не пришли к общему понимаю того, что же нам делать, для кого, зачем и почём. Витя хотел более камерный проект, больше для фана и коммьюнити. Мне же в большей степени хочется делать настоящий бизнес, который работает сам, растёт, может обеспечивать нас и будущую команду. Хотя меня пугала громадная конкуренция на рынке онлайн-образования.
Слово "плохая" в кавычках неспроста. Хоть моей первой реакцией и были раздражение и обида, мол, как же так, опять! - это, на мой взгляд, к лучшему. Хорошо, что потратили не так много времени, что не затащили глубокие разногласия далеко в совместный проект. Что сумели поговорить честно и по душам и понять, что хотим разного (как минимум, на данном этапе).
Не рассориться, наконец, оставшись друзьями 🙂
Важное наблюдение: делать что-то вместе намного веселее и приятнее, чем сидеть одному в углу и что-то там себе планировать. Нужно плечо, поддержка, а ещё разный опыт и взгляд на вещи. Кстати, не уверен, что мы были бы хорошими кофаундерами - но даже несмотря на это, месяц, который мы провели в обсуждениях, был крутой и полезный.
Я попробовал новый для себя инструмент - глубинные интервью, укрепился в том, что моё понимание типичных проблем и задач разработчиков вполне адекватное и глубокое. Всё ж не зря сам лямку тянул почти 15 лет.
Попрактиковался в написании JTBD-сценариев на основе услышанного, что очень полезно для предпринимателя и продакта. Поупражнялся в расчётах юнит-экономики, хотя это в большей степени полезно было для нашего Стильного клуба.
Прикол в том, что попробовать новые штуки самому сложнее. Когда рядом нет верного товарища, быть смелым не так-то просто. А с кофаундером появляется ещё и коммитмент перед близким и важным человеком, который нарушать не хочется ну никак.
Ещё мои представляения о бизнесе постепенно трансформируются.
Спросить меня полгода-год назад о том, нужны ли инвестиции - и мой ответ сходу был бы "нет". Дескать, если не понимаешь своих клиентов настолько хорошо, что можешь сам сделать первые 10-100 продаж руками, заработать, подтвердить гипотезу и принести пользу, то и нафиг такой бизнес (в качестве первого). Но есть же и более капиталоёмкие проекты, есть маркетинг, который, как показывает опыт, даже при наличии бьющего в цель продукта всё равно необходим и стоит денег. Жить на что-то надо, в конце концов, пока твой бизнес ещё не генерирует прибыль.
Так же и с кофаундерами: если раньше мне казалось, что делить компанию с кем-то - плохая идея, то сейчас очевидно ровно обратное. Скорее всего, раскачать хоть сколько-либо сложный бизнес в одиночку не получится. Без громадного прошлого опыта и капитала так точно. А значит нужны люди, на которых можно положиться, с которыми и комфортно, и интересно, и полезно как общаться, так и делать дела.
Я редко рассказываю, когда мои идеи заканчиваются ничем. Даже не знаю, почему: вроде бы я и считаю неудачи не "провалами", а просто необходимыми этапами на длинном пути к цели, но то ли публично рефлексировать страшно и непривычно, то ли не вполне понятны выводы из этих неудач.
Например, я задумывал делать Frontend Bay - портал для фронтендеров, заказал дизайн, полтора месяца обсуждал его, а потом перегорел и забил. Забил на исследование зарплат разработчиков, забил на Калькулятор стоимости жизни, несколько опенсорс-проектов - в общей сложности заброшенных проектов уже несколько десятков.
Так же вышло с книжкой про карьеру: написал громадный план - садись и пиши, но запал по пути пропал, а ещё в комментариях встретился с крайне неприятным для себя обесцениванием. И вроде бы я от него отмахнулся, но спустя уже почти год оказалось, что нет - не отмахнулся. Засело глубоко, пришлось вытаскивать из подсознания и прорабатывать.
Образовательного проекта с моим другом Витей не будет
Сегодняшняя, а точнее уже месячной давности "плохая" новость в том, что мы с Витей разбежались. Проинтервьюировали 8 человек, много обсуждали и планировали, но так и не пришли к общему понимаю того, что же нам делать, для кого, зачем и почём. Витя хотел более камерный проект, больше для фана и коммьюнити. Мне же в большей степени хочется делать настоящий бизнес, который работает сам, растёт, может обеспечивать нас и будущую команду. Хотя меня пугала громадная конкуренция на рынке онлайн-образования.
Слово "плохая" в кавычках неспроста. Хоть моей первой реакцией и были раздражение и обида, мол, как же так, опять! - это, на мой взгляд, к лучшему. Хорошо, что потратили не так много времени, что не затащили глубокие разногласия далеко в совместный проект. Что сумели поговорить честно и по душам и понять, что хотим разного (как минимум, на данном этапе).
Не рассориться, наконец, оставшись друзьями 🙂
Важное наблюдение: делать что-то вместе намного веселее и приятнее, чем сидеть одному в углу и что-то там себе планировать. Нужно плечо, поддержка, а ещё разный опыт и взгляд на вещи. Кстати, не уверен, что мы были бы хорошими кофаундерами - но даже несмотря на это, месяц, который мы провели в обсуждениях, был крутой и полезный.
Я попробовал новый для себя инструмент - глубинные интервью, укрепился в том, что моё понимание типичных проблем и задач разработчиков вполне адекватное и глубокое. Всё ж не зря сам лямку тянул почти 15 лет.
Попрактиковался в написании JTBD-сценариев на основе услышанного, что очень полезно для предпринимателя и продакта. Поупражнялся в расчётах юнит-экономики, хотя это в большей степени полезно было для нашего Стильного клуба.
Прикол в том, что попробовать новые штуки самому сложнее. Когда рядом нет верного товарища, быть смелым не так-то просто. А с кофаундером появляется ещё и коммитмент перед близким и важным человеком, который нарушать не хочется ну никак.
Ещё мои представляения о бизнесе постепенно трансформируются.
Спросить меня полгода-год назад о том, нужны ли инвестиции - и мой ответ сходу был бы "нет". Дескать, если не понимаешь своих клиентов настолько хорошо, что можешь сам сделать первые 10-100 продаж руками, заработать, подтвердить гипотезу и принести пользу, то и нафиг такой бизнес (в качестве первого). Но есть же и более капиталоёмкие проекты, есть маркетинг, который, как показывает опыт, даже при наличии бьющего в цель продукта всё равно необходим и стоит денег. Жить на что-то надо, в конце концов, пока твой бизнес ещё не генерирует прибыль.
Так же и с кофаундерами: если раньше мне казалось, что делить компанию с кем-то - плохая идея, то сейчас очевидно ровно обратное. Скорее всего, раскачать хоть сколько-либо сложный бизнес в одиночку не получится. Без громадного прошлого опыта и капитала так точно. А значит нужны люди, на которых можно положиться, с которыми и комфортно, и интересно, и полезно как общаться, так и делать дела.
❤32👍9🔥2
Совпадения и хорошие новости
Я не большой любитель всяческой мистики. Совпадения считаю если не случайностями, то закономерностями. Меняется восприятие - начинаешь замечать новое, говорить "да", когда обычно сказал бы "нет". Получаются совпадения, иногда даже судьбоносные.
Про восприятие расскажу отдельно, это прям супер-глубокая и важная для меня тема. А пока про совпадения.
Первое совпадение достаточно тривиальное. Несколько месяцев назад я задумался, что кроме Клуба мне хотелось бы делать и что-то своё. В идеале бизнес, но как минимум какой-то проект за деньги. Работы для меня в Клубе хоть и достаточно, но не всегда мои прямые компетенеции и обязанности бьют в следующую точку роста. Да и заработать не помешало бы, в конце концов.
Буквально в этот же день я натыкаюсь на классный проект. И сейчас занимаюсь им - с огромным удовольствием. Проектов на самом деле несколько: это утилиты для предпринимателей, основанные на ChatGPT и других публчиных API.
Кайфовый формат, в котором я работаю: один, как хочу, без какого-либо контроля и с полным доверием со стороны клиента. Фулстек, всё от инфраструктуры и devops до бэкенд-сервисов, фронта и UX, prompt engineering ещё. Не то чтобы я всё это одинаково люблю, но мне намного комфортнее делать всего по чуть-чуть, чем одни джейсоны гонять или формы полировать. Дизайн делает дизайнер клиента, но фундаментально на продукт я влияю, кажется, в большей степени.
Вероятно, потому, что финальный продукт находится где-то между тем, что мы можем сделать с помощью LLM (а это напрямую зависит от меня), и тем, как видит его заказчик. Всего за несколько недель у нас в работе уже третья версия UI для сервиса - и явно не последняя.
Результат получается тоже супер: уже спустя 3 недели работы (из них 2/3 - это архитектура системы и простые деплои) и самый первый прототип тулзы, я сам с огромным удовольствием с ним балуюсь. Очень интересно выходит. И я всерьёз задумываюсь о том, как использовать LLM и прочие нейронки в бизнесе и собственных проектах. Огромный класс задач на классификацию, экстракцию любой информации из текста, саммаризацию (как по-русски будет?) покрывается с громадной точностью и почти бесплатно.
Платят за проект тоже нормально. Не так много, как хотелось бы, конечно, но мне хватает. А дальше ценник буду поднимать, описав кейс для портфолио. В общем, кайф.
А вот второе совпадение уже в куда меньшей степени совпадение, и в куда большей - "судьбоносное". О нём в следующем посте.
Я не большой любитель всяческой мистики. Совпадения считаю если не случайностями, то закономерностями. Меняется восприятие - начинаешь замечать новое, говорить "да", когда обычно сказал бы "нет". Получаются совпадения, иногда даже судьбоносные.
Про восприятие расскажу отдельно, это прям супер-глубокая и важная для меня тема. А пока про совпадения.
Первое совпадение достаточно тривиальное. Несколько месяцев назад я задумался, что кроме Клуба мне хотелось бы делать и что-то своё. В идеале бизнес, но как минимум какой-то проект за деньги. Работы для меня в Клубе хоть и достаточно, но не всегда мои прямые компетенеции и обязанности бьют в следующую точку роста. Да и заработать не помешало бы, в конце концов.
Буквально в этот же день я натыкаюсь на классный проект. И сейчас занимаюсь им - с огромным удовольствием. Проектов на самом деле несколько: это утилиты для предпринимателей, основанные на ChatGPT и других публчиных API.
Кайфовый формат, в котором я работаю: один, как хочу, без какого-либо контроля и с полным доверием со стороны клиента. Фулстек, всё от инфраструктуры и devops до бэкенд-сервисов, фронта и UX, prompt engineering ещё. Не то чтобы я всё это одинаково люблю, но мне намного комфортнее делать всего по чуть-чуть, чем одни джейсоны гонять или формы полировать. Дизайн делает дизайнер клиента, но фундаментально на продукт я влияю, кажется, в большей степени.
Вероятно, потому, что финальный продукт находится где-то между тем, что мы можем сделать с помощью LLM (а это напрямую зависит от меня), и тем, как видит его заказчик. Всего за несколько недель у нас в работе уже третья версия UI для сервиса - и явно не последняя.
Результат получается тоже супер: уже спустя 3 недели работы (из них 2/3 - это архитектура системы и простые деплои) и самый первый прототип тулзы, я сам с огромным удовольствием с ним балуюсь. Очень интересно выходит. И я всерьёз задумываюсь о том, как использовать LLM и прочие нейронки в бизнесе и собственных проектах. Огромный класс задач на классификацию, экстракцию любой информации из текста, саммаризацию (как по-русски будет?) покрывается с громадной точностью и почти бесплатно.
Платят за проект тоже нормально. Не так много, как хотелось бы, конечно, но мне хватает. А дальше ценник буду поднимать, описав кейс для портфолио. В общем, кайф.
А вот второе совпадение уже в куда меньшей степени совпадение, и в куда большей - "судьбоносное". О нём в следующем посте.
❤15🤔1