TGViewer
Channel Public Channel
EvApps

EvApps

@evapps_team

IT-aутстафферы из Тулы💚
https://evapps.ru/

Здесь пишем про веб- и мобильную разработку

▶️ Наш чат для системных аналитиков: https://t.me/pro_sa_evapps

▶️ Посмотреть, как мы живём: https://vk.com/evapps
Subscribers
199
Photos
1.2K
Videos
52
Links
253

Showing posts older than #1523 · Back to latest

Older Posts 20 shown
Post #1521 90
🌐 Кэш, про который все забывают
Все гоняются за скоростью: CDN, Redis, сжимаем картинки.
А самый простой кэш уже встроен в каждый браузер — это HTTP-кэширование.
Только большинство лепит заголовки наугад, а потом ловит классику: «я выкатил фикс, а у юзеров всё ещё старая версия».

🔀 Есть два разных режима, и их вечно путают
1. «Вообще не спрашивай сервер».
Браузер берёт ответ из своей памяти и всё, никакого запроса по сети.
Самый быстрый вариант — данные появляются мгновенно.
2. «Спроси, но по-дешёвому».
Браузер стучится на сервер, но качает тело ответа только если оно реально поменялось.
Запрос лёгкий, мегабайты по сети не летят.

Нормальный кэш — это когда ты грамотно миксуешь эти два режима.

⏳ Cache-Control — сколько времени можно не дёргать сервер
Главный заголовок:
Cache-Control: max-age=3600

Переводится как «час эти данные считаются свежими, не трогай сервер».
Весь час браузер отдаёт ответ из памяти.
Ещё пара слов, которые важно не перепутать: — public — можно кэшировать и по пути, на CDN и прокси; — private — только в браузере юзера, для персональных данных; — no-store — не кэшировать нигде и никогда (пароли, оплата).

Типичный прострел себе в ногу: влепить большой max-age на HTML.
Потом выкатываешь обновление, а у людей висит старая страница ещё час, и ты ничего не сделаешь.

🏷 ETag — «слушай, а оно вообще менялось?» Вот тут и живёт дешёвая проверка.
Сервер вешает на ответ что-то вроде отпечатка:
ETag: "v23-abc123"

Браузер его запоминает и в следующий раз спрашивает: «у меня вот такой отпечаток, всё ещё актуально?».
Если на сервере тот же — он отвечает 304 Not Modified с пустым телом.
Браузер берёт данные из кэша, а по сети улетел только копеечный запрос вместо перекачки всего файла.
Красота.

🧩 Как это делают на нормальных проектах Весь фокус — разделить файлы на два типа.


Статика с хэшем в имени (app.a1b2c3.js) — кэшируем жёстко и надолго:
Cache-Control: public, max-age=31536000, immutable

Год жизни, immutable — «даже не перепроверяй».
Поменял код — поменялся хэш в названии, а значит это уже новый файл с новым адресом.
Старый кэш сам отваливается за ненадобностью.

HTML — почти не кэшируем, но проверяем:
Cache-Control: no-cache

И вот тут главный подвох: no-cache — это НЕ «не кэшировать».
Это «кэшируй, но каждый раз перепроверяй через ETag».
То есть HTML всегда свежий, а тяжёлые скрипты и картинки грузятся из кэша по вечному адресу.
Быстро и без залипшей старой версии.

Короче: no-cache и no-store — это вообще про разное, и половина болей с кэшем именно из-за того, что их считают одним и тем же.
Прежде чем прикручивать очередной Redis, глянь, что уже отдают твои заголовки — там частенько бесплатно лежит половина скорости.
А вы заголовки кэша руками крутите или как фреймворк из коробки поставил, так и живёте? 🤔

#web #http #performance #backend #frontend #dev
  • ✍ 1
  • 🔥 1
Post #1520 107
🧙 Git-команды, которыми мало кто пользуется (а зря)
add-commit-push знают все. reflog и stash — почти все. А вот это — уже реже, хотя именно оно превращает Git из «системы контроля версий» в нормальный рабочий инструмент.
Погнали по тем командам, до которых обычно доходишь годам к трём коммерческого стажа.

1. git worktree — несколько веток одновременно, без stash и клонов 🌲 Классика боли: пилишь фичу, прилетает срочный фикс.
Обычно ты либо стэшишь недоделку, либо клонируешь репо второй раз.
Ни то, ни другое не нужно:
git worktree add ../hotfix -b hotfix main

Это создаёт ВТОРУЮ рабочую папку рядом, на отдельной ветке, с тем же общим репозиторием.
Чинишь хотфикс в ../hotfix, а твоя недоделанная фича лежит нетронутой в основной папке.
Никакого переключения контекста. Закончил — git worktree remove.

2. git bisect run — ищет баг сам, пока ты пьёшь кофе 🤖 Про bisect многие слышали, но вручную помечать good/bad на двадцати коммитах — тоска. Отдай это скрипту:
git bisect start HEAD <старый_рабочий_хэш>
git bisect run ./test.sh

Git сам прогонит бинарный поиск: на каждом коммите запускает test.sh, exit 0 — коммит хороший, не 0 — плохой. Через минуту он выдаёт точный коммит, который всё сломал.
Работает с любым тестом, хоть с одной строкой на curl.

3. git log -S — когда именно появилась (или исчезла) эта строка 🕵️ Ситуация: в коде есть странный костыль или, наоборот, из кода пропала нужная строчка, и никто не помнит когда. Pickaxe:
git log -S "old_feature_flag" --oneline

Он находит не где строка есть, а коммиты, где её количество поменялось — то есть где её добавили или удалили. Мгновенно приводит к автору и контексту. -G — то же самое, но по регулярке.

4. git rerere — разрешаешь один и тот же конфликт один раз ♻️ Долгий ребейз или регулярный мёрж, где постоянно всплывает один и тот же конфликт в одном месте?
Включи один раз:
git config --global rerere.enabled true

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

5. git commit --fixup + autosquash — чистая история без мучений ✨ Ревьюер попросил поправить коммит из середины ветки.
Обычно это интерактивный ребейз и ручное перетаскивание. Проще:
git commit --fixup=<хэш_нужного_коммита>
git rebase -i --autosquash <база>

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

6. git log -L — история одной функции, а не всего файла 📜 Когда надо понять, как эволюционировал конкретный кусок кода:
git log -L :имя_функции:file.py

Git покажет только те коммиты и диффы, что трогали именно эту функцию.
Никакого продирания через историю файла на 2000 строк.
Суть простая: Git умеет сильно больше, чем «сохранить и откатить».
Половина рутины, которую мы делаем руками, у него уже автоматизирована — просто про эти команды редко рассказывают.

Что из этого уже в вашем арсенале? И чем сами пользуетесь, чего тут нет? 🤔

#git #dev #backend #tools #programming #devops
  • 👍 2
  • 🔥 1
Post #1519 104
⚡️ Бэкенд тормозит? Скорее всего это N+1
Когда какой-нибудь эндпоинт вдруг начинает отвечать по полсекунды, все первым делом лезут оптимизировать код: асинхронщина, воркеры, микрооптимизации.
А причина обычно куда скучнее — ты просто дёргаешь базу сотню раз там, где хватило бы одного запроса.

🐘 Как выглядит N+1 Берём список из 50 заказов и для каждого лезем за пользователем:
orders = Order.all            # 1 запрос — забрали заказы
for o in orders:
print(o.user.name) # +50 запросов, по одному на заказ

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

🔧 Как чинить Идея одна: вытащить всех пользователей сразу, одним-двумя запросами, а не по штучке в цикле.
В разных ORM это включается по-разному:
# Django
Order.objects.select_related("user")

# SQLAlchemy
session.query(Order).options(joinedload(Order.user))

# Rails
Order.includes(:user)

Под капотом это либо JOIN, либо второй запрос вида WHERE user_id IN (...).
Было 51 обращение — стало два.
На реальных данных разница огромная.

🔍 Как заметить Беда в том, что глазами N+1 не видно — код выглядит чистым.

Поэтому: — смотри SQL-лог в дев-режиме: если на один запрос к API летит пачка одинаковых SELECT-ов — вот он, красавец; — поставь профайлер (django-silk, bullet, rack-mini-profiler) — они прямо тычут носом; — или тупо считай запросы на эндпоинт.
Больше десятка на простую страницу — уже звоночек.

💾 И про кэш, пока не разогнались
Как только заходит разговор про скорость, все сразу хотят прикрутить Redis.
Но кэш — это не «сделать быстро», это «отложить проблему и получить новую»: теперь надо думать, когда его чистить.
Прежде чем кэшировать, честно ответь себе: — эти данные вообще часто читают и редко меняют?
если нет — кэш ни к чему; — что будет, когда они устареют и я отдам юзеру старьё? — как я вообще пойму, что пора обновлять кэш?

Часто выходит, что убрать N+1 и повесить нормальный индекс дают те же 200 мс выигрыша — только без лишнего слоя, который потом будет отдавать неактуальные данные и портить тебе вечера.
Правило простое: сначала померь и убери явную дичь в запросах, и только потом тащи кэш.
Наоборот — почти всегда дорога к боли.

#backend #performance #database #orm #optimization #dev
Post #1518 110
🗄 Почему твой SQL-индекс молчит
Знакомая история: повесил индекс, а запрос как тормозил, так и тормозит.
Индекс вроде есть, но планировщик смотрит на него и проходит мимо.
Собрал самые частые причины, из-за которых так происходит.
1. Обернул колонку в функцию 🔧 Вот это ломает индекс чаще всего:
WHERE YEAR(created_at) = 2024

Как только колонка попадает внутрь функции, индекс по ней уже не применить — и привет, seq scan по всей таблице. Лечится диапазоном:
WHERE created_at >= '2024-01-01'
AND created_at < '2025-01-01'

Ну или заводишь функциональный индекс, если без функции совсем никак.

2. LIKE, который начинается с % 🔍 Тут всё просто:
WHERE name LIKE '%anton'   -- бесполезно
WHERE name LIKE 'anton%' -- работает

B-tree умеет искать по началу строки, а не по середине. Если тебе реально нужен поиск по куску внутри — это уже полнотекстовый индекс или триграммы (в постгресе pg_trgm).

3. Порядок колонок в составном индексе 📚 Индекс (a, b) устроен как телефонная книга: сначала сортировка по a, и только внутри неё по b. Поэтому запрос, где есть только b, его не подхватит:
INDEX (user_id, created_at)

WHERE user_id = 5 -- норм
WHERE user_id = 5 AND created_at… -- норм
WHERE created_at > … -- мимо, левого префикса нет


4. Типы не совпали 🎭 Классика, на которой все хоть раз спотыкались:
-- phone у нас VARCHAR
WHERE phone = 89991234567 -- число против строки, индекс мимо
WHERE phone = '89991234567' -- порядок

База молча приведёт типы сама, а заодно тихо выкинет индекс.
Так что следи, чтобы тип значения совпадал с колонкой.

5. Индекс на колонке, где всего два значения 🎲 is_active, gender и прочие флаги индексировать почти бессмысленно.
Если под условие подходит половина таблицы, планировщику дешевле прочитать её целиком, чем скакать туда-сюда по индексу.
И он тут прав.
Индексы хороши там, где значение отсекает много строк, а не половину.

6. SELECT * мешает covering index 📦 Иногда индекс уже содержит все поля, которые тебе нужны, и база может отдать ответ прямо из него, не заглядывая в таблицу.
Красота. Но стоит написать SELECT * — и она вынуждена лезть в таблицу за каждой строкой ради остальных колонок. Бери только то, что реально используешь.

В общем, индекс — это не «создал и забыл».
Прежде чем гадать на кофейной гуще, открой EXPLAIN ANALYZE и посмотри, что там планировщик на самом деле делает.
Он-то не соврёт.

А у вас какой случай в духе «индекс есть, но его как бы нет» бесил сильнее всего? 🤔

#sql #database #postgres #backend #performance #dev
Post #1517 143
Двенадцать лет назад мы торжественно отделились от материнской компании и начали свой самостоятельный путь - то есть, все почти как у людей 😀
Сегодня EvApps - это порядка 100 спецов, которые каждый день усиливают ИТ-команды банков, страховых, маркетплейсов и промышленных компаний по всей России.
Более 450 проектов.
Более 100 клиентов, которые возвращаются к нам снова.

Двенадцать лет на ИТ-рынке — это несколько эпох.
И мы прошли их все💪

Менялись технологии, рынок труда (и не один раз!), правила игры. И каждый раз мы не просто удерживались на плаву - мы росли. Потому что за эти годы научились главному: быстро разбираться в новом, честно говорить с клиентом и не бросать проект, когда становится сложно.

Но цифры — это следствие, а причина всего — наша команда💚 Люди, которые остаются на проектах годами, а не месяцами, и клиенты потом просят «дайте нам ещё таких же!»

Спасибо каждому в команде EvApps — вы и есть компания!
Спасибо клиентам, которые доверяют нам свои задачи. И партнерам, которые всегда поддержат.

Нам 12, и мы только разгоняемся 🚀
  • 🔥 9
Post #1516 110
🧠 Что реально происходит, когда ты вызываешь LLM API
Ты пишешь await openai.chat(...), ждёшь ответ и думаешь, что тормозит твой код. Чаще всего код тут ни при чём. Между твоим fetch и текстом ответа лежит физика, железо и география — и большую часть задержки ты не оптимизируешь ни одной строчкой.
Разберём путь запроса по частям.

🌍 1. Дорога до дата-центра (это физика, а не баг) Сигнал в оптоволокне идёт примерно на 2/3 скорости света — это жёсткий предел, его не обойти. От Лагоса до Лондона ~5000 км, и только на дорогу туда-обратно уходит минимум ~50 мс — ещё до того, как сервер начал думать. С учётом маршрутизации и заторов набегает 100–200 мс чистой географии.
Почему так? Потому что почти вся LLM-инфраструктура живёт в us-east-1 (Вирджиния) и eu-west (Ирландия/Франкфурт). В Нигерии, для контраста, 17 дата-центров, в США — больше 5500. Чем ты дальше от железа, тем дороже тебе обходится каждый вызов — просто по расстоянию.

⚙️ 2. GPU считает ответ по одному токену Промпт приходит на GPU, и та прогоняет миллиарды операций слой за слоем, чтобы предсказать следующий токен (это примерно 3/4 слова). Дальше — ещё один, и ещё, строго по очереди. Ответ не появляется целиком: он собирается токен за токеном.
Отсюда два следствия: — Длинный промпт грузит вход, длинный ответ грузит выход. Оба стоят компьюта. — Rate limits — это не вредность вендора, а отражение физического потолка железа. Один Nvidia H100 стоит ~$30 000, и он не резиновый.

❄️ 3. Cold start Модель — это сотни гигабайт весов, которые надо загрузить в память GPU. Если запросов давно не было, система могла выгрузить модель, чтобы освободить ресурсы. Первый запрос после простоя ждёт, пока веса заедут обратно — поэтому он заметно медленнее следующих. Тот самый «почему первый ответ тупил, а дальше полетело».

🛠 Что с этим делать на практике — Стримь ответ. Показывай токены по мере поступления, а не жди весь блок. Реальная задержка та же, но воспринимается сильно быстрее. — Кэшируй агрессивно. Повторяющиеся и почти одинаковые промпты — из кэша. Экономишь и инференс, и дорогу. — Бери модель под задачу. Для классификации, извлечения и коротких ответов 7B обычно быстрее и дешевле 70B — и по качеству не хуже.
Мораль простая: когда LLM «тормозит», сначала посмотри, где именно — на дороге, на железе или на холодном старте. Промпт-инжиниринг тут не поможет, а стриминг и кэш — очень даже.

А вы что делаете с латентностью LLM — стрим, кэш, свой прокси поближе к региону? 🤔
#ai #llm #webdev #backend #dev #performance
Post #1515 116
🤖 Как AI-агент тихо выбирает за тебя зависимости

Ты спрашиваешь агента: «какую библиотеку использовать под фича-флаги?».
Получаешь уверенный ответ с обоснованием и сразу идёшь пилить.
Кажется, что за тебя провели ресёрч и что именно этот вариант подобран под твой кейс.
Но на самом деле нейросеть выбрала архитектурное решение — а ты даже не заметил, что размышления не было.

📊 Что показывают цифры На llmrank.fyi каждый месяц гоняют одни и те же промпты через Claude, ChatGPT и Gemini и смотрят, что те советуют.
Картина такая: — Каждая модель называет один и тот же топ-вариант в 97–99% случаев.
Выглядит как устоявшийся консенсус — Но между собой три модели сходятся в топ-3 только в 58% случаев.

В 42% сценариев стоило спросить вторую модель — и она указала бы на другое решение. Консенсуса нет.
Есть три уверенных монолога, которые друг с другом не согласны.

🔍 Пример — Фича-флаги:
LaunchDarkly доминирует у всех, но Split.io всегда по-разному, а ChatGPT его вообще не упоминает.
Альтернативы AWS: ChatGPT в 100% случаев выдаёт Azure, а Gemini её не включает ни разу.

Одна и та же задача, разные модели — разный «очевидный» выбор.

🕵️ В чём настоящая ловушка
Опасность не в том, что совет плохой.
Опасность в том, что уверенность маскирует разногласие.
Детерминированный, гладкий ответ читается как результат исследования — а это всего лишь один предвзятый сэмпл с капелькой уверенности
И мы просматриваем план внедрения ровно с тем же ленивым доверием, с каким апрувим чужой PR по диагонали.

Раньше выбор зависимости — это был ресёрч, обсуждение в команде, проверка лицензии, иногда целый RFC.
Теперь дефолтный флоу: спросил агента → получил ответ → начал пилить.
Этап, где решение вообще осмыслялось, просто выпал.

🧭 Что с этим делать
Не «взять то, за что проголосовало большинство моделей».
Само разногласие — это сигнал.
Сходятся три модели — риск низкий.
Разошлись — это не шум, который надо усреднить, это флаг: вот здесь настоящая точка выбора, и тут нужен человек, который реально разберётся.

И скорее всего это не только про зависимости.
Так же уверенно агент подаёт и другие архитектурные решения — с доверием, которого он не заслужил.

А вы проверяете, что советует агент, второй моделью — или сразу пилите? 🤔

#ai #agents #architecture #programming #webdev #discussion
  • ❤ 1
Post #1514 142
Поздравления в студию🎉🎉🎉

Мы прошли ежегодную процедуру Минцифры России и в очередной раз подтвердили: EvApps — официально аккредитованная ИТ-компания.

И это вам не формальность "для галочки"!

💭 Ежегодная переаккредитация — это проверка того, что компания действительно ведёт профильную ИТ-деятельность, соответствует требованиям по структуре доходов и продолжает работать прозрачно.

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

Что вообще все это значит?

✅ Для наших сотрудников, попадающих под нужные категории, сохраняется отсрочка от призыва, остаётся доступной льготная ИТ-ипотека, а компания продолжает пользоваться налоговыми — а значит, может стабильно инвестировать в команду💚

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

Спасибо нашей команде, чья ежедневная работа делает такие результаты возможными 🙌
  • 🔥 2
Post #1513 120
🤖 Кто отвечает за код, который написал AI?

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

⚖️ Что говорят суды В США копирайт требует живого автора — то самое правило, по которому обезьяна не смогла зарегистрировать своё селфи. Copyright Office уточнил: AI-код охраняется, только если человек внёс «достаточно выразительного вклада». Одних промптов мало — в отчёте это сравнили с рулеткой: колесо крутишь, результат не контролируешь.

Инженерный перевод: «Tab to accept» — это рулетка. А «принял, переписал 4 строки, встроил в свой класс» — уже твой вклад. Код, вышедший из модели нетронутым, может юридически не принадлежать компании так, как код сеньора: его не защитить как актив и не засудить того, кто скопировал.

📜 Что говорит договор Строка из условий GitHub Copilot, которую не цитируют на встречах про внедрение AI: «Вы сохраняете всю ответственность за ваш код, включая Suggestions в нём».

Одно предложение отдаёт вам suggestions — и вешает все риски: лицензии из обучающих данных, чужой копирайт, баги. Это единственная рабочая форма: вендор не может гарантировать чистоту кода, обученного на всём GitHub, поэтому риск уходит туда, где кнопка merge.

🕳 Дыра с защитой от исков GitHub прикрывает от исков — но только на Business/Enterprise. На Individual те же suggestions, тот же риск и ноль защиты. А проекты сплошь едут на личных подписках: компания платит за Enterprise и думает, что закрыта, но код через личный аккаунт под защиту не попадает. CISO узнаёт об этом, когда прилетает иск.

🇪🇺 Регулятору всё равно, кто писал В ЕС смотрят не на владение, а на результат. AI Act, Cyber Resilience Act и Product Liability Directive складываются в одну логику: неважно, писал человек, AI или подрядчик — важен продукт на рынке. Уже 2 августа 2026 вступает часть AI Act. «Это модель предложила» — не аргумент, как и «это подрядчик написал».

⚠️ Тихая проблема ревью AI-код ревьюят менее внимательно: меньше времени, меньше замечаний, больше «компилируется, выглядит норм — го». Диф вдвое длиннее, ревью вдвое поверхностнее — там и проскакивают баг, косяк с лицензией и тихая дыра в авторизации.

🧭 Рабочая ответственность — четыре скучных шага — Все на индемнифицированном тарифе, никаких личных подписок для рабочего кода. — Duplicate-фильтр включён и проверяется в аудите, как SSO. — AI-код на ревью = сторонняя зависимость. Вопрос себе: «подписался бы я под этим, если бы модели рядом не было?» — У каждого PR есть человек с именем, который возьмёт трубку, когда всё сломается. Не модель, не вендор, не «команда».

Каждый suggestion — это вклад анонимного соавтора, который прочитал весь опенсорс планеты, которого не допросишь и которого договор вынес за пределы ответственности. Это не повод бросать AI и не повод тормозить. Вся защита сводится к самой скучной части процесса: человек смотрит на диф и решает, мержить или нет.

Ты написал merge commit — значит, он твой.

А у вас в команде кто отвечает за AI-код? 🤔

#ai #programming #career #law #webdev #discussion
  • 👍 1
Post #1512 94
Четвёртая часть про то, что браузер уже умеет сам — а мы по привычке тянем пакет.
Popover API 🎈 В прошлой части был <dialog> для модалок. Но половина случаев — это не модалка, а поповер: меню, тултип, дропдаун. И вот тут раньше начинался цирк с z-index, кликом «мимо» и Escape. Теперь два атрибута:
<button popovertarget="menu">Меню</button>
<div id="menu" popover>
<a href="#">Профиль</a>
<a href="#">Выйти</a>
</div>

Клик вне — закрывает. Escape — закрывает. Всё в top layer, никаких войн со z-index. Ноль JS.
:has() — родительский селектор 🎯 Двадцать лет фронтенд просил «а можно стилизовать родителя от ребёнка?». Наконец можно:
/* карточка, внутри которой есть картинка */
.card:has(img) { padding-top: 0; }

/* форма с невалидным полем */
form:has(:invalid) button { opacity: .5; }

Раньше это был querySelectorAll + навешивание классов вручную. Теперь — одна строка CSS.
crypto.randomUUID() 🆔 Классика, которая до сих пор живёт в проде:
const id = Date.now() + Math.random();

Нормальный UUID v4 встроен:
const id = crypto.randomUUID();
// "6f4d2c1e-8a3b-4f9c-b2e1-7d0a5c3e1f92"

Криптографически стойкий, коллизий можно не бояться. Работает во всех современных браузерах (нужен https или localhost).
Object.groupBy() 🗂 Сколько раз вы писали reduce, чтобы сгруппировать массив? Теперь это одна функция:
const users = [
{ name: "Аня", role: "dev" },
{ name: "Петя", role: "qa" },
{ name: "Ира", role: "dev" },
];

const byRole = Object.groupBy(users, u => u.role);
// { dev: [Аня, Ира], qa: [Петя] }

Есть и Map.groupBy(), если ключом должен быть объект. lodash можно не звать.
Intl.RelativeTimeFormat ⏳ «2 часа назад», «через 3 дня» — ради этого до сих пор ставят moment/dayjs. А это уже в браузере, да ещё и с локализацией:
const rtf = new Intl.RelativeTimeFormat("ru", { numeric: "auto" });

rtf.format(-2, "hour"); // "2 часа назад"
rtf.format(1, "day"); // "завтра"

Сам знает про склонения и «вчера/завтра». Бесплатно, без килобайтов зависимостей.
Иммутабельные методы массива 🔄 sort() и reverse() десятилетиями мутировали исходный массив — и ловили баги в React, где стейт менять нельзя. Теперь есть неразрушающие версии:
const sorted = list.toSorted((a, b) => a - b);
const reversed = list.toReversed();
const patched = list.with(2, "new"); // заменить по индексу

Оригинал цел, возвращается копия. Больше никаких [...arr].sort().
View Transitions API 🎬 Плавные переходы между состояниями UI раньше означали Framer Motion или ручные костыли. Теперь браузер сам анимирует разницу до/после:
document.startViewTransition(() => {
updateTheDOM();
});

Меняете DOM внутри колбэка — браузер делает кроссфейд между старым и новым кадром. Для SPA-переходов и списков выглядит дорого почти бесплатно. Пока стабильно в Chromium, для остальных — прогрессивное улучшение.
inert 🚫 Открыли модалку, а Tab всё равно уводит фокус на кнопки под ней. Классическая дырка в доступности. Один атрибут — и целое поддерево выключено: ни фокуса, ни кликов, ни скринридера.
<main inert>...весь фон...</main>
<dialog open>...</dialog>

Раньше это был ручной обход всех focusable-элементов и tabindex="-1". Теперь — inert.
text-wrap: balance ⚖️ Заголовок, у которого одно слово одиноко висит на второй строке — вечная боль. Раньше лечили   вручную. Теперь браузер сам балансирует строки:
h1 { text-wrap: balance; }
p { text-wrap: pretty; }

balance — ровные строки для заголовков, pretty — убирает висячие слова в абзацах. Красиво и без ручной вёрстки.
Перед тем как поставить очередную библиотеку, всё чаще стоит проверить — а вдруг платформа это уже умеет? 😄
Что из этого уже используете?
#web #frontend #javascript #css #webdev #dev
  • 🔥 1
Post #1511 163
⚡️Как обещали на вчерашнем вебинаре, делимся полезными материалами для тех, кто хочет знать, какие метрики помогут сэкономить бюджет, а какие дадут только красивые дашборды.

⬇️ Запись вебинара
📎 Презентация
📎 Шаблон дорожной карты

‼️ А подписаться на новый канал для IT руководителей можно здесь
VK Видео Когда метрики не работают: как резать затраты через воронки и узлы влияния Разложили по полочкам: - как найти ту самую "сломанную ступеньку" воронки, где теряется 30% пользователей — и не латать остальное; - как отличить метрики, которые нужны вам для управления, от тех, которые просто создают видимость работы; - как посчитать ROI…
Post #1510 159
А вот и прямая ссылка для подключения к вебинару⬇️
Ждем всех в 18:30 МСК👌

EvApps приглашает вас на запланированную конференцию: Zoom.
Подключиться к конференции Zoom
https://us06web.zoom.us/j/85033644309?pwd=fnxE9hKm233Fso1Y5hgo9s1GE2k14l.1

Ссылка на чат конференции
https://us06web.zoom.us/launch/jc/85033644309

Идентификатор конференции: 850 3364 4309
Код доступа: 068498
Zoom Join our Cloud HD Video Meeting Zoom is the leader in modern enterprise cloud communications.
Post #1509 329
А есть ли среди нас РП и прочие руководители разработки? Сейчас проверим👌

Если тебе знаком регулярный разбор полетов на тему "Почему в прошлом квартале все метрики были на высоте, а деньги все равно куда-то ушли?", и вечная головная боль с бюджетами, тебе точно на наш новый вебинар.

🤬 Команда часами ворошит дашборды, пытаясь понять, "что вообще не так с продуктом", а проблема в сломанной кнопке, миграции, которую никто не отследил, или воронке с дырой размером в 30% пользователей?

10 июня в 18:30 по Москве наш системный аналитик Мария Костыкова расскажет:

✅ Как найти в воронке "слабое звено", в котором исчезает почти треть лидов - и не сломать то, что работает;
✅ Как отличить "метрики для управления" от "метрик для видимости";
✅ Как посчитать ROI самого мониторинга, чтобы прийти к финансистам с цифрами, а не с фразой "нам бы еще отчетик настроить..."

У Маши 8 лет опыта в финтехе, промышленности и на ведомственных проектах - она разбирается💪

⭐️ Результат: ваша команда перестанет тратить часы на диагностику, а вы начнете видеть, где реально и безболезненно срезать затраты.

Участие бесплатно, но без регистрации - никак.
🔗 Ссылочка для регистрации
Post #1508 197
Третья часть про то, что уже встроено в браузер, но мы всё равно городим лишнее.

1) <dialog> 🪟
Признайтесь — вы ставили bootstrap или MUI только ради модалки.
А браузер давно закрыл этот вопрос — и сразу с нормальной доступностью:
<dialog id="alert">
<p>Вы уверены? Это нельзя отменить.</p>
<button onclick="alert.close()">Подумаю ещё</button>
<button onclick="doTheThing()">Да, я понимаю</button>
</dialog>
<button onclick="alert.showModal()">Удалить всё</button>

Никаких зависимостей. Работает. Доступно.

2) Container Queries 📦
Media queries всегда смотрели на окно целиком — и это было источником боли при переиспользовании компонентов. Теперь компонент живёт своей жизнью:
.widget { container-type: inline-size; }

@container (min-width: 500px) {
.widget__body {
display: flex;
gap: 1rem;
}
}

Положи его в сайдбар или в центр страницы — он сам разберётся.

3) @supports 🧪
Раньше — полифилы, гадание на кофейной гуще и молитвы перед деплоем.
Теперь просто пишете условие и браузер сам решает:
.hero {
background: #1a1a2e;
}

@supports (background: oklch(50% 0.2 270)) {
.hero {
background: oklch(20% 0.15 270);
}
}

Старые браузеры получают fallback, новые — красоту.

4) crypto.getRandomValues 🔐
Вот этот паттерн живёт в миллионе кодовых баз:
const id = Math.random().toString(36).slice(2);
// где-то в продакшене плачет дев

Замените на нормальное решение:
const arr = new Uint8Array(10);
crypto.getRandomValues(arr);
const id = [...arr].map(n => n.toString(16).padStart(2, "0")).join("");

Энтропия реальная, вероятность коллизий — смешная.

5) requestIdleCallback ⏸️
Отправка аналитики прямо в момент рендера — один из способов испортить пользователю жизнь незаметно. Браузер сам скажет, когда у него есть свободное время:
const sendStats = () => {
navigator.sendBeacon("/api/stats", JSON.stringify(stats));
};

"requestIdleCallback" in window
? requestIdleCallback(sendStats)
: setTimeout(sendStats, 300);

Safari долго саботировал этот API — fallback обязателен.

6) :focus-within 🎯
Классика жанра: инпут в фокусе, а стилизовать нужно обёртку вокруг него. Раньше это решалось через JS и eventListener. Сейчас:
.field {
border: 2px solid transparent;
transition: border-color 0.2s;
}

.field:focus-within {
border-color: #6c63ff;
}

Чисто, без единой строчки скрипта.

7) navigator.onLine 📶
Офлайн — это не баг, это сценарий. Телефон в кармане, лифт, метро — всё это реальность:
const queue = [];

window.addEventListener("offline", () => {
queue.push(getPendingData());
});

window.addEventListener("online", async () => {
while (queue.length) {
await sendToServer(queue.shift());
}
});

Главная ловушка — online говорит только о наличии сети, но не о том, что ваш сервер жив.

8) Speech Recognition API 🎙
Перед тем как добавить ещё один тяжёлый пакет — сначала это:
const Engine = window.SpeechRecognition || window.webkitSpeechRecognition;

if (Engine) {
const voice = new Engine();
voice.lang = "ru-RU";
voice.onresult = ({ results }) => {
handleInput(results[0][0].transcript);
};
voice.start();
}

Работает только в Chromium-браузерах. В продакшен — с осторожностью, для прототипа — идеально.

9) requestAnimationFrame 🎞
setInterval для анимаций — это как делать setTimeout(fn, 16) и надеяться на лучшее. Браузер знает точный момент перерисовки, воспользуйтесь этим:
let position = 0;

function loop(timestamp) {
position = Math.sin(timestamp / 800) * 120;
el.style.transform = `translateX(${position}px)`;
requestAnimationFrame(loop);
}

requestAnimationFrame(loop);

Плавно, без артефактов, CPU не страдает.

Библиотеки — хорошо. Но перед npm install стоит спросить себя: а не встроено ли это уже? 😄

Что из списка уже в вашем коде?

#web #frontend #javascript #css #webdev
Post #1506 207
Стартуем через полчаса🚀

EvApps приглашает вас на запланированную конференцию: Zoom.
Подключиться к конференции Zoom
https://us06web.zoom.us/j/86917612389?pwd=g9Lpv9yy4Jj4xNP9VqL2BNO5zjqm3u.1

Идентификатор конференции: 869 1761 2389
Код доступа: 874163
Zoom Join our Cloud HD Video Meeting Zoom is the leader in modern enterprise cloud communications.
  • ❤ 1
Post #1505 197
EvApps Готовь сани летом, а архитектуру - на старте проекта! 🚀 Если не хотите через пару лет плакать над контроллерами на 2000+ строк и молиться на God Class - приходите на наш вебинар. ⏰ 2 апреля в 18:30 МСК встречаемся с ведущим fullstack-разработчиком EvApps…
Наш новый вебинар уже сегодня в 18:30 по Москве, не забывайте подключаться😊
Post #1504 249
Врываемся в твою рабочую неделю с новым эпизодом подкаста IT ToLк🚀

В новом выпуске - Андрей Карпов, сооснователь проекта PVS-Studio: поиск ошибок в коде программ. Андрей 17 лет в IT, у него есть бэкграунд CTO, а сегодня он является амбассадором статического анализа кода, автором целого сборника "вредных советов" о C#, а еще строит DevRel в своей компании.

Что в подкасте?

⭐️ Скучают ли топ-разработчики по C++ после перехода в управление?

⭐️ Rust вытеснит C++ или это хайп?

⭐️ Почему AI-код - это новая головная боль для SAST-инструментов?

⭐️ И главное: что посоветовать себе в 2007 году, чтобы писать код без багов?

Гость честный, вопросы интересные. Поехали 🎧

Смотрим по ссылке: https://clck.ru/3Si2SR
  • ❤ 1
Post #1503 393
Готовь сани летом, а архитектуру - на старте проекта! 🚀

Если не хотите через пару лет плакать над контроллерами на 2000+ строк и молиться на God Class - приходите на наш вебинар.

⏰ 2 апреля в 18:30 МСК встречаемся с ведущим fullstack-разработчиком EvApps Михаилом Прохоровым, чтобы поговорить по делу:

✅ Почему «просто MVC» - это путь к архитектурному долгу?
✅ Где проходит грань, когда DDD реально окупается, а не просто «усложняет ради понтов»?
✅ Как тактические паттерны (Value Objects, Aggregates) вытаскивают проект из болота?

Участие бесплатное, но места надо занять😉
🔗 Регистрация по ссылке: clck.ru/3SaKJV
Older posts →
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →