Т.к. мы пилим продукты и нам важна Обратная связь от пользователей - мы ее собираем в Google Forms, они оч крутые, но есть пара нюансов:
1) Обработка персональных данных по закону должна быть в РФ. Штраф до 500к рублей! И т.к. мы хотим делать бизнес на РФ - нам нужно использовать оператора из РФ.
2) Мы сейчас собрали таблицу куда приходят все “Данные” от наших иностранных пользователей.
У нас сейчас 6 таблиц, только с 3 расширений, дальше будет больше.
Ходить по страницам не так удобно как у всех топовых сервисов по созданию форм.
Мы хотим видеть одну таблицу с сортировкой по последним и фильтрами по всем ответам/заявкам.
Нужно было объяснить зачем нужна роль для LLM и как ей пользоваться)
Можно представить что роль - это персонаж, у которого есть свои особые характеристики и свойства. То как мы пропишем персонажа влияет на то, как агент или llm будет себя вести (стиль ответа, его поведение, "характер"). В чатах обычно можно использовать с "act as [ROLE]"
🖼 🚀 Я почти всегда выбираю ISR в Next.js для контентных сайтов.
Вот почему:
SSR: - Каждый запрос = генерация страницы
SSG: - Обновить контент = пересобрать весь проект - При 1000+ страниц билдится часами
ISR - лучший вариант: - Не генерит страницы сразу. Только по запросу. - Ключевой параметр: revalidate - определяет, как часто Next.js должен перегенерировать страницы.
Например revalidate: 60 - страница обновляется раз в 60 сек, а между этим - юзер видит кэш из памяти. Для некоторых контентных сайтов норма обновления данных 8-24 часа. Данные будут в оперативной памяти все это время.
💡 Фишка для SEO: После деплоя (CI/CD) - страницы прогреваются скриптом, чтобы не ждать первого захода. Это нужно чтобы поисковые боты видели всегда лучшую версию сайта, а не ждали прогрузки кеша.
📌 Вывод: Если тебе не нужен real-time обновления сайта - ISR закрывает почти все потребности.
Задача будет полезна разработчикам веб-приложений и сервисов.
Текст подготовил Артём Шумейко — внештатный райтер, амбассадор Selectel и автор YouTube-канала о разработке.
Условие
В компании «ГигаПост» выпустили долгожданное обновление: на сайте появилась новая кнопка «Подписаться на тему». Интерфейс готов, API поддерживает, проверено на стенде — все работает как часы.
Но после релиза начались странности. Некоторые пользователи видят кнопку, а некоторые — нет. Кто-то говорит, что она появилась через сутки. Кто-то — что только после нажатия Ctrl+F5.
Команда фронтенда уверена — код задеплоен. Бэкенд-эндпоинт отвечает корректно. На тестовом стенде все видно. Даже сам разработчик открывает сайт на своем ноутбуке — кнопка есть.
Начали подозревать баг в логике отображения, потом — переключение языка, затем подумали про авторизацию. Но фича пропадает у пользователей даже с одинаковыми условиями.
И вот тогда кто-то предложил простую мысль: а пользователи вообще видят свежую версию сайта?
Задача
Почему часть пользователей не видит новую кнопку, хотя код задеплоили? В чем может быть причина? Где в цепочке доставки может остаться старая версия?
Предлагайте свое решение в комментариях. А правильный ответ можно подсмотреть в Академии Selectel.
Апгрейднули тарифы и добавили пояснение к каждому. Давайте знакомиться:
👉Тариф Dev. Подходит для тестирования функционала нашего Managed Kubernetes — 200 ₽/мес
Пример использования: небольшие пет-проекты
👉Тариф Base. Для кластеров со средней нагрузкой, где отказоустойчивость не критична — 2000 ₽/мес
Пример использования: корпоративные веб-сервисы или ML inference-сервисы
👉Тариф Custom. Для гибкой настройки — от 2520 ₽/мес
Пример использования: highload-платформы и BigData/ML пайплайны
Пара слов о тарифе Custom. В нем можно выбрать количество ядер, объем оперативы и диска, а также установку с одной мастер-нодой или с тремя для настройки отказоустойчивости.
Разработка или Vibe Coding продолжается 3 день. Я все ещё использую Bolt и смог сделать вполне рабочий сервис. Вручную программисту потребовалось бы около 1-2 недель на такой функционал.
Авторизация
Редактор форм
Просмотр ответов форм с фильтрами
Выгрузка ответов в csv
Шаблоны форм
Загрузка файлов в хранилище
Публикация формы для клиента
Все уже работает и связано с БД
Остальные скрины и ДЕМО версию пришлось опубликовать в телеге т.к. тут лимит на 1 картинку.
BSONError: Invalid UTF-8 string in BSON document
BSONError: bad embedded document length in bson
Документы в базе нормальные. Ошибки появлялись только при включённом Xray с WARP (через встроенный WireGuard). Когда VPN отключён — всё читается корректно.
Поначалу думал, что что-то с кодировкой или драйвером, но оказалось, что проблема в том, что Mongo работала через Cloudflare WARP.
Когда запросов было мало — срабатывало нормально.
Когда запускался алгоритм и начинались частые обращения к базе — Mongo валился с ошибками BSON.
Причина — искажение бинарного трафика при передаче через WARP.
Mongo использует бинарный протокол BSON, и даже один сбитый байт ломает парсинг.
Пофиксил так:
добавил в routing.rules Xray правило, чтобы трафик к Mongo шёл мимо WARP:
внезапно пришла идея - кажется витает в воздухе новый путь установки пакетов и вообще библиотек в приложения.
Начну как всегда из далека: В последнее время, так как вместо человека теперь LLM чаще всего обращается к сайтам и приложениям, становится популярно добавлять специально написанные для llm страницы (как раньше sitemap, только теперь md файлы)
Это даже стало правилом для MCP серверов - например Cline добавляет только те MCP сервера, которые LLM может поставить в one shot или имеют llm-install.md файл.
По сути, кажется это становится файлом установки для llm - то есть, llm, следуя инструкциям этого файла ставит и конфигурирует MCP сервер на компьютере (прям как раньше, люди скачивали и устанавливали на windows).
Уязвимостей тут может быть много (потому что промпт может быть corrupt), но если следить за процессом и обязательно проверять команды исполнения - why not?
А теперь - представляете что можно сделать это для любого пакета и любого языка?
То есть, есть библиотека например для интеграции авторизации. Вместо того, чтобы разворачивать её вручную, автор библиотеки может написать llm-install и агент, имея контекст проекта в котором он работает, может целиком его развернуть!🙌 Креды например строго хранить в отдельном фале, не давать доступ (например использовать cursorignore etc..) и такие пакеты можно интегрировать как клиенту и бекенду!
И тоже самое можно применить и для удаления! И таким образом весь процесс интгерации станет больше как лего plug & play 🤩
В каждой компании, где я работал, были свои правила формализации и постановки обнаруженных багов. Чаще всего - никаких правил. Что нашел тестировщик, то отписал в комментарии к задаче (и это еще хорошо!), а иногда просто на созвоне обсудил с разрабом, тот быстро пофиксил и факт найденного бага остался между ними. Для мелких студий так работать, может, и допустимо, но рано или поздно отсутствие элементарного порядка начинает надоедать. И тут появляются вопросы:
Как оформить баг? Комментарий к задаче? Отдельная задача? Чек-лист в описании задачи?
Как отметить баг выполненным? Проверенным? Повторно проявившимся?
Какая система багтрекинга вам показалась наиболее удобной? Jira? Youtrack? Яндекс.Трекер? Битрикс24? Что-то из новомодного импортозамещения?
Какие шаблоны описания бага приняты в вашей компании? Нет правил? Шаг 1 - шаг 2 - шаг 3 - ожидание - реальность? Скриншот со стрелочками? Видеозапись с экрана?
Есть ли какая-то классификация багов? Минор / мажор? Бэк / фронт? Бэклог / критикал?
Очень интересует ваш опыт в данной теме. Буду очень признателен, если поделитесь своими знаниями в комментариях.
🚀 +32 подписчика в наш телеграм канал, это значит вам понравилась моя статья в рамках челленджа 12 проектов за 12 месяцев, которую я вчера опубликовал.
Несмотря на кадровый голод в IT занято огромное количество лишних людей деятельность которых в общем-то бесполезна. А все что бесполезно как известно приносит только вред. Возьмем к примеру новомодную отрасль UI/UX которая по задумке должна улучшать пользовательский опыт - так называемый "экспириенс". На планете существует целый зоопарк разных форматов дат: 1 ноября 2000, 01.11.2000 и т.д и т.п. Это мелочь, но и в мелочах можно тот самый "экспириенс" взять да и улучшить. И он был повсеместно "улучшен" до формата "3 года назад". Как правило без какой либо возможности вернуть нормальную дату.
Теперь просто хочется простереть руки к небу и крикнуть за что это мне?
Самое печальное в этой ситуации, что исправить ничего нельзя. Этот самый "экспириенс" внедряется крупными корпорациями у которых достаточно ресурса чтобы содержать огромное количество людей не занятых ни какой полезной деятельностью и было бы лучше если бы они вообще ничего не делали. Как в нейросетях лишние веса приносят только вред, так и в индустрии, лишние отрасли приносят пользователям только вред.
Я много и часто просматриваю JSON-файлы: от конфигураций сервисов до API ответов и логов. Каждый раз, открывая очередной файл, я форматирую содержимое, чтобы было удобнее читать (ведь JSON не только machine-readable, но и human-readable). И каждый раз я грущу, что все сервисы (онлайн, встроенные средства IDE и даже плагины) предоставляют лишь две крайности: форматировать всё или ничего (минифицировать в одну строку).
Но что, если я хочу отформатировать JSON лишь до определённого уровня? Что, если у меня есть огромный список словарей (возможно, даже глубоких), который при форматировании выглядит как-то так:
Да, многие текстовые редакторы вроде Sublime Text или VS Code дают возможность свернуть контент до определённого уровня. Но что, если я хочу оставить файл в этом промежуточном виде и просматривать его прямо в терминале, подключившись по ssh? Или я хочу посмотреть файл на гитхабе с телефона? Да, возможно, мои вкусы весьма специфичны, но в существующих реалиях я вынужден грустно довольствоваться лишь полностью развёрнутым вариантом (или делать это вручную). Встроенные средства форматирования JSON в JS или Python также не предоставляют простой возможности ограничить глубину (либо я так и не научился их готовить).
Поэтому я собрался с силами и написал свой форматтер с возможностью ограничить глубину. Помимо базового функционала вроде валидации, минификации и настройки количества отступов, в нём есть настройка максимальной глубины (по умолчанию она равна нулю, что соответствует привычному форматированию без ограничений).
Да, он вряд ли подойдёт, чтобы редактировать на лету гигантские JSON файлы. И он уж точно не пытается стать убийцей какого-либо из существующих онлайн сервисов. В первую очередь он призван решить мою проблему: сделать форматирование JSON чуточку более управляемым. А если и вы сталкивались с подобной проблемой, буду рад, если сервис поможет и вам!
Привет, Хабр! Попробуйте решить задачу. Особенно интересно будет бэкенд-разработчикам, которые работают с микросервисной архитектурой и регулярно сталкиваются с неожиданным поведением инфраструктуры.
Условие
В компании «Доки.Онлайн» выкатили обновление: теперь пользователи могут загружать PDF-файлы с отсканированными договорами. Все работало отлично в локальной среде — разработчик протестировал загрузку больших файлов, убедился, что API обрабатывает их корректно, и спокойно отправил изменения в продакшн.
Но радость была недолгой. На боевом сервере при попытке загрузить файл система выдавала ошибку 413 Request Entity Too Large. Причем происходило это до того, как пользователь получал какой-либо отклик от самого приложения.
Разработчик Геннадий Завров начал искать причину. Он проверил логи всех четырех компонентов системы:
фронтенда;
API Gateway (определяет, в какой микросервис послать запрос);
микросервиса загрузки файлов;
микросервиса обработки документов.
Во всех логах пусто, будто никакого запроса и не было. Ни один сервис даже не попытался начать обработку файла.
Геннадий начал подозревать сетевые сбои, перегрузку API Gateway и баг в коде фронтенда. Однако простые тесты с маленькими файлами работали стабильно. Проблема проявлялась только при загрузке чего-то «потяжелее».
В какой-то момент он задал себе вопрос: а точно ли запрос доходит до приложений?
Задача
Почему при загрузке большого файла система возвращает ошибку 413, если сами сервисы даже не видят входящий запрос? Кто может остановить запрос еще до бэкенда?
Делитесь своим ответом в комментариях. А посмотреть полное решение можно в Академии Selectel.
Мы все с вами привыкли, что в питоне можно "зарайзить" исключение в любой момент: raise Exception Но, что если в какой-то момент времени мы не можем вызывать исключение?
Простейший пример: что произойдет при запуске такого скрипта?
# ex.py
class BrokenDel:
def __del__(self):
raise ValueError('del is broken')
obj = BrokenDel()
del obj
print('done!') # будет ли выведено?
Тут может быть два варианта:
Или del вызовет ValueError и программа завершится
Или случится какая-то магия, ошибка будет вызвана, напечатается, но программа продолжится
» python ex.py
Exception ignored while calling deallocator :
Traceback (most recent call last): File "/Users/sobolev/Desktop/cpython/ex.py", line 3, in __del__ raise ValueError('del is broken')
ValueError: del is broken
done!
Знакомьтесь – unraisable exceptions 🤝
Как оно работает?
В некоторых местах C кода у нас есть необходимость вызывать исключения, но нет технической возможности. Пример, как выглядит упрощенный dealloc для list?
static void
list_dealloc(PyListObject *op)
{
Py_ssize_t i;
PyObject_GC_UnTrack(op); // убираем объект из отслеживания gc
if (op->ob_item != NULL) {
i = Py_SIZE(op);
while (--i >= 0) {
// уменьшаем счетчик ссылок каждого объекта в списке
Py_XDECREF(op->ob_item[i]);
}
op->ob_item = NULL;
}
PyObject_GC_Del(op);
}
А, как вы можете знать, чтобы в C коде вызвать ошибку, нужно сделать две вещи:
Взывать специальное АПИ вроде PyErr_SetString(PyExc_ValueError, "some text")
И вернуть NULL как PyObject * из соответствующих АПИ, показывая, что у нас ошибка. Если вернуть NULL нельзя, то мы не можем поставить ошибку в текущий стейт интерпертатора. А тут у нас void и вернуть вообще ничего нельзя. Потому приходится использовать вот такой подход с unraisable exception
Они создают ошибку, но не выкидывают её обычным способом, а сразу отправляют в специальный хук-обработчик. Данный хук не производит классическое "выбрасывание" исключения, а просто его печатает по-умолчанию. Ниже посмотрим, как его можно кастомизировать.
В питоне оно используется где-то 150 раз. То есть – прям часто. Примеры:
Ошибки при завершении интерпретатора, попробуйте сами:
🚀 291 установка за месяц. Как мы запустили браузерное расширение «Rar File Opener»
Расскажу про создание нового продукта — расширения для открытия RAR-архивов прямо в браузере. Простое решение проблемы, когда средствами ОС RAR не открыть.
2️⃣ Проверили запросы в Google и Chrome Store. Люди ищут способы извлечения RAR архивов по запросам "rar extractor", "rar file opener" и т.д. Конкуренция в Chrome Store при этом низкая.
3️⃣ Разработали простое расширение, которое:
- Открывает RAR прямо в браузере (а также ZIP, 7z, TAR, GZ, TAR.GZ и другие форматы)
- Не крадёт данные (работает локально)
- Работает с несколькими архивами
У нас процесс уже отлажен:
✅ Разработали MVP
✅ Сделали иконки, промо картинки и лендинг
✅ Сделали SEO-описание + перевод на 55 языков
✅ Тесты и фикс багов
На все 3-4 недели.
Копаем ил дальше в поисках идеи нового продукта! 😁
Свой класс события для плагинов Joomla. Продолжение.
Продолжение потому, что начало уже было в статье Виталия Некрасова на Хабре.
Кратко.
В Joomla 5+ для событий аргументы упаковываются в собственные классы событий: ContentPrepareEvent, AfterSaveEvent и т.д. Данные из них мы получаем в виде $event->getArgument('argument_name') или [$var, $var2] = array_values($event->getArguments());. Также для разных типов событий могут быть специфичные методы типа $article = $event->getItem(); в ContentPrepareEvent и т.д. И в статье Виталия как раз об этом рассказывается.
А так же рассказывается о методах onGet и onSet. В ядре Joomla в классе \Joomla\CMS\Event\AbstractEvent сказано:
/**
* Add argument to event.
* It will use a pre-processing method if one exists. The method has the signature:
*
* onSet($value): mixed
*
* where:
*
* $value is the value being set by the user
* It returns the value to return to set in the $arguments array of the event.
*
* @param string $name Argument name.
* @param mixed $value Value.
*
* @return $this
*
* @since 4.0.0
*/
Добрался я тоже до своего класса события для плагинов, порылся в ядре и подумал, что onSet... и onGet... методы не обязательно делать (хотя в статье по ссылке об этом не упоминается). Это методы для "предварительных проверок и манипуляций" с данными перед тем, как они будут отданы через getArgument() или get<ArgumentName>. Метод getData() отдаст данные, которые предварительно будут обработаны методом onGetData(). Но обработаны они будут только в том случае, если метод реализован. Если нет, то ничего страшного. Ошибки не будет.😎
Эти методы напоминают своеобразные плагины внутри плагинов. На мой взгляд излишнее усложнение, хотя сеттеры и геттеры должны заниматься по идее только сеттерством и геттерством, а проверку/ приведение типов можно отдать в методы onSet... / onGet....