4736. При интеграции с платежным сервисом система получила код 429 (Too Many Requests). Разработчик обработал его как фатальную ошибку, хотя бизнес ожидал автоматических повторов. Где возникла проблема?
Anonymous Quiz
1%
Разработчик неверно понял код ответа
9%
Тестировщик не проверил сценарий с 429
76%
Аналитик не описал поведение системы для разных HTTP-кодов
13%
Архитектор не заложил механизм retry
👍1 1
❌ Почему не другие варианты:
A (Разработчик) — он строго следовал ТЗ, где не было указаний по обработке 429. Если в требованиях написано «при ошибке прерывать операцию», разработчик поступил корректно.
B (Тестировщик) — тестировщик проверяет только задокументированные сценарии. Без требования сценарий с 429 не включается в тест-план.
D (Архитектор) — архитектор может предложить общий механизм повторных попыток (retry policy), но конкретные условия — какие коды должны вызывать повторы, сколько попыток делать, с какими интервалами — это зона ответственности аналитика, который описывает функциональное поведение системы.
Корень проблемы: Аналитик не заложил в требованиях обработку различных HTTP-кодов ответа от внешнего сервиса. Интеграционные сценарии часто содержат множество нюансов: временные ошибки (429, 503), требующие повтора; ошибки валидации (400), требующие корректировки данных; ошибки авторизации (401, 403), требующие обновления токена.
Что должен был сделать аналитик:
Изучить документацию внешнего сервиса и выписать все возможные коды ответа.
Для каждого кода согласовать с бизнесом ожидаемое поведение системы:
200 — успех, идём дальше.
400 — ошибка в данных, показываем пользователю, что нужно исправить.
429 — временная ошибка, автоматически повторяем до 3 раз с интервалом 5, 30, 60 секунд.
500 — ошибка сервера, повторяем 3 раза, если не успешно — уведомляем администратора.
Задокументировать это в виде понятной таблицы или матрицы решений.
Последствия отсутствия таких требований:
Разработчик принимает решение на основе своего опыта (часто ошибочного).
Тестировщик не проверяет непрописанные сценарии.
В прод уходит функционал, который не соответствует ожиданиям бизнеса.
Пользователи видят ошибки там, где могли бы быть автоматические повторы.
Вывод: Полнота требований к интеграциям — прямая ответственность аналитика. Чем детальнее прописано поведение системы во всех возможных ситуациях, тем меньше рисков на этапах разработки и тестирования. 🎯
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
4737. Банк обновил версию API для проверки кредитных историй. Разработчики протестировали новую версию, всё работало. Через неделю старая версия API отключилась, и система перестала выдавать кредиты. В чём первопричина?
Anonymous Quiz
13%
Разработчики не учли отключение старой версии
22%
Тестировщики не проверили сценарий без старого API
51%
Аналитик не описал требование к graceful degradation при недоступности внешнего сервиса
14%
Архитектор не заложил механизм автоматического переключения
❌ Почему не другие варианты:
A (Разработчики) — они реализовали поддержку новой версии, но не знали, что старая скоро отключится, и не закладывали логику переключения.
B (Тестировщики) — тестировали то, что написано в требованиях. Без требования о graceful degradation этот сценарий не включается в план тестирования.
D (Архитектор) — может предложить решение (балансировку, fallback), но только если в требованиях указана необходимость непрерывности работы.
Что должен был сделать аналитик:
При описании интеграции указать: «Система должна поддерживать обе версии API в течение переходного периода».
Прописать поведение при недоступности API (например, отложить обработку, уведомить администратора, использовать кэшированные данные).
Включить в приёмочные тесты сценарии отключения старой версии и потери связи с новой.
Вывод: Надёжность системы закладывается на уровне требований, а не обнаруживается после падения в проде. 📉
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Как находят работу за бугром в IT
Наити работу на немецком рынке. И не через релокацию! Сам нашел и сам переехал!
Это история Бизнес аналитика, который работает в Deutsche Bank и язвительно пишет из солнечного Франкфурта-на-Майне.
Из ХЗ в ТЗ — блог про работу в финтехе и как там у них. Антон также исследует рынок РФ и продолжает ходить на собеседования.
Истории, которые уже вышли:
🟢 собеседование в банк Азии на 380К. Кринж😬
🟢 красные флаги в тестовом задании. И референс ответа.
🟢 как мы делали mit den Jungs в банке
🟢 стал бы я в 2026-ом накручивать опыт в резюме?
Переходите знакомиться: @anton_alekseev
Наити работу на немецком рынке. И не через релокацию! Сам нашел и сам переехал!
Это история Бизнес аналитика, который работает в Deutsche Bank и язвительно пишет из солнечного Франкфурта-на-Майне.
Из ХЗ в ТЗ — блог про работу в финтехе и как там у них. Антон также исследует рынок РФ и продолжает ходить на собеседования.
Истории, которые уже вышли:
🟢 собеседование в банк Азии на 380К. Кринж😬
🟢 красные флаги в тестовом задании. И референс ответа.
🟢 как мы делали mit den Jungs в банке
🟢 стал бы я в 2026-ом накручивать опыт в резюме?
Переходите знакомиться: @anton_alekseev
4738. Вы пишете запрос для отчета по продажам. Нужно получить список товаров, у которых общая сумма продаж превышает 100 000 рублей. Как правильно написать условие?
Anonymous Quiz
32%
WHERE SUM(sales_amount) > 100000
53%
HAVING SUM(sales_amount) > 100000
4%
FILTER SUM(sales_amount) > 100000
11%
GROUP BY с условием в SELECT
WHERE применяется до группировки (GROUP BY) и фильтрует отдельные строки таблицы.
HAVING применяется после группировки и фильтрует уже сгруппированные результаты (агрегатные функции, такие как SUM(), COUNT(), AVG()).
В вашем случае SUM(sales_amount) вычисляется после группировки по товарам, поэтому условие должно быть в HAVING. Если написать WHERE SUM(sales_amount) > 100000, СУБД выдаст ошибку, потому что на момент выполнения WHERE агрегатные функции ещё не вычислены.
Пример правильного запроса:
```sql
SELECT product_id, SUM(sales_amount) as total_sales
FROM sales
GROUP BY product_id
HAVING SUM(sales_amount) > 100000;
```
❌ Почему не другие варианты:
A — WHERE не работает с агрегатными функциями.
C — FILTER существует в SQL, но это нестандартный синтаксис для агрегации (используется редко, например, в PostgreSQL для частичной агрегации), и здесь он не применим.
D — условие нельзя поместить в SELECT, потому что оно должно отсеивать целые группы, а не просто отображать значение.
Вывод для аналитика:
При работе с группированными данными всегда используйте HAVING для фильтрации по агрегатам, а WHERE оставьте для фильтрации отдельных записей до группировки. Это фундаментальное правило SQL, которое помогает избежать ошибок в отчётах и анализах. 📊
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2🤩1
4739. Аналитик ищет клиентов без заказов: «ID клиента отсутствует в списке ID заказов». В списке ID заказов есть NULL. Что вернет запрос?
Anonymous Quiz
16%
Только клиентов без заказов
39%
Всех клиентов, кроме тех, у кого есть заказы
28%
Пустой результат (ни одного клиента)
18%
Ошибку выполнения запроса
🤔7❤1
Пример для понимания:
Представьте, что у нас есть клиенты с ID 1, 2, 3. В таблице заказов есть ID клиентов: 1 и NULL. Запрос мысленно проверяет каждого клиента:
Клиент 2 отсутствует в списке {1, NULL}? Сравнение с 1 даёт TRUE (2 не равно 1), но сравнение с NULL даёт UNKNOWN (2 не равно неизвестности). Итог — UNKNOWN, условие не выполняется.
Аналогично для всех клиентов — ни один не проходит.
Как избежать ошибки:
Использовать проверку через NOT EXISTS (она корректно обрабатывает NULL).
Исключить NULL из подзапроса, добавив условие WHERE customer_id IS NOT NULL.
Вывод для аналитика:
Всегда учитывайте возможность NULL в данных при написании запросов с отрицанием. Это частая причина некорректных отчётов и потери данных. 🎯
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
4740. Вы проектируете таблицу для хранения действий пользователей. Прогноз: 10 млн записей в день. Самый частый запрос: «показать все действия конкретного пользователя за последние 30 дней». Как обеспечить максимальную производительность?
Anonymous Quiz
23%
Создать составной индекс (user_id, action_time) в одной таблице.
56%
Партиционировать таблицу по месяцам и создать локальный индекс по user_id.
13%
Хранить действия каждого пользователя в отдельном JSON-документе в MongoDB.
9%
Шардировать таблицу по user_id на несколько физических серверов.
10 млн записей в день → ≈ 300 млн записей в месяц, ≈ 3.6 млрд в год.
Типичная строка лога: user_id (8 байт) + action_time (8 байт) + action_type (2 байта) + details (переменная). Оценим средний размер строки в 100 байт.
Размер таблицы за месяц: 300 млн × 100 байт = 30 ГБ.
Размер за год: 360 ГБ (без учёта индексов и накладных расходов).
Индекс на (user_id, action_time) в такой таблице займёт не менее 20–30% от объёма данных (B-дерево, ссылки на строки). Это ещё + 100 ГБ через год.
Запрос «за последние 30 дней» будет читать 1/12 часть от годового объёма (если нет партиционирования). Но даже чтение 30 ГБ с диска — это секунды или минуты, даже при полном сканировании индекса. Однако проблема не только в объёме, но и в структуре доступа.
2. Почему вариант A (просто индекс) не спасёт
Индекс (user_id, action_time) ускоряет поиск по конкретному пользователю, но данные в таблице физически разбросаны по всей её территории (записи вставляются в хронологическом порядке, но для одного пользователя они распределены по всем блокам).
Чтобы получить все действия пользователя за 30 дней, СУБД должна:
Найти в индексе все записи для этого user_id за указанный период (это быстро).
Выполнить случайные чтения (random I/O) каждой строки из таблицы по указателям из индекса. При большом количестве строк (например, у активного пользователя 1000 действий за месяц) это приведёт к тысячам случайных обращений к диску.
Даже если используется covering index (включить все нужные поля в индекс), то индекс сам по себе станет огромным и его сканирование за 30 дней всё равно потребует чтения миллионов записей индекса.
Индекс не решает проблему устаревания данных: удаление старых записей (например, через год) потребует тяжёлого DELETE с блокировками и фрагментацией.
3. Почему партиционирование (вариант B) — оптимальное решение
Партиционирование по диапазону дат (например, по месяцам) — стандартный паттерн для временных рядов. Оно даёт:
✅ Partition Pruning (отсечение партиций)
Планировщик запроса понимает, что условие action_time BETWEEN ... AND ... затрагивает только определённые партиции (например, текущий и предыдущий месяц). Он не будет сканировать остальные партиции с данными за прошлые годы. Вместо 360 ГБ читается максимум 30–60 ГБ (1–2 партиции).
✅ Локальные индексы
Индекс по user_id внутри каждой партиции имеет гораздо меньшую высоту B-дерева (т.к. работает с 30 млн строк, а не с 3.6 млрд). Это ускоряет поиск и снижает потребление памяти.
✅ Управление жизненным циклом данных
Удаление старых данных — это DROP PARTITION, а не DELETE. Операция занимает миллисекунды и не вызывает фрагментации.
Архивация: партицию можно быстро отсоединить от таблицы (DETACH PARTITION) и сохранить как отдельную таблицу или файл.
Обслуживание (реиндексация, анализ) можно проводить на отдельных партициях, не блокируя всю таблицу.
✅ Производительность записи
Записи распределяются по партициям в соответствии с датой. Это не создаёт «горячих точек» (hotspots) — вставки идут в текущую партицию, но это нормально. Если нужно распределить нагрузку ещё сильнее, можно использовать субпартиционирование (например, по дням или по хешу user_id внутри месяца).
4. Почему MongoDB (вариант C) — не лучший выбор
Ограничение размера документа: BSON-документ не может превышать 16 МБ. У активного пользователя за несколько лет действий может накопиться гораздо больше (даже за месяц — тысячи событий, JSON с ними легко превысит 16 МБ).
Запросы внутри документа: Если хранить все действия в массиве, то выборка за последние 30 дней потребует разбора всего массива и фильтрации на стороне приложения — это неэффективно и нагружает сеть.
Индексирование: MongoDB поддерживает индексы на полях вложенных массивов, но при частых обновлениях документа (добавлении нового действия) происходит перезапись всего документа — это дорого и вызывает фрагментацию.
Транзакционность: Если нужны транзакции или сложные joins (например, с пользователями), MongoDB не подходит.
Когда MongoDB хороша: для редких изменений и чтения всего документа целиком.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
Продолжение к объяснению ❗️
5. Почему шардинг по user_id (вариант D) неэффективен для этого запроса
Шардинг (горизонтальное масштабирование) распределяет данные по физическим узлам на основе ключа (например, user_id).
Запрос «действия за последние 30 дней» не ограничен по ключу шардирования — он требует данные за определённый период, но у разных пользователей эти данные лежат на разных узлах.
Придётся выполнить запрос ко всем шардам (fan-out), собрать результаты и отсортировать. Это создаёт огромную сетевую нагрузку и задержки.
Шардинг полезен, когда все запросы содержат ключ шардирования (например, «показать действия пользователя X»). Но в нашем случае запрос включает ещё и период, поэтому шардинг по user_id не даст partition pruning по времени.
Шардинг требует дополнительной инфраструктуры (маршрутизаторы, балансировка) и усложняет операции JOIN и транзакции.
Когда шардинг оправдан: если данные не имеют естественного временного разреза и запросы всегда содержат ключ шардирования (например, социальная сеть: все посты пользователя).
6. Дополнительные рекомендации для аналитика
При проектировании таблиц с временными метками всегда задавайте вопрос: «Какой объём данных будет накоплен через год? Через три года?»
Партиционирование должно быть заложено в требования к физической модели данных с самого начала. Это не оптимизация постфактум — добавить партиционирование на таблицу с миллиардами строк очень трудоёмко.
Выбирайте ключ партиционирования так, чтобы он совпадал с условиями в самых частых запросах (здесь — action_time).
Рассмотрите интервал партиционирования (день, неделя, месяц) в зависимости от частоты запросов и скорости роста.
Не забывайте про локальные vs глобальные индексы: в большинстве СУБД (PostgreSQL, Oracle, MySQL) локальные индексы автоматически партицируются вместе с таблицей и обслуживаются независимо.
Учитывайте операции обслуживания: партиционирование позволяет делать VACUUM, ANALYZE, REINDEX на отдельных партициях без простоя всей системы.
Для ещё большей производительности можно комбинировать партиционирование с кластеризацией (физической сортировкой данных внутри партиции по user_id), но это требует дополнительного планирования.
7. Итог
Партиционирование по времени с локальными индексами — единственный вариант, который одновременно:
сокращает объём сканируемых данных (partition pruning),
обеспечивает быстрый доступ по пользователю (локальный индекс),
упрощает обслуживание и удаление старых данных (DROP PARTITION),
масштабируется горизонтально без усложнения инфраструктуры.
Это промышленный стандарт для систем с временными рядами (логи, события, метрики). Системный аналитик, закладывающий такое решение на этапе проектирования, предотвращает будущие проблемы с производительностью и стоимостью владения системой. 🎯
5. Почему шардинг по user_id (вариант D) неэффективен для этого запроса
Шардинг (горизонтальное масштабирование) распределяет данные по физическим узлам на основе ключа (например, user_id).
Запрос «действия за последние 30 дней» не ограничен по ключу шардирования — он требует данные за определённый период, но у разных пользователей эти данные лежат на разных узлах.
Придётся выполнить запрос ко всем шардам (fan-out), собрать результаты и отсортировать. Это создаёт огромную сетевую нагрузку и задержки.
Шардинг полезен, когда все запросы содержат ключ шардирования (например, «показать действия пользователя X»). Но в нашем случае запрос включает ещё и период, поэтому шардинг по user_id не даст partition pruning по времени.
Шардинг требует дополнительной инфраструктуры (маршрутизаторы, балансировка) и усложняет операции JOIN и транзакции.
Когда шардинг оправдан: если данные не имеют естественного временного разреза и запросы всегда содержат ключ шардирования (например, социальная сеть: все посты пользователя).
6. Дополнительные рекомендации для аналитика
При проектировании таблиц с временными метками всегда задавайте вопрос: «Какой объём данных будет накоплен через год? Через три года?»
Партиционирование должно быть заложено в требования к физической модели данных с самого начала. Это не оптимизация постфактум — добавить партиционирование на таблицу с миллиардами строк очень трудоёмко.
Выбирайте ключ партиционирования так, чтобы он совпадал с условиями в самых частых запросах (здесь — action_time).
Рассмотрите интервал партиционирования (день, неделя, месяц) в зависимости от частоты запросов и скорости роста.
Не забывайте про локальные vs глобальные индексы: в большинстве СУБД (PostgreSQL, Oracle, MySQL) локальные индексы автоматически партицируются вместе с таблицей и обслуживаются независимо.
Учитывайте операции обслуживания: партиционирование позволяет делать VACUUM, ANALYZE, REINDEX на отдельных партициях без простоя всей системы.
Для ещё большей производительности можно комбинировать партиционирование с кластеризацией (физической сортировкой данных внутри партиции по user_id), но это требует дополнительного планирования.
7. Итог
Партиционирование по времени с локальными индексами — единственный вариант, который одновременно:
сокращает объём сканируемых данных (partition pruning),
обеспечивает быстрый доступ по пользователю (локальный индекс),
упрощает обслуживание и удаление старых данных (DROP PARTITION),
масштабируется горизонтально без усложнения инфраструктуры.
Это промышленный стандарт для систем с временными рядами (логи, события, метрики). Системный аналитик, закладывающий такое решение на этапе проектирования, предотвращает будущие проблемы с производительностью и стоимостью владения системой. 🎯
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1👍1
4741. ва руководителя в банке спорят о дизайне главного экрана личного кабинета. Один требует «максимум информации», второй — «только самое нужное». Сроки горят, бюджет ограничен. Что делать аналитику?
Anonymous Quiz
8%
Найти средний вариант и утвердить у обоих
78%
Провести совместный воркшоп, чтобы согласовать приоритеты
12%
Сделать настраиваемый интерфейс под каждого пользователя
1%
Передать решение архитектору
Это классический конфликт интересов стейкхолдеров, возникающий из-за разных бизнес-целей и видения продукта. Ситуация осложняется жёсткими сроками и бюджетом, что делает любой неоптимальный выбор критичным.
1. Почему это реальный кейс?
В крупных проектах (банки, ритейл, телеком) разные отделы преследуют свои цели:
Кредитный отдел хочет увеличить выдачу кредитов → нужна информация о лимитах, предложениях, задолженностях на виду.
Отдел депозитов заботится о привлечении средств → нужны ставки, остатки, условия.
Маркетинг хочет промо-акции.
Безопасность требует ограничить видимость чувствительных данных.
Аналитик оказывается между молотом и наковальней. Просто «усреднить» требования (вариант A) — значит, скорее всего, не удовлетворить никого. Кастомизация (C) — дорого и долго, а бюджет фиксирован. Передать архитектору (D) — значит, снять с себя ответственность, но техническое решение не заменит бизнес-согласования.
2. Почему воркшоп с фасилитацией (B) — единственно верный путь?
Цель: перевести эмоциональные требования («хочу много / хочу мало») в измеримые и объективные критерии, а затем согласовать приоритеты.
Этапы воркшопа:
Подготовка:
Собрать все противоречивые требования заранее.
Определить критерии оценки (например: влияние на конверсию, затраты на разработку, риски, соответствие стратегии).
Пригласить обоих стейкхолдеров + фасилитатора (лучше нейтрального, можно внешнего).
Выявление истинных потребностей (техника «5 почему»):
«Почему вам нужна максимальная информация?» → «Чтобы клиент сразу видел, что может взять кредит» → истинная потребность: повышение конверсии в кредиты.
«Почему вы хотите минимум информации?» → «Клиенты жалуются на сложность интерфейса» → истинная потребность: удобство и снижение оттока.
Перевод в измеримые цели:
«Увеличить конверсию в кредиты на 15%».
«Снизить количество обращений в поддержку по навигации на 20%».
Поиск вариантов решения, удовлетворяющих обе цели:
Например, умный дашборд, который показывает разный набор виджетов в зависимости от сегмента клиента (активные кредиты — видит кредитную информацию, вкладчики — депозитную).
Или поэтапный подход: сначала MVP с ограниченным набором, но с возможностью быстрого A/B-тестирования гипотез.
Приоритизация (MoSCoW или Weighted Scoring):
Оцениваем каждую «фичу» по влиянию на цели и стоимости.
Стейкхолдеры видят объективную картину и договариваются, что важнее сделать сейчас, а что отложить.
Фиксация договорённостей:
Протокол с чёткими решениями, подписанный обоими руководителями.
3. Инструменты фасилитации для такого конфликта
Impact Mapping: связать требования с бизнес-целями.
User Story Mapping: увидеть, как фичи вписываются в пользовательский путь.
Kano Model: классифицировать требования на базовые, линейные и «восхитители».
Weighted Shortest Job First (WSJF): приоритизация по ценности и срочности.
4. Роль аналитика в этом процессе
Аналитик не должен становиться «третьей стороной», которая выбирает между стейкхолдерами. Его задача:
Фасилитировать диалог, а не принимать решения за бизнес.
Переводить эмоции в факты (цифры, исследования, метрики).
Предлагать варианты (не «или-или», а «и то, и другое, но с ограничениями»).
Документировать компромиссы и обоснования для будущих изменений.
5. Почему другие варианты не работают в долгосрочной перспективе
A (компромисс): Половинчатое решение часто не удовлетворяет ни одну из сторон. Через месяц начнутся новые споры, требования изменятся, разработка пойдёт по кругу.
C (кастомизация): Технически сложно и дорого. Кроме того, интерфейс, настраиваемый пользователем, требует собственного UX-исследования и поддержки. При фиксированном бюджете это убьёт проект.
D (передать архитектору): Архитектор спроектирует технически красивое решение, но оно не решит бизнес-конфликт. Техническая реализация не заменяет согласования целей.
6. Что делать, если воркшоп не помог?
Бывает, что стейкхолдеры не готовы договариваться. Тогда аналитик должен:
Поднять вопрос на уровень выше (спонсор проекта, продуктовый комитет).
Предложить A/B-тест (если позволяет время и бюджет)— пусть данные покажут
Please open Telegram to view this post
VIEW IN TELEGRAM
Продолжение к объяснению❗️
Что лучше для бизнеса.
Задокументировать риски (например, «если не договоримся, срыв сроков с вероятностью 80%»).
7. Профессиональный вывод
Умение управлять конфликтами стейкхолдеров — один из ключевых навыков системного аналитика высокого уровня. Техническая часть (написать ТЗ) — это только вершина айсберга. Основа — выявление истинных потребностей, фасилитация и принятие взвешенных решений в условиях неопределённости. Именно такие кейсы отделяют senior-аналитика от middle.
Итог: Воркшоп с фасилитацией — не просто «поговорить», а структурированный процесс, который превращает конфликт в конструктивное решение, экономя время и бюджет проекта. 🎯
Задокументировать риски (например, «если не договоримся, срыв сроков с вероятностью 80%»).
7. Профессиональный вывод
Умение управлять конфликтами стейкхолдеров — один из ключевых навыков системного аналитика высокого уровня. Техническая часть (написать ТЗ) — это только вершина айсберга. Основа — выявление истинных потребностей, фасилитация и принятие взвешенных решений в условиях неопределённости. Именно такие кейсы отделяют senior-аналитика от middle.
Итог: Воркшоп с фасилитацией — не просто «поговорить», а структурированный процесс, который превращает конфликт в конструктивное решение, экономя время и бюджет проекта. 🎯
Please open Telegram to view this post
VIEW IN TELEGRAM