YeaHub Tech
412 subscribers
343 photos
15 videos
2 files
450 links
Новые технологии, советы и обучающие материалы

YeaHub — это платформа для IT-специалистов, объединяющая обучение, карьерный рост, развитие и сообщество единомышленников.

Платформа: https://yeahub.ru

Для связи: @ruslan_kuyanets
Download Telegram
#webdev

🔖 Что если не отправлять HTML вообще?

Знаете, что делает большинство фреймворков? Они генерируют HTML на сервере, отправляют его вам, а потом начинают танцы с бубном под названием "гидратация". Тяжело, медленно, дорого.

А разработчик Neo.mjs подумал: "А что если вообще не отправлять HTML?"

И сделал приложение, которое стартует с пустым body. Звучит дико?


🔸Вот как это происходит:

Браузер получает крошечные JS-генераторы, кэширует их один раз, и всё. Дальше с сервера летит только лёгкий JSON с данными — никакого HTML-мусора. Генераторы строят интерфейс на лету, и навигация работает так быстро, что кажется, будто страницы уже загружены.

Внутри этого приложения живёт редактор Monaco — 3 мегабайта кода из VSCode. Обычно такой монстр хоронит любые метрики производительности. Но здесь он летает. Более того — вы пишете код прямо в нём, импортируете модули, и они вживую встраиваются в приложение. Без перезагрузки. Магия? Нет, SharedWorker.


🔸Самое безумное:

Вы можете открыть компонент и буквально вытащить его в другое окно браузера. Не создать копию. Не открыть iframe. А переместить тот же самый JS-объект! Открыли три окна? Они все синхронизированы, делят состояние, работают как один организм.

И вот кульминация: production-версия приложения (минифицированная, оптимизированная) и development-код из редактора работают одновременно в одном SharedWorker, не конфликтуя. До недавнего времени это было технически невозможно.


🔸Почему это важно?

Потому что main thread свободен. Пока другие фреймворки душат браузер вычислениями, Neo.mjs спокойно выдаёт 60 FPS, потому что вся тяжёлая работа в воркере. Плюс экономия трафика — для корпораций с тысячами пользователей это реальные деньги.

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

Код открыт, MIT-лицензия. И да, там спрятан секретный режим "cube layout". Просто потому что можно 😎

Демо | GitHub


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
#Cloudflare

🔖Cloudflare запустила свой стейблкоин для AI-агентов

Cloudflare представила Net Dollar — криптовалюту, привязанную к доллару 1:1. Но это не очередной стейблкоин для трейдеров, а инфраструктура для "экономики агентов".


🔸Зачем это нужно?
Идея в том, что AI-агенты смогут совершать платежи автоматически: покупать билеты, когда цена упала, оплачивать API-запросы или заказывать продукты. Всё это — без участия человека и мгновенно.

Cloudflare считает, что традиционные банковские переводы слишком медленные для такого сценария. Net Dollar обещает:
• Платежи из любой точки мира
• Мгновенные микротранзакции
• Новую модель монетизации для разработчиков


🔸Что говорит CEO:
"Десятилетиями интернет жил на рекламе и банковских переводах. Следующая эра — это оплата по факту использования и микротранзакции", — Мэтью Принс.

🔸Скептически:
Вопрос в том, нужен ли для этого ещё один стейблкоин, или это решение в поисках проблемы. Пока что AI-агенты с кошельками звучат как концепция из далёкого будущего.


🔸Бонус: на прошлой неделе Cloudflare также анонсировала свой email-сервис — Cloudflare Email Service, который объединит отправку и маршрутизацию писем в одном интерфейсе.


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
#GitHub

🔖Рабочий процесс на GitHub: как работать профессионально

Работа в команде требует чёткой организации. Разбираем ключевые этапы GitHub workflow — от создания issue до слияния кода.

🔸1. Issues — начало всего

Формулируем задачи по шаблону:
[FIX]: Fix auth flow
[STYLE]: Improve login page styling
[FEATURE]: Implement admin dashboard


Крупные задачи разбиваем на подзадачи с префиксом [SUB-FEAT].

🔸2. Ветки

Создаём ветку отдельно от main, чтобы ошибки не попали в продакшн. GitHub позволяет привязать issue к ветке — после мержа проблема закроется автоматически.

Формат именования:
<type>/<issue-number>/<short-description>
fix/92/fix_auth_flow
feature/15/implement_dashboard


🔸3. Коммиты

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

Примеры:
feat: implement dashboard header
fix: fix data fetching
refactor: improve API readability


Для монорепозиториев добавляем scope:
feat(front): implement dashboard header
feat(back): implement dashboard APIs


🔸4. Pull Request

После пуша создаём PR. Название должно отражать ветку без номера issue:
feature/implement-dashboard
style/improve-login-page-styling


Добавляем описание, связываем с issue, назначаем ревьюверов, ставим метки (bug, feature, enhancement).

🔸5. Code Review

Рекомендуется минимум 2 ревьювера на каждый PR. Это помогает найти больше проблем и делиться знаниями внутри команды.

🔸6. Merge

После аппрува мержим PR — связанная issue закроется автоматически. Не забываем удалить ветку, чтобы держать репо в чистоте.

✔️ Итого:
Issue → Branch → Commits → PR → Review → Merge

Полезные типы:
feat, style, fix, enhance, refactor, docs

Scope'ы для монорепо:
front, back, api, db, docs, test, chore

Лайфхак: используйте Git-хук pre-commit для автоматической проверки формата коммитов.

Поначалу кажется сложно, но быстро входит в привычку.


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
This media is not supported in your browser
VIEW IN TELEGRAM
#Video

🔖Улучшение воспроизведения видео с помощью ExoPlayer

Команда Reddit поделилась практическими советами по оптимизации видео на Android, подкреплёнными реальными production-данными. Разбираем ключевые находки.


🔸Доставка контента: MP4 vs DASH

75% видео на Reddit длятся менее 45 секунд. Команда провела эксперимент: для коротких роликов заменили адаптивный DASH на обычный MP4.

Результаты для видео < 45 сек:
• Ошибки воспроизведения: -5,5%
• Выход до старта видео: -2,5%
• Общие просмотры: +1,7%

Вывод: для короткого контента адаптивные протоколы избыточны — MP4 быстрее и надёжнее.


🔸Кэширование: подводные камни

Проблемы SimpleCache:
• ExternalDir недоступен везде (например, на OPPO) — лучше использовать internalDir
• Может удалять другие файлы — нужна отдельная папка
• Бьёт по диску в конструкторе — создавайте в фоновом потоке
• URL как ключ — если у вас разные сигнатуры, нужна своя фабрика ключей


🔸Предзагрузка контента

Протестировали варианты: следующее видео, два параллельно, только MP4. Лучше всего сработала предзагрузка только следующего видео:

• Загрузка < 500ms: +1,9%
• Загрузка > 1000ms: -0,872%

Попытка учитывать пропускную способность (2-20 Мбит/с) провалилась — метрики ухудшились.


🔸LoadControl: настройка буферизации

По умолчанию значения буфера в ExoPlayer слишком большие для коротких видео. Reddit снизил:
• bufferForPlaybackAfterRebufferMs: 1000ms
• minBuffer и maxBuffer: 20000ms

Результаты:
• Загрузка < 250ms: +2,7%
• Загрузка < 500ms: +4,4%
• Загрузка > 1000ms: -11,9%
• Ребуферинг: -4,8%
• Общие просмотры: +1,5%

Самый впечатляющий эксперимент!


🔸BandwidthMeter и адаптивный битрейт

Проблема: предзагрузка через DownloadManager не учитывалась в общей пропускной способности. После фикса:

• Разрешение видео: +1,4%
• Просмотры в цепочке: +0,5%
• Смена битрейта: -0,5%
• Загрузка > 1000ms: +0,3% (компромисс)


🔸Декодеры

Ошибка 4001 (декодер недоступен) — частая проблема. Решения:
• Включить fallback на программный декодер
• Запрашивать audio focus явно
• Рассмотреть пул плееров или один экземпляр

Fallback снизил ошибки на 0,9%.


🔸Рендеринг: SurfaceView vs TextureView

SurfaceView эффективнее (рендерит на GPU напрямую), но до Android N были проблемы с анимациями.

В Compose постоянное re-inflation занимало до 30ms. Решение — собственный ViewPool:

• Загрузка < 250ms: +1,7%
• Просмотренные минуты: +1,4%
• P50 создания: улучшено в 30 раз (1ms)


🔸Ключевой вывод

Всё упирается в аналитику и A/B-тесты. Обязательные метрики:
• Время до первого кадра
• Ребуферинг
• Старт/стоп воспроизведения
• Ошибки

ExoPlayer предоставляет AnalyticsListener для трекинга.


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Forwarded from YeaHub
🚀 Прокачиваем подготовку к собеседованиям на максимум!

Если ты ещё не заглядывал(а) в 👍 «Авось Прорвёмся» — самое время.
Это проект Ани для самостоятельной подготовки, где есть:
- 1000+ записей реальных собеседований с разбором задач и вопросов;
- контакты рекрутеров;
- гайды по резюме, трудоустройству и стратегиям поиска работы.
- задачи и вопросы с собеседований

Мы в YeaHub продолжаем добавлять собеседования из этих чатов — теперь их можно проходить в тренажёре, тренируя ответы на реальные вопросы работодателей.

🎧 Сначала слушай, как проходят настоящие собесы,
💡 потом — разбирай и практикуй те же кейсы в тренажёре.


Направления: iOS, Android, Frontend, Python, Go, Java, C#, QA, Analytics.

👉 Обучайтесь, общайтесь, растите вместе с YeaHub и Авось Прорвемся
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3🔥3🤝3
#MachineLearning

🔖 Как Random Forest вычисляет продавцов-мошенников на маркетплейсах

Автор столкнулась с обманом на маркетплейсе и решила разобраться: можно ли с помощью ML выявлять мошенников до того, как они обманут покупателей?


🔸 Данные

Набор данных с Kaggle содержит информацию о продавцах C2C-платформы: подписчики, активность, количество товаров, давность входа. Целевая переменная Fraud (1 = мошенник) добавлена на основе несоответствия отправленного товара описанию.

Проблема: датасет несбалансирован — среднее значение Fraud меньше 0.5, что означает перекос в сторону честных продавцов.


🔸 Построение модели

Используется Random Forest с GridSearchCV для подбора гиперпараметров. Ключевой момент — параметр class_weight: ['balanced', 'balanced_subsample'] для борьбы с дисбалансом классов.

Метрика оптимизации — ROC AUC score.

Первый запуск:
paramdict = {
'criterion': ['gini','entropy'],
'min_samples_leaf': range(1,5),
'class_weight': ['balanced','balanced_subsample'],
...
}


Результат показал, что min_samples_leaf=4 (верхняя граница диапазона) — нужно расширить поиск.

Второй запуск:
'min_samples_leaf': range(4,10)


Лучшие параметры оказались внутри диапазона — модель найдена.

Результат: Recall для класса 1.0 (мошенники) = 69.77%

Недостаточно для production!


🔸 Оптимизация порога

Автор предлагает сместить акцент с точности на полноту (Recall). Логика:

Два типа ошибок:
- False Negative: пропустили мошенника (опасно!)
- False Positive: заблокировали честного продавца (менее критично)

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

ROC-кривая в 3D

Построена интерактивная 3D ROC-кривая с осями:
- X: False Positive Rate
- Y: True Positive Rate
- Z: Probability Threshold

Автор отмечает три точки компромисса:

Красная черта: FPR ≈ 36%, порог = 0.35
Оранжевая: FPR ≈ 50%, порог = 0.25
Зелёная: FPR ≈ 60%, порог = 0.20

Выбор зависит от толерантности к ложным срабатываниям. Автор выбрала зелёную (порог 0.20) как оптимальную.


🔸 Финальные результаты

После сдвига порога до 0.20:

Recall для класса 1.0 = 90.11%

Прирост с 69.77% до 90.11% — модель теперь обнаруживает 9 из 10 мошенников!


🔸 Ключевые выводы

1. Дисбаланс классов решается через class_weight='balanced'
2. GridSearchCV помогает найти оптимальные гиперпараметры
3. Сдвиг порога критически важен для несбалансированных задач
4. ROC-кривая в 3D — удобный инструмент для выбора порога
5. Бизнес-логика важнее технических метрик: лучше больше FP, чем FN

Практическое применение:

Маркетплейс может использовать эту модель для:
- Автоматической проверки новых продавцов
- Дополнительной верификации подозрительных аккаунтов
- Приоритизации ручных проверок

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


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🔖 Распознавание речи в реальном времени с Python и AssemblyAI

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


🔸 Цель проекта

Каждую секунду микрофон передаёт аудио в AssemblyAI, которая распознаёт речь пословно. По завершении предложения ИИ понимает смысл и добавляет пунктуацию.


🔸 Установка

pip install pyaudio
pip install websockets


Mac: brew install portaudio — всё настроится автоматически

Windows: Скачайте wheel-файл с [неофициального сайта Python](https://www.lfd.uci.edu/~gohlke/pythonlibs/), затем:
pip install pyaudio‑0.2.11‑cp39‑cp39‑win_amd64.whl


Импорты:
import websockets
import asyncio
import base64
import json
from configure import auth_key
import pyaudio


🔸 Настройка конфигурации

Создайте файл configure.py:
auth_key = 'ваш_API_ключ_из_AssemblyAI'


Ключ можно получить на сайте AssemblyAI. Для real-time транскрибации нужен платный план.

🔸 Параметры аудио

FRAMES_PER_BUFFER = 3200
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 16000

p = pyaudio.PyAudio()

stream = p.open(
format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=FRAMES_PER_BUFFER
)

URL = "wss://api.assemblyai.com/v2/realtime/ws?sample_rate=16000"


Частота дискретизации 16000 синхронизирована с AssemblyAI API.


🔸 Асинхронные функции

Главная функция подключения:
async def send_receive():
async with websockets.connect(
URL,
extra_headers=(("Authorization", auth_key),),
ping_interval=5,
ping_timeout=20
) as _ws:

session_begins = await _ws.recv()
print(session_begins)


Функция отправки аудио:
async def send():
while True:
try:
data = stream.read(FRAMES_PER_BUFFER)
data = base64.b64encode(data).decode("utf-8")
json_data = json.dumps({"audio_data": str(data)})
await _ws.send(json_data)
except websockets.exceptions.ConnectionClosedError as e:
break
await asyncio.sleep(0.01)
return True


Функция получения текста:
async def receive():
while True:
try:
result_str = await _ws.recv()
print(json.loads(result_str)['text'])
except websockets.exceptions.ConnectionClosedError as e:
break


🔸 Запуск

send_result, receive_result = await asyncio.gather(send(), receive())

while True:
asyncio.run(send_receive())


Запустите командой:
python speech_recognition.py



🔸 Как это работает

1. Микрофон захватывает аудио каждые 3200 фреймов
2. Аудио кодируется в base64 и отправляется через WebSocket
3. AssemblyAI транскрибирует в реальном времени
4. Получаете два типа результатов:
- Частичные: посекундная транскрипция
- Финальные: с заглавными буквами и пунктуацией

Пример вывода:
hello
hello world
hello world this
Hello world, this is a test.



🔸 Особенности

- ИИ автоматически форматирует предложения
- Добавляет знаки препинания
- Исправляет грамматику
- Работает в реальном времени с минимальной задержкой


🔸 Ключевые технологии

- pyAudio — захват аудио с микрофона
- websockets — двусторонняя связь с API
- asyncio — асинхронная обработка
- AssemblyAI — ML-движок для распознавания речи

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


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🔖 NPM-пакеты: что это, откуда взялись и когда их использовать

Пакеты NPM — компоненты многократно используемого кода, которые делают JavaScript-разработку более управляемой. Разбираемся в истории, преимуществах и сценариях использования.


🔸 Что такое NPM-пакеты?

Пакет NPM — многократно используемый код JavaScript, которым можно делиться через реестр NPM. От простых утилит до полноценных фреймворков.

Ключевые особенности:
• Многократное использование — написал один раз, используй везде
• Контроль версий через семантическое версионирование (1.0.0)
• Совместная работа через публичный реестр


🔸История: как было до NPM

Начальный период (до 2009):
• Copy-paste — копировали код с форумов, что вело к несоответствиям
• Ручная загрузка — скачивали jQuery с сайта и подключали через <script>
• Git-репозитории — клонировали вручную, управляли зависимостями самостоятельно
• Браузерные войны — адаптировали код под IE или Netscape

Рождение NPM (2009):

Выход Node.js позволил JavaScript работать на серверах. Появилась потребность в эффективном обмене кодом. NPM решил проблему:
• Централизованный реестр пакетов
• Простая установка: npm install <package-name>
• Автоматическое управление зависимостями


🔸 Когда СТОИТ создавать NPM-пакет

1. Многократное использование

Если код решает общую проблему в нескольких проектах:

// Валидатор email
function isValidEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}

// Публикуем как пакет
npm install my-email-validator


2. Межкомандное сотрудничество

Для больших организаций, где фронтенд, бэкенд и мобильные команды используют общий код:

// Общие типы TypeScript
export type User = {
id: string;
name: string;
email: string;
};

npm install @company/shared-types


3. Вклад в open source

Публикация помогает другим разработчикам и укрепляет вашу репутацию.

4. Заполнение пробела

Если для вашей проблемы нет решения — создайте пакет, который интегрируется со сложным API или форматирует данные уникальным образом.


🔸 Когда НЕ стоит создавать пакет

1. Код слишком специфичен

Скрипт, работающий только с вашей уникальной схемой БД, не имеет смысла публиковать.

2. Слишком простой код

// Слишком просто для пакета
const reverseString = (str) => str.split('').reverse().join('');


Лучше поделиться как фрагментом или включить в существующую библиотеку.

3. Уже есть лучшее решение

Перед созданием проверьте реестр NPM. Зачем дублировать date-fns?

// Ваша функция
function formatDate(date) {
const d = new Date(date);
return `${d.getMonth() + 1}/${d.getDate()}/${d.getFullYear()}`;
}

// Уже есть date-fns
import { format } from "date-fns";
console.log(format(new Date("2023-01-01"), "MM/dd/yyyy"));


4. Нет ресурсов на поддержку

Публикация = обязательства: обновления, исправление багов, поддержка пользователей. Пакет с хакатона, который никто не будет поддерживать, лучше не публиковать.


🔸 Итого

NPM-пакеты — основа современной JavaScript-разработки. Они позволяют:
• Переиспользовать код
• Обеспечивать консистентность в командах
• Делиться решениями с сообществом

Но помните: не каждый код должен стать пакетом. Оцените масштаб задачи и готовность к долгосрочной поддержке.


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🔖 6 лайфхаков для улучшения JavaScript-кода

Современный JavaScript развивается стремительно. Разбираем 6 фич, которые делают код короче и чище.


1. Опциональные цепочки (?.)

Безопасная навигация по объектам без множественных проверок:

const someObject = {
profile: {
firstName: 'Nicky',
lastName: 'Christensen'
}
}

// ✅ С optional chaining
if (someObject?.profile?.firstName) {
console.log(someObject.profile.firstName)
}

// ❌ Старый способ
if (someObject && someObject.profile && someObject.profile.firstName) {
console.log(someObject.profile.firstName)
}

// Не выполнится, если свойства нет
if (someObject?.profile?.name) {
console.log('Not executed')
}


2. Оператор нулевого слияния (??)

Возвращает правый операнд только если левый null или undefined:

const falsy = false;
const emptyString = '';
const nullish = null;

console.log(falsy ?? 'Default'); // false
console.log(emptyString ?? 'Default'); // "" (не заменяется!)
console.log(nullish ?? 'Default'); // "Default"

// VS оператор ||
console.log(falsy || 'Default'); // "Default"
console.log(emptyString || 'Default'); // "Default"
console.log(nullish || 'Default'); // "Default"


Преимущество `??`: не заменяет false, 0 или "" — только null/undefined.

3. Динамический импорт

Асинхронная загрузка модулей (code splitting):

let someAsyncModule = await import('/modules/my-module.ts');


4. Promise.allSettled()

Узнать результат всех промисов, даже отклонённых:

const promise1 = Promise.resolve("OK");
const promise2 = Promise.reject("Error");
const promise3 = Promise.resolve("Done");

Promise.allSettled([promise1, promise2, promise3])
.then((results) => console.log(results));

// Результат:
// [
// { status: 'fulfilled', value: 'OK' },
// { status: 'rejected', reason: 'Error' },
// { status: 'fulfilled', value: 'Done' }
// ]


Отличие от `Promise.all()`: не падает при reject, показывает статус каждого промиса.

5. Spread-оператор (...)

Для массивов:
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = [...arr1, ...arr2]; // [1,2,3,4,5,6]


Для объектов:
const basePerson = {
name: 'Nicky C',
country: 'DK'
}

const footballer = {
...basePerson,
team: 'Man UTD',
shirtNumber: '11'
}
// { name: 'Nicky C', country: 'DK', team: 'Man UTD', shirtNumber: '11' }


6. Деструктуризация объектов

Распаковка значений из объекта в переменные:

const footballer = {
name: 'Nicky C',
team: 'Man UTD',
shirtNumber: '11'
}

const { team, shirtNumber } = footballer;
console.log(team, shirtNumber); // "Man UTD" "11"



🔸Итог

- Сокращают код
- Улучшают читаемость
- Снижают количество багов
- Стандарт в современном JavaScript


Используйте уже сегодня!


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
🔖 Моделирование данных в современном стеке данных 2.0

Зачем моделировать данные, если Snowflake всё обработает сам? Разбираемся на примере.


🔸 Задача

Проанализировать элементы, которые пользователи видят в списке (товары, результаты поиска, посты в ленте).

Аналитикам нужно:
- Оценить CTR
- Определить влияние позиции (1-я vs 10-я)


🔸Современный стек данных

Этап 1: Сбор данных

Фронтенд отправляет JSON-событие:
{
"user_id": 3299223,
"event_time": "2022-04-21 12:31:55 UTC",
"items_shown": [1037287139, 1727228887, ..., 1665899805]
}


Этап 2: Доставка

API → Kafka/Airbyte/Fivetran → Snowflake → таблица RAW_Events

Этап 3: Готово!

Преимущества:
- Не нужно думать о масштабируемости
- Не нужно преобразовывать данные
- JSON работает из коробки


🔸 Анализ без моделирования

Запрос 1: Развернуть массив
SELECT 
user_id,
value::NUMBER as item_id,
index as position
FROM RAW_Events,
LATERAL FLATTEN(input => items_shown)


Запрос 2: Сколько пользователей видели item на позиции 1?
SELECT COUNT(DISTINCT user_id)
FROM (
SELECT user_id, value, index
FROM RAW_Events,
LATERAL FLATTEN(input => items_shown)
)
WHERE value = 1037287139
AND index = 0
AND DATE(event_time) = CURRENT_DATE


🔸 Проблемы:
- Сложный синтаксис
- Читает ВСЕ JSON из таблицы
- Распаковывает и разворачивает каждый раз
- Медленно и дорого
- При JOIN'ах всё повторяется


🔸 Решение: моделирование

Создаём таблицу Events:
CREATE TABLE Events AS
SELECT
user_id,
event_time,
value::NUMBER as item_id,
index as position
FROM RAW_Events,
LATERAL FLATTEN(input => items_shown)


Обновляем инкрементально (ежедневно/ежечасно).

Запрос 4: Тот же вопрос, но проще
SELECT COUNT(DISTINCT user_id)
FROM Events
WHERE item_id = 1037287139
AND position = 0
AND DATE(event_time) = CURRENT_DATE



🔸 Результаты (20M JSON)

Запрос 2 (без модели) | 47 сек | X-Small |
Запрос 4 (с моделью) | 0.5 сек | X-Small |

Разница: 94x быстрее!

При миллиардах JSON разница в 1000+ раз по времени и деньгам.


🔸 Когда нужно моделирование?

1. Данные достаточно большие
2. Запросы достаточно частые
3. Количество аналитиков ограничено
4. Нужна консистентность результатов


🔸 Итог

10-20 лет назад: аналитик не мог работать с сотнями миллионов строк без инженера данных.

Сегодня: современный стек позволяет исследовать данные самостоятельно. Моделирование не обязательно, но:
- Ускоряет запросы
- Упрощает SQL
- Снижает затраты
- Повышает производительность

Современный стек даёт свободу. Моделирование даёт скорость.


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
🔖 JavaScript vs Gleam: пишем Todo CLI

Создаём одинаковое приложение на двух языках и сравниваем подходы.


🔸 Что делаем

CLI-приложение с 4 командами:
- list — показать все задачи
- add — добавить задачу
- done — отметить выполненные
- clear — удалить все

Формат хранения:
[ ] заголовок задачи
[*] выполненная задача


🔸JavaScript: Setup

pnpm init


Node v20, ESM модули, без сторонних библиотек. Файл todo.mjs.

Добавить задачу:
async function addTodo(filePath, todoTitle) {
return await useTodoFile(filePath, async (fd) => {
fd.appendFile(`[ ] ${todoTitle}\n`)
})
}


Форматированный список:
async function formattedTodos(filePath) {
return await useTodoFile(filePath, async (fd) => {
const content = await fd.readFile('utf8')
return content
.split('\n')
.filter((title) => title)
.map((title, index) => {
const isDone = /^\[\*\]/.test(title)
const rawTitle = title.match(/^\[[\s*]\]\s(.+)/)?.[1]
return `${index + 1} ${isDone ? strikethrough(rawTitle) : rawTitle}`
})
.join('\n')
})
}


Отметить выполненной:
async function markDone(filePath, ids) {
return await useTodoFile(filePath, async (fd) => {
const content = await fd.readFile('utf8')
const lines = content.split('\n')
ids.forEach((id) => {
lines[id - 1] = lines[id - 1].replace(/^\[\s\]/, '[*]')
})
await fd.truncate()
await fd.writeFile(lines.join('\n'))
})
}


🔸 Gleam: Setup

gleam new gleam_todos
gleam add argv simplifile gleam_community_ansi


Добавить задачу:
pub fn add_todo(file_path: String, title: String) -> Result(Nil, Nil) {
simplifile.append(file_path, "[ ] " <> title <> "\n")
|> result.replace(Nil)
|> result.nil_error
}


Форматированный список:
pub fn formatted_todos(file_path: String) -> Result(String, Nil) {
simplifile.read(file_path)
|> result.map(split_to_lines)
|> result.map(format_todo_lines)
|> result.map(join_lines)
|> result.nil_error
}

fn format_todo_lines(lines: List(String)) -> List(String) {
list.index_map(lines, fn(line, index) {
case line {
"[ ] " <> rest -> int.to_string(index + 1) <> " " <> rest
"[*] " <> rest -> int.to_string(index + 1) <> " " <> ansi.strikethrough(rest)
_ -> line
}
})
}


Отметить выполненной:
pub fn mark_done(file_path: String, ids: List(Int)) -> Result(Nil, Nil) {
simplifile.read(file_path)
|> result.map(split_to_lines)
|> result.map(fn(lines) { mark_todo_done(lines, ids) })
|> result.map(join_lines)
|> result.try(fn(content) { simplifile.write(file_path, content) })
|> result.nil_error
}


🔸 Запуск

JavaScript:
node todo.mjs add "first todo"
node todo.mjs list


Gleam:
gleam run add "first todo"
gleam run list



Итог ⬇️

JavaScript:
- ✅ Знакомый синтаксис
- ✅ Нативная работа с файлами
- ❌ Многословный код
- ❌ Ручная обработка ошибок

Gleam:
- ✅ Чистый, лаконичный синтаксис
- ✅ Result для обработки ошибок
- ✅ Pattern matching вместо регулярок
- ✅ Pipes |> для цепочек операций
- ❌ Менее знакомый синтаксис

Автор предпочитает Gleam за красивый код, мощную обработку ошибок и простые зависимости.

Gleam код читается как описание того, что делает программа, а не как она это делает.


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
🔖 C++: подробное руководство по выводу массива

Массивы — фундаментальные структуры данных C++. Разбираем эффективные способы их вывода для отладки и визуализации.


🔸 Классический цикл for

void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
std::cout << arr[i];
if (i < size - 1) std::cout << ", ";
}
}


✅ Полный контроль над форматированием
✅ Легко менять разделители


🔸 Range-based for (C++11)

for (int i : std::array<int, 5>{arr, arr + size}) {
std::cout << i << " ";
}


✅ Лаконичнее
✅ Меньше ошибок с индексами


🔸STL: std::copy + std::ostream_iterator

std::copy(arr, arr + N, 
std::ostream_iterator<T>(std::cout, ", "));


✅ STL-подход
✅ Работает с любым типом


🔸Многомерные массивы

void print2DArray(int arr[][3], int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
std::cout << arr[i][j] << " ";
}
std::cout << std::endl;
}
}


Вложенные циклы для матриц.


🔸std::array — современный подход

template<typename T, size_t N>
void printArray(const std::array<T, N>& arr) {
for (size_t i = 0; i < N; ++i) {
std::cout << arr[i];
if (i < N - 1) std::cout << ", ";
}
}


✅ Размер известен на компиляции
✅ Безопасность + удобство


🔸 Пользовательские типы

struct Person {
std::string name;
int age;

friend std::ostream& operator<<(
std::ostream& os, const Person& p) {
return os << p.name << " (" << p.age << ")";
}
};


Переопределяем operator<< для кастомных объектов.


🔸 Практика: анализ сенсоров

struct SensorReading {
double temperature;
double humidity;

friend std::ostream& operator<<(
std::ostream& os, const SensorReading& sr) {
return os << "Temp: " << sr.temperature
<< "°C, Humidity: "
<< sr.humidity << "%";
}
};


🔸 Когда что использовать?

• Простой for — полный контроль
• Range-based — современный код
• std::copy — STL-стиль
• std::array — фиксированный размер
• operator<< — свои типы


⬇️Итог

Эффективный вывод массива — это представление данных в понятном формате для отладки, анализа и работы с пользователем.


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
🔖 9 полезных инструментов CSS для веб-разработчиков

Написание CSS с нуля отнимает много времени. Эти бесплатные инструменты помогут ускорить разработку и повысить продуктивность.


1️⃣ Neumorphism

Генератор модного стиля "мягкого UI". Создаёт плавные тени и объёмные элементы.

Настройки:
🔸 Цвет
🔸 Размер
🔸 Радиус
🔸 Расстояние
🔸 Интенсивность теней

Автоматическая генерация CSS-кода для неоморфного дизайна.

2️⃣ CSS Clip-path Maker

Создание сложных фигур без знания свойства clip-path:
🔸 Многоугольники
🔸 Круги
🔸 Эллипсы
🔸 Кастомные формы

Визуальный редактор + готовый CSS-код.

3️⃣ Animista

Коллекция готовой CSS-анимации:
🔸 Входы/выходы элементов
🔸 Трансформации
🔸 Эффекты наведения
🔸 Текстовая анимация

Выбираете анимацию → редактируете → копируете код.

4️⃣ PurgeCSS

Удаление неиспользуемого CSS-кода.

Зачем нужен:
🔸 Фреймворки содержат много лишнего кода
🔸 Уменьшение размера файлов
🔸 Повышение производительности

Особенно полезен с Bootstrap, Tailwind и другими фреймворками.

5️⃣ Shadow Brumm

Генератор плавных теней.

Настройки:
🔸 Количество слоёв
🔸 Размытие
🔸 Смещение
🔸 Прозрачность

Создаёт реалистичные многослойные тени одним кликом.

6️⃣ CSS Gradient

Создание градиентных фонов:
🔸 Linear градиенты
🔸 Radial градиенты
🔸 Conic градиенты
🔸 Множественные цвета

Визуальный редактор + копирование кода.

7️⃣ CSS Scan

Премиум-инструмент для просмотра CSS любого элемента.

Возможности:
🔸 Наведите курсор → увидите все стили
🔸 Копирование кода одним кликом
🔸 Инспекция любого сайта

Идеально для изучения чужого кода и поиска вдохновения.

8️⃣ CSS Grid Generator

Генератор макетов CSS Grid.

Настройки:
🔸 Количество столбцов
🔸 Количество строк
🔸 Единицы измерения (fr, px, %)
🔸 Gap между элементами

Получаете CSS + HTML-код сетки.

9️⃣ Get Waves

Создание SVG-волн для фона.

Параметры:
🔸 Высота волн
🔸 Количество слоёв
🔸 Цвета
🔸 Инверсия

Готовый SVG + CSS для вставки в проект.


Итог ⬇️

Эти инструменты:
✅ Экономят время разработки
✅ Не требуют глубоких знаний CSS
✅ Генерируют чистый код
✅ Повышают продуктивность

Используйте их для быстрого создания красивых страниц без написания CSS с нуля.


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
🔖 Поддержка нескольких модальных окон в React Native

Команда Whitespectre создала библиотеку rn-modal-presenter для решения проблем с множественными модальными окнами в React Native.


❌ Проблемы стандартного подхода

1. Управление состояниями — нужна переменная для каждого модального окна
2. iOS ограничение — нельзя показать >1 модального окна одновременно
3. Размонтирование — при навигации модалки исчезают
4. Тихие ошибки — вторая модалка не появляется без предупреждений

Пример проблемы:

const App = () => {
const [showRateAppModal, setShowRateAppModal] = useState(false);
const [showAppRatedPositiveModal, setShowAppRatedPositiveModal] = useState(false);
const [showAppRatedNegativeModal, setShowAppRatedNegativeModal] = useState(false);
const [showActivateGadgetModal, setShowActivateGadgetModal] = useState(false);

return (
<SafeAreaView>
{showActivateGadgetModal && <ActivateGadgetModal />}
{showRateAppModal && <RateAppModal />}
{showAppRatedPositiveModal && <PositiveFeedbackModal />}
{showAppRatedNegativeModal && <NegativeFeedbackModal />}
</SafeAreaView>
);
};


Проблемы:
🔸 4 переменные состояния для 4 модалок
🔸 Сложная логика управления
🔸 При одновременном показе двух модалок — молчаливая ошибка на iOS

Почему сторонние библиотеки не помогли

react-native-modal:
🔸 Расширяет стандартный Modal
🔸 Те же ограничения iOS
🔸 Не решает проблему множественных окон

react-native-modalfy:
🔸 Поддерживает множественные модалки (JS-реализация)
❌ Много boilerplate кода
❌ Нельзя показать несколько экземпляров одной модалки

✅ Решение: rn-modal-presenter

Преимущества:

1. Императивный API — вызов из любого места
2. Без состояний — не нужны useState
3. Множественные модалки — из коробки
4. Гибкость — любой компонент как модалка
5. 100% JavaScript — работает кроссплатформенно

Установка:

yarn add @whitespectre/rn-modal-presenter


Использование:

import { ModalPresenterParent, showModal } from '@whitespectre/rn-modal-presenter';

const App = () => (
<ModalPresenterParent>
<SafeAreaView>
<Button
title="Activate Gadget"
onPress={() => showModal(ActivateGadgetModal, {})}
/>
</SafeAreaView>
</ModalPresenterParent>
);


Модалка управляет собой:

const ActivateGadgetModal = ({dismiss}: ModalContentProps) => (
<View style={styles.modal}>
<Text>Your Gadget has been activated</Text>
<Button
title="Close"
onPress={() => {
dismiss();
showModal(RateAppModal, {});
}}
/>
</View>
);


Сравнение подходов

Standard Modal:
❌ Много состояний
❌ Нет множественных модалок
❌ iOS ограничения
❌ Нет императивного API

react-native-modal:
❌ Много состояний
❌ Нет множественных модалок
❌ iOS ограничения
❌ Нет императивного API

react-native-modalfy:
✅ Без состояний
✅ Множественные модалки
✅ Императивный API
🔸Много boilerplate

rn-modal-presenter:
✅ Без состояний
✅ Множественные модалки
✅ Императивный API
✅ Минимум кода
✅ Несколько экземпляров одной модалки

Планы развития

🔸 Очередь модальных окон
🔸 Система приоритетов
🔸 Показ поверх Native вьюх
🔸 Кастомная анимация


Итог⬇️

rn-modal-presenter упрощает работу с множественными модальными окнами, убирая управление состояниями и предоставляя императивный API. Идеально для сложных флоу с несколькими последовательными модалками.


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
🔖 C++: руководство по сортировке строк

Сортировка строк — фундаментальная операция в программировании. Разбираем все методы от базовых до продвинутых.


1️⃣ Базовая сортировка: std::sort

#include <algorithm>
#include <string>

std::string str = "zycbwvutsrqponmlkjihgfedcba";
std::sort(str.begin(), str.end());
// Результат: "abcdefghijklmnopqrstuvwxyz"


Символы сортируются по значениям ASCII в порядке возрастания.

2️⃣ Сортировка вектора строк

std::vector<std::string> words = {"banana", "apple", "cherry", "date"};
std::sort(words.begin(), words.end());

// Результат:
// apple
// banana
// cherry
// date


Лексикографическая сортировка (словарный порядок).

3️⃣ Сортировка без учёта регистра

bool case_insensitive_compare(const std::string& a, const std::string& b) {
return std::lexicographical_compare(
a.begin(), a.end(),
b.begin(), b.end(),
[](char a, char b) {
return std::tolower(a) < std::tolower(b);
}
);
}

std::vector<std::string> words = {"Banana", "apple", "Cherry", "date"};
std::sort(words.begin(), words.end(), case_insensitive_compare);

// Результат:
// apple
// Banana
// Cherry
// date


Пользовательский компаратор игнорирует регистр, но сохраняет исходное написание.

4️⃣ Сортировка по длине

std::vector<std::string> words = {"short", "very long string", "medium"};

std::sort(words.begin(), words.end(),
[](const std::string& a, const std::string& b) {
return a.length() < b.length();
});

// Результат:
// short
// medium
// very long string


Лямбда-функция сравнивает длину строк.

5️⃣ Практика: сортировка имён

struct Name {
std::string first;
std::string last;
};

std::sort(names.begin(), names.end(),
[](const Name& a, const Name& b) {
if (a.last != b.last) {
return a.last < b.last;
}
return a.first < b.first;
});

// Входные данные:
// John Doe, Jane Doe, Alice Smith, Bob Johnson

// Результат:
// Jane Doe
// John Doe
// Bob Johnson
// Alice Smith


Многокритериальная сортировка: сначала по фамилии, затем по имени.

6️⃣ Unicode с библиотекой ICU

#include <unicode/coll.h>

bool compare_unicode(const std::string& a, const std::string& b) {
UErrorCode status = U_ZERO_ERROR;
icu::Collator* collator = icu::Collator::createInstance(icu::Locale(""), status);

icu::UnicodeString ua = icu::UnicodeString::fromUTF8(a);
icu::UnicodeString ub = icu::UnicodeString::fromUTF8(b);

UCollationResult result = collator->compare(ua, ub, status);
delete collator;

return result == UCOL_LESS;
}

std::vector<std::string> words = {"café", "cafe", "résumé", "resume"};
std::sort(words.begin(), words.end(), compare_unicode);


Корректная обработка диакритических знаков.

🔸Оптимизация производительности

1. std::string_view
Избегайте копирования для строк только для чтения.

2. std::stable_sort
Сохраняет относительный порядок эквивалентных элементов.

3. Параллельная сортировка (C++17+)
Для больших наборов данных.

Бенчмарк:
100,000 строк по 10 символов:
• std::sort: 0.245 сек
• std::stable_sort: 0.298 сек


🔸Выбор метода

Простая сортировка:
→ std::sort

Без учёта регистра: → Кастомный компаратор

По длине: → Лямбда-функция

Unicode: → ICU библиотека

Сохранить порядок: → std::stable_sort

Большие данные: → Параллельная сортировка (C++17+)


Итог⬇️

Сортировка строк в C++ — от базовой лексикографической до сложной многокритериальной — позволяет эффективно обрабатывать любые задачи.

Выбирайте метод исходя из требований: регистр, Unicode, критерии, производительность.


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
🔖 Слоты в Angular: делаем компоненты по-настоящему гибкими

При создании UI-библиотек один из главных вызовов — найти баланс между гибкостью и простотой API. Разберём, как правильно работать с проекцией контента в Angular.


Два основных подхода:

1️⃣ ng-content — для простых случаев

Отлично подходит, когда:
• Слоты всегда определены
• Не нужен условный рендеринг
• Контекст не требуется

<app-field>
<span slot="label">Label</span>
<input>
</app-field>



2️⃣ ng-template — для сложных сценариев

Используйте, когда нужно:
• Передать контекст в слот
• Ленивая инициализация контента
• Условный рендеринг

<ng-template #suffix let-control>
<button [class.bg-red]="control.invalid">
Search
</button>
</ng-template>



💡 Практический совет:

Создайте универсальную директиву для унификации работы со слотами:

@Directive({
selector: 'ng-template[slot]'
})
export class Slot {
readonly template = inject(TemplateRef);
readonly name = input.required<string>({ alias: 'slot' });
}


Это даёт:
✅ Единообразный синтаксис
✅ Простую документацию
✅ Чистую идентификацию контента

🔸Реальный пример:

Компонент Accordion может комбинировать оба подхода:
🔸title — через ng-content (статичный текст)
🔸icon — через ng-template (нужен контекст открыт/закрыт)
🔸content — оба варианта (eager/lazy загрузка)


Итог ⬇️

Правильная работа со слотами — это не выбор между ng-content и ng-template, а понимание, когда использовать каждый инструмент. Комбинируйте оба подхода в зависимости от задачи, и ваши компоненты станут по-настоящему переиспользуемыми и удобными для других разработчиков.


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
🔖 Развертывание Cloud Functions в GCP с помощью Terraform

Автоматизируйте развертывание serverless функций с Infrastructure as Code!


Что будем делать:

Создадим полностью автоматизированную инфраструктуру:
🔸Bucket для загрузки файлов
🔸Bucket для хранения исходного кода
🔸Cloud Function, которая триггерится при загрузке файла


Структура проекта:

cloud_function_project/
├── terraform/
│ ├── backend.tf
│ ├── function.tf
│ ├── main.tf
│ ├── storage.tf
│ └── variables.tf
└── src/
├── main.py
└── requirements.txt


Ключевые шаги:

1️⃣ Создаем Python функцию
def hello_gcs(event, context):
print('Event ID:', context.event_id)
print('Bucket:', event['bucket'])
print('File:', event['name'])


2️⃣ Настраиваем Terraform
Объявляем провайдера, переменные и ресурсы GCP

3️⃣ Упаковываем и деплоим
data "archive_file" "source" {
type = "zip"
source_dir = "../src"
}

resource "google_cloudfunctions_function" "function" {
runtime = "python37"
entry_point = "hello_gcs"
event_trigger {
event_type = "google.storage.object.finalize"
}
}


🔸Развертывание:

cd ./terraform
terraform init
terraform plan
terraform apply


💡 Преимущества подхода:

✅ Воспроизводимость инфраструктуры
✅ Версионирование через Git
✅ Простое масштабирование
✅ Автоматизация CI/CD
✅ Безопасность через код


Итог ⬇️

Terraform + Cloud Functions = идеальная комбинация для serverless архитектуры. Описываете инфраструктуру декларативно, а Terraform заботится о развертывании и управлении состоянием. Код становится единственным источником истины для вашей инфраструктуры.

Бонус: Поддержка Python, Node.js, Go, Java, .NET, Ruby и PHP!


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
🔖 C++: полное руководство по «приведению вверх»

Разбираем фундаментальную концепцию ООП, которая делает ваш C++ код гибким и расширяемым!


Что такое Upcasting?

Преобразование указателя/ссылки производного класса в указатель/ссылку базового класса — движение вверх по иерархии наследования.

Dog* dog = new Dog();
Animal* animal = dog; // Приведение вверх
animal->makeSound(); // "The dog barks"


Два способа приведения:

1️⃣ Неявное (рекомендуется)
Animal* animal1 = dog;


2️⃣ Явное
Animal* animal2 = static_cast<Animal*>(dog);


🔸 Приведение вверх + Полиморфизм

Пишите код для базового класса, работайте с любыми производными:

std::vector<Shape*> shapes;
shapes.push_back(new Circle(5));
shapes.push_back(new Rectangle(4, 6));

for (const auto& shape : shapes) {
shape->draw(); // Вызов правильного метода
cout << shape->area();
}


❗️Важно про деструкторы:

class Base {
public:
virtual ~Base() {} // ОБЯЗАТЕЛЬНО virtual!
};


Без виртуального деструктора не вызовется деструктор производного класса при удалении через базовый указатель → утечки памяти!

🔸 Практический пример: игровой движок

class GameEngine {
std::vector<std::unique_ptr<GameObject>> objects;
public:
void addObject(std::unique_ptr<GameObject> obj);
void updateAll(); // Обновляет всё единообразно
void renderAll(); // Рисует всё единообразно
};

engine.addObject(std::make_unique<Player>());
engine.addObject(std::make_unique<Enemy>());
engine.addObject(std::make_unique<Projectile>());


Храним Player, Enemy, Projectile в одном контейнере и работаем с ними единообразно!


💡 Upcasting vs Downcasting:

🔸 Upcasting — всегда безопасно, обычно неявно
🔸 Downcasting — может быть опасно, используйте dynamic_cast

Derived1* d1 = dynamic_cast<Derived1*>(basePtr);
if (d1) {
d1->derived1Method(); // Безопасно!
}


🔸Множественное наследование:

class C : public A, public B {};

C* c = new C();
A* a = c; // Приведение к A
B* b = c; // Приведение к B
// Оба работают корректно!



Итог ⬇️

Приведение вверх — основа полиморфизма в C++. Используйте его для:
✅ Единообразной работы с разными типами объектов
✅ Гибкого и расширяемого кода
✅ Реализации паттернов проектирования
✅ Создания универсальных контейнеров

Помните: всегда делайте базовый деструктор виртуальным при использовании полиморфизма!


📎 Статья

🎙 Новости

📝 База вопросов
Please open Telegram to view this post
VIEW IN TELEGRAM