Microservices Thoughts
7.76K subscribers
32 photos
55 links
Вопросы и авторские статьи по микросервисам, архитектуре, БД

Сотрудничество: t.me/qsqnk
Download Telegram
Наткнулся на статью https://blog.wilsonl.in/search-engine/, где чел весьма подробно рассказывает, как с нуля зафигачил семантический поиск (включая сбор данных) по 3млрд документов

Понравилось, что описывается не просто "я сделал так-то", а "попробовал так-то, не получилось, поэтому сделал вот так"

Рекомендую почитать + погулять по референсам — там много интересного как про ML, так и про инфру и бэкенд
4👍63🔥4
⚡️TTFB/FCP vs response time

TTFB (Time To First Byte) — это время от отправки запроса до получения первого байта данных от сервера

FCP (First Contentful Paint) — это время от открытия страницы до того как на экране появится первый контент

Почему я рассматриваю эти метрики одновременно? Потому что зачастую они оптимизируются с одной целью — как можно раньше что-то показать пользователю, т.е. сделать интерфейс отзывчивее

---

В зависимости от контекста response time может иметь два значения:

1. Конкретный запрос к бэкенду

Это время от начала запроса до получения последнего байта по конкретному запросу к апишке бэкенда

response time = TTFB + время загрузки всех данных по этому запросу

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

В основном, для конечного пользователя здесь будет разница, когда есть какой-то стриминг: видео/аудио/...

2. Загрузка всей страницы

Более неформальный вариант — это время от момента открытия страницы до загрузки всех необходимых данных

response time = FCP + время загрузки всех данных для этой страницы

Пример: при открытии ленты в соц сети сначала подгрузится разметка, затем текст постов, затем фото в постах

Конечный пользователь ощутит разницу, когда фронтенд не дожидается всех данных от сервера и потом их отрисовывает, а делает несколько запросов до бэкенда и рисует страницу по частям

---

Вообще, один из актуальных примеров это диалог с чатгпт

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

Overall, оптимизировать TTFB/FCP имеет смысл когда одновременно
1) полный response time долгий
2) неполный контент уже имеет смысл и будет полезен пользователю

p.s.: поправил несколько неточностей и решил перепостнуть
1👍31💅11
Забавно, что статья, как положить постгрес, воспринимается гораздо проще статей про оптимизации

Домашка от Тайлера — прочитать и внедрить в своем сервисе https://byteofdev.com/posts/making-postgres-slow/#the-parameters
8😁47👍9💅52
На мой взгляд, один из признаков мастерства в каком-то деле — это наличие структуры мышления, которая позволяет принимать решения осознанно, а не интуитивно

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

И тут есть два пути:
- Ходить по граблям и итеративно выстраивать свою структуру
- Поучиться на опыте других

Ходить по граблям я не очень люблю (хотя иногда полезно), поэтому чтобы ускорить этот процесс, я пошел на курс для M2+ руководителей к ребятам из Стратоплана

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

---

Пару слов про поступление

Оно проходит в два этапа
1. Эссе про опыт + решение менеджерского кейса
2. Собеседование, где обсуждаете опыт и кейс

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

Обучение стартует в конце сентября, примерно в это же время начнутся первые посты на эту тему

stay tuned!
1👍57🔥23🤔11😁7💅63
⚡️Что может пойти не так с очередью на postgres

Очереди задач на БД — вещь объективно удобная. Тут и транзакционность, и возможность сделать очередь с приоритетами, и возможность настроить кастомные политики ретраев. И что важно — нет необходимости тянуть еще одну зависимость в виде внешней очереди

---

С точки зрения БД это write-intensive таблица, в которую летят примерно такие запросы:

1. Поллинг запланированных задач

select * from tasks
where status = 'scheduled'
and scheduled_ts < now()
order by scheduled_ts
limit 100


2. Обновления задач по id (смена статусов, кол-ва ретраев и т.д.)

update tasks
set ...
where id = 123


Конкретные реализации могут использовать for update skip locked либо heartbeat pattern, но сейчас это не суть вопроса

Энивей, логично сделать как минимум два индекса
1. Для первого запроса — на (scheduled_ts) where status = 'scheduled'
2. Для второго запроса — pk на (id)

---

Далее представим, что в нашей базе начинается долгая транзакция

Из поста про bloat мы знаем, что долгие транзакции держат горизонт базы и не позволяют autovacuum-у чистить dead tuples, которые "старше" этого горизонта

Посмотрим, как это заафектит наш индекс на (scheduled_ts). На самом нижнем уровне индекса есть leaf pages, которые между собой образуют двусвязный список

[row1, row2, row3] <-> [row4, row5, row6] <-> [...] 


В ходе выполнения первого запроса для выборки задач у нас есть сортировка по scheduled_ts, поэтому мы
1. Спускаемся по btree в самую левую часть
2. Идем в отсортированном порядке по двусвязному списку

Затем мы проставляем этим задачам другой статус, например, 'running'. Эти апдейты создают новые версии строк, а старые помечают удаленными

Но если autovacuum не работает, то удаленные версии строк не будут чиститься, и в левой части индекса начнут копиться dead tuples

Изначальное состояние индекса по scheduled_ts:

[row1, row2, row3] <-> [row4, row5, row6] <-> [...] 


Сделали выборку, апдейтнули статусы

[dead, dead, dead] <-> [row4, row5, row6] <-> [...] 


Сделали выборку, апдейтнули статусы

[dead, dead, dead] <-> [dead, dead, dead] <-> [...] 


И так далее

Если подождать часок, то может образоваться "полоска" из нескольких сотен тысяч dead tuples. И чтобы сделать очередную выборку, нам сначала нужно будет вручную пробежаться по этой "полоске" из тысяч dead tuples и только после этого взять живые записи

Это может привести к тому, что скорость выборки деградирует на несколько порядков, например, с 1мс до 200мс

А если таких выборок в секунду происходит штук 50, то чисто на выборки нам придется тратить 50 * 0.2 = 10 cpu cores

Если подождать еще часок, то кластер развалится...

---

Мораль сей басни такова: сочетание долгих транзакций и queue-like ворклоада на постгресе — вещь крайне опасная

Но иногда долгих транзакций просто не избежать. Один из самых частых примеров: создание индекса на большой таблице. Да, даже create index concurrently под собой удерживает долгую транзакцию

Суммаризируя, рекомендации здесь весьма банальные:
- Стараться избегать долгих транзакций
- Если все-таки надо, то снижать нагрузку на время обслуживания
- Не допускать, чтобы таблицы сильно разрастались. Этого можно добиться с помощью партицирования, шардирования и введения retention-а (например, хранить данные только за последнюю неделю) — это позволит выполнять создание индекса на таблице сильно быстрее
3👍54🔥9💅411
Не всякая неидеальность — техдолг

"Некрасивая" архитектура — еще не техдолг
"Некрасивая" архитектура, из-за которой разработка простой фичи требует изменений в 20 сервисах — уже техдолг

Потому что техдолг — это набор технических решений, которые снижают скорость и/или снижают качество и/или повышают стоимость разработки, что приводит к негативным последствиям для целей бизнеса

Из этого определения следуют два простых и важных умозаключения:

1. Бизнес и разработка должны быть "на одной стороне"

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

- запутанная архитектура, в которую сложно добавлять новые фичи => высокий TTM

- много багов => расходы на поддержку

- сервис нестабилен и регулярно падает => пользователь может просто уйти к конкурентам

2. Продуктовые и технические задачи должны приоритезироваться вместе

Это следствие из первого пункта. Если продуктовые фичи скорее про "как заработать денег", то техдолг про "как не потерять деньги". И эти вещи должны скориться вместе друг с другом, чтобы можно было сопоставить, что важнее, и какие риски мы можем принять

---

Важный момент (про который часто забывают), чтобы такая схема работала — исправление критичного техдолга должно оцениваться по важности на уровне с продуктовыми фичами на perfomance review. Без этого логично, что у разработчиков не будет никакой мотивации заниматься техдолгом
5👍50🔥9
Раз в несколько месяцев я рекомендую другие каналы по бэкенду/управлению, которые сам читаю и что-то оттуда черпаю (из последнего раз и два)

Сегодня — про канал @thisnotes, автором которого является Ваня Ходор — руководитель одной из команд в Яндекс Лавке

Ваня пишет как на генерик бэкендовые темы, так и про всякие забористые штуки типа CRDT и полнотекстового поиска. Также иногда можно увидеть контент по C++

С чего можно начать:

- Про shared databases

- Что под капотом у поиска Яндекс Лавки

- Почему балансировка трафика это не всегда просто

- Пост, который хотел сам написать, но не дошли руки — про устройство codesearch

Если вам нравится контент в моем канале, но подобного контента хочется побольше, то канал выше — мастхев:)
2👍27🔥7
Про PACELС

"На практике" CAP говорит, что при разделении сети в системе:
- либо каждое чтение возвращает последнюю запись, но может быть недоступность (CP)
- либо доступность, но можем не видеть записанные данные (AP)

Если же сеть в порядке, то система может быть доступной и консистентной одновременно

Однако CAP не раскрывает еще один трейдофф: даже когда с сетью все в порядке, соблюдение Consistency может требовать дополнительных задержек (например, на синхронную репликацию)

То есть, если с сетью все в порядке, то:
- либо чтение возвращает последнюю запись, но может увеличиваться latency
- либо latency низкое, но можем не видеть записанные данные

Именно это описывается принципом PACELC, который расширяет CAP на случай, когда с сетью все в порядке:


if (Partition) {
Availability OR Consistency
} else {
Latency OR Consistency
}


Это позволяет классифицировать системы как PA/EL, PA/EC, PC/EL, PC/EC. Например, DynamoDB — PA/EL: выбирает доступность вместо согласованности при разделении сети, а когда с сетью все в порядке — минимальную задержку вместо согласованности

p.s.: такие классификации — это не формальные описания систем, а скорее ментальные модели, которыми можно удобно оперировать. На эту тему есть хорошая статья
1👍52🔥5
Немножко про LLM системы

Работа вынуждает иногда что-то читать на эту тему, поэтому ловите пару полезных статеек

Workflows and Agents

Статья описывает самые популярные типы систем на базе LLM
tl;dr (картинка оттуда):

1. Workflow

Все пути исполнения флоу заранее известны. Делится на два подвида:

1.1. Путь исполнения единственен. В некоторых местах флоу вызывается модель

1.2. Путь исполнения может меняться в зависимости от ответов модели. Например, отправили вопрос пользователя в классификатор, который определил тематику, и в зависимости от тематики исполнение идет в одну из веток

2. Agent

Путь исполнения заранее не известен. Модель сама рассуждает и решает, что ей делать, вызывает внешние тулы (например, с помощью MCP), обрабатывает результаты, думает что делать дальше и т.д.

12 правил разработки проектов с LLM

Статья от @vikulin_ai (подписывайтесь, канал классный, и это даже не реклама!), в которой как ни парадоксально описываются 12 правил разработки проектов с LLM. Читается легко, много полезных референсов

Building effective AI agents

Статья от Anthropic: описываются некоторые подробности про типы LLM систем, описанные выше
1👍26🔥7🤔1
Недавно был на мероприятии, где ребята из IT One и Сколково презентовали большое исследование по влиянию AI в SDLC

Пара интересных выводов:

- Большинство компаний, внедряющих ИИ в SDLC, не замеряют эффекты

А те, кто замеряют, в основном смотрят на
- TTM, Lead time/Cycle time (~90%)
- Количество ошибок в релизах (~35%)
- Лайки/дизлайки действиям ИИ-ассистентов (~25%)

Что довольно логично, основное влияние — на скорость разработки, при этом важно не просыпать метрики качества, что является основным риском использования ИИ. Другой риск — потеря компетенций людей, но пока не понятно, как это замерять в общем случае

- Прогноз изменения структуры команд

Ключевые изменения:

Разработчики 40-50% -> 65-75%
Архитекторы 0-5% -> 5%
Тестировщики 15-20% -> 5-7%
Системные-аналитики 10-20% -> 5-10%

Обоснование следующее: ассистенты частично дают разработчикам доступ к смежным компетенциям (проработка требований, тестирование), что может увеличить потребность в мультифункциональных разработчиках, которые сами умеют и требования проработать, и план тестирования написать. Это в свою очередь снижает потребность в бизнес-аналитиках и тестировщиках

*fun fact: в некоторых компаниях наоборот разработчики начали замещаться аналитиками, вероятно из-за того, что в таких компаниях основной частью SDLC была не разработка, а сбор, формализация и детализация требований

Также возрастает потребность в архитекторах, которые будут задавать вижн, как технически должна развиваться система. Это важный пункт, потому что без AI разработчик держит в голове довольно большое количество "негласных правил", как надо писать код в конкретном месте. При применении AI-агентов это может затираться (в промт же не перенесешь все мысли из головы) — поэтому возникает потребность в людях, которые будут это контролировать

- Перестройка идет довольно туго и хаотично

Основные причины две:
- Нет инициативы снизу — в среднем малая часть сотрудников готова активно тестировать и внедрять новые инструменты
- Нет контроля/направления сверху — даже если есть инициативные сотрудники, но нет правил и гайдлайнов, как использовать новые инструменты, то всё превращается в хаос

Другие причины:
- Риски ИБ
- Отсутствие ресурсов на доп инфру

---

Более полную версию с красивыми графичками можно посмотреть в презентации
👍24🔥3
Последние недели график стал что-то совсем плотным, поэтому на посты вдохновения не смог найти(

А еще я начал учиться в Стратоплане на https://stratoplan-school.com/head и могу сформировать первое мнение

Программа посвящена роли руководителя отдела — под этим подразумевается C-1 менеджер, который руководит другими руководителями

И уже прошло 4 занятия:
- Вводное про роль руководителя отдела
- Аудит компании и отдела
- Стейкхолдер-менеджмент
- Формирование оргструктур

Первые два занятия для меня скорее были систематизацией практических знаний и наработок. Из части про аудит сотрудников почему-то запомнилась Двухфакторная теория Герцберга, которая делит "мотивационные" факторы на два вида: гигиена (зарплата, политика управления и т.п.) и мотиваторы (достижения, признание, развитие). Гигиенические факторы не мотивируют людей, а предотвращают недовольство. Например, если у сотрудника зп ниже его субъективной оценки, то это будет сильно его демотивировать, а если выше — то просто восприниматься нормальным и само по себе не будет драйвить больше/эффективнее работать. При этом мотивационные факторы, как например достижения, признания уже могут именно драйвить и заряжать. В вузе на курсе менеджмента мы это еще называли стимуляторы (внешние факторы) и мотиваторы (внутренние факторы). Наверное, это одна из немногих моделей с которыми полностью согласен

Занятие про стейкхолдер менеджмент посвящено двум вещам: управлению ожиданиями и обеспечению прозрачности (вообще ребята очень верят в прозрачность, и на курсе много крутится вокруг этого. В этой статье можно понять основную концепцию). Глобально здесь мне очень резонирует мысль про индивидуальный подход в работе с ожиданиями и репортингом — твоим разным стейкхолдерам зачастую важны совершенно разные вещи в твоей работе, и если ты сам проактивно показываешь, что делаешь важные для них вещи, это сильно повышает прозрачность и как следствие доверие, что существенно помогает в дальнейшей работе

Четвертое занятие про формирование оргструктур было скорее тоже систематизацией знаний. Но мне энивей было интересно, учитывая мою склонность классифицировать сложные штуки и смотреть на свойства этих классов

Формат обучения — теория + практика. Занятие делится на две "итерации": сначала небольшой теоретический кусок => самостоятельная работа над кейсом (какой-то менеджерской ситуацией, приближенной к работе) => обсуждение в группах с другими участниками => разбор с лектором + Q/A. И потом вторая итерация по близкой теме

Оверолл, пока нравится. Особенно радует, когда что-то получается успешно применить в работе — это таки добавляет ощущения "не зря время потратил"
👍35🔥12💅121
И немного детокса от менеджмента

На работе мне давеча пришлось разбираться с function calling в LLM

Что это такое? Наверняка уже все видели агентов, которым даешь какое-то задание, они ходят по каким-то сайтам/апишкам, что-то собирают, что-то делают, и отдают результат. Вот это "походить по внешним апишкам" как раз делается благодаря function calling

Как оно верхнеуровнево работает: клиент говорит ллмке, какими она инструментами может пользоваться => ллмка отвечает клиенту, что ему нужно вызвать => клиент делает вызов и отдает ллмке результат => ллмка генерит итоговый ответ


client -> LLM:
Какая сейчас погода в москве?

доступные инструменты:
- get_weather
-- Описание: используй, когда хочешь получить погоду в конкретном городе
-- Аргументы: city (string)

------------

client <- LLM:
Вызови get_weather("moscow")

------------

client:
api call get_weather("moscow") = 13 градусов

client -> LLM:
get_weather("moscow") = 13 градусов

------------

client <- LLM:
В москве сейчас 13 градусов


Самый простой способ это реализовать — это действительно вставлять в промт спецификацию инструментов и парсить "необходимости что-то вызвать" из ответа модельки. Но такой способ подвержен галлюцинациям и проблемам со структурой

Следующий шаг — это сделать fine-tune модели, чтобы она лучше научилась справляться с подобными задачами, когда есть спека тулов, а ей нужно ответить, какие из них использовать

Следующий шаг — ввести структурированный формат, который избавляет от проблем с неспецифицированной структурой. Это и есть формат function calling от openai, про который можно почитать тут
👍31
Зачем нужны многоколоночные foreign key

Представь, тебе нужно описать граф в реляционной модели

Скорее всего получится приблизительно такое:

create table graph (
id bigserial primary key
);

create table node (
id bigserial primary key,
graph_id bigint
);

create table edge (
id bigserial primary key,
from_id bigint,
to_id bigint
);


Далее добавим внешние ключи, чтобы гарантировать целостность

node (graph_id) -> graph (id)
edge (from_id) -> node (id)
edge (to_id) -> node (id)


И на этом в целом можно закончить

Однако схема позволяет создать ребро, которое будет связывать две вершины из разных графов:

graph(id = 1)
graph(id = 2)

node(id = 1, graph_id = 1)
node(id = 2, graph_id = 2)

edge(id = 1, from_id = 1, to_id = 2) // no error


Валидировать такое можно с помощью
- триггеров
- проверок на уровне приложения
- многоколоночных внешних ключей

С первыми двумя вариантами в целом все ясно, а третий рассмотрим подробнее. Реализовать его можно так:

1. Добавляем в таблицу edge колонку graph_id
2. Для ребер многоколоночные внешние ключи:

node (graph_id) -> graph (id)
edge (graph_id, from_id) -> node (graph_id, id)
edge (graph_id, to_id) -> node (graph_id, id)


Такие ключи уже на уровне схемы данных не позволят привязать ребро к вершинам из разных графов

p.s.: для того чтобы сделать многоколоночный внешний ключ, на целевой таблице этот набор колонок должен быть уникальным — в нашем случае должен быть primary key / unique index на node (graph_id, id)
2👍32🤔11🔥2💅11
Microservices Thoughts
И немного детокса от менеджмента На работе мне давеча пришлось разбираться с function calling в LLM Что это такое? Наверняка уже все видели агентов, которым даешь какое-то задание, они ходят по каким-то сайтам/апишкам, что-то собирают, что-то делают, и отдают…
В продолжение: как связаны function calling и MCP

function calling — функциональность модели, которая:
- позволяет клиенту вместе с промтом передать список тулзов, которые модель может "использовать"
- модель отвечает, какие тулзы и с какими параметрами нужно вызвать
- и далее клиент совершает вызовы и передает результаты обратно в модель

MCP server же в свою очередь выполняет две фукнции:
- говорит, какие тулзы вообще есть (и не только тулзы)
- позволяет через себя вызывать эти тулзы

Чуть строже: mcp — это протокол, который описывает, как должен выглядеть mcp client и mcp server: то есть буквально, какое API у mcp сервера, и как mcp клиент в это API должен долбиться

Такая стандартизация довольно удобна, тк позволяет наклепать mcp клиента на одном языке, mcp сервер на другом, и все будет работать, тк и тот и тот соблюдают протокол

Также это позволяет каждый раз не задавать вручную, какие тулзы есть у очередного ai-приложения, а просто один раз их описать в mcp сервере. Например, есть mcp сервер гитхаба, и к нему можно подключиться хоть из курсора, хоть из claude code, хоть из какого-то своего приложения

---

На практике — чтобы создать свой mcp сервер, берешь какую-то готовую либу, описываешь какие у тебя есть тулзы (человекочитаемое описание + спека параметров), описываешь как тул должен исполняться. Готово

Для клиента аналогично — берешь готовую либу, вставляешь урл mcp сервера, токен авторизации. Готово

---

И если суммаризировать — как связаны function calling и MCP:


app -> mcp: запрос на получение списка тулзов
app <- mcp: список тулзов

app -> llm: запрос со списком тулзов (function calling)
app <- llm: какие тулзы надо вызвать

app -> mcp: вызов тулзов
app <- mcp: результаты вызова

app -> llm: финальный запрос с результатами вызова
app <- llm: финальный ответ


👍 — если осознали
👍44🔥4😁22💅1
Немного чувствую вину, что в исконно бэкендовый канал практически ничего не пишу про бэкенд))

Поэтому буду признателен, если в комментах поделитесь темами/практическими задачами/etc, про которые было бы интересно почитать

p.s.: если чето интересно про мльку или управление командой, тоже пишите
😁10👍2
и небольшой опросник для понимания общей картины

мне интересны посты про:
Anonymous Poll
95%
бэкенд
23%
мл
24%
менеджмент
Как разобраться в новой кодовой базе

По просьбе https://xn--r1a.website/MicroservicesThoughts/213?comment=2217

Представим, что нам нужно сделать фичу в сервисе, про который мы примерно знаем, чем он занимается, но код в нем отродясь не писали

Думаю с опытом у каждого вырабатывается свой подход, поэтому ниже субъективщина, как разбираюсь в новом сервисе я (зачастую неосознанно)

1. Посмотреть что снаружи

Обычно сервис живет в рамках какого-то bounded context-а, где он закрывает определенную задачу

Например:
- есть pricing-service
- выясняем, что он живет в рамках контекста checkout (оформление заказа). Там есть checkout-service, cart-service, pricing-service
- pricing-service закрывает собой задачу ценообразования: хранит в себе правила расчета итоговой цены с учетом скидок, промокодов и доставки

Артефакты:
- контекст
- задача, которую закрывает сервис

2. Модель данных

На этом этапе хочется понять, какие основные сущности есть внутри сервиса, и какие между ними связи

Здесь удобно сгенерить ER-диаграмму по сущностям БД (делается за пару кликов в ide/dbeaver/...), и на основе нее подопрашивать коллег

Артефакты:
- ER диаграмма

3. Инварианты

Самые основные бизнес правила, которые в себе инкапсулирует сервис. Например, в pricing-service могло бы быть что-то такое:
- Итоговая цена = базовая цена + надбавки – скидки
- Итоговая цена всегда неотрицательна

Артефакты:
- Список инвариантов

4. Посмотреть на основные сценарии

И только тут начинаем детально копаться в коде

Зачастую есть 4-5 основных апишек, благодаря которым происходит вся основная работа

Нужно их взять и детально раскрутить, как по коду происходят вызовы: что вызывается и в какой последовательности

Только ради бога в 2025 не занимайтесь этим полностью вручную, когда есть Cursor и ask mode))

Артефакты:
- Основные сценарии и их цепочки вызовов

5. Разобрать пару баг репортов

Неочевидный совет, но лично мне это давало наибольший профит в понимании, как работает сервис. Мейби потому что у тебя есть конкретная цель, и чтобы ее достичь надо понять "а как должно работать", "а как сейчас работает", "а почему так работает" и т.д

Также багрепорты зачастую подсвечивают херовые места в коде (без них скорее всего баг бы не произошел)

---

Overall после такого появляется понимание
- общего контекста и задачи сервиса
- как устроены данные
- как в коде реализованы основные сценарии, по каким компонентам они проходят
- в каких местах находили баги

И делитесь в комментах, если у вас другой подход:)
👍37🔥20
Внезапный пост — ищу разработчика

https://yandex.ru/jobs/vacancies/razrabotchik-bekenda-v-infrastrukturu-klientskogo-servisa-yandex-crowd-40028

Без автоматизации не получить эффективный клиентский сервис. Мы как раз занимаемся разработкой low-code платформы для автоматизации поддержки: тысячи workflow, миллионы запусков в день, ML-классификаторы, RAG и вот это всё

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

Стек: kotlin, spring, postgresql + экспериментируем с langchain4j

По всем вопросам можно писать мне напрямую: @qsqnk

p.s.: если у вас есть знакомые, кому было бы интересно, смело рекомендуйте
🔥15😁12👍33
Делать ли систему масштабируемой заранее

В ответ на https://xn--r1a.website/MicroservicesThoughts/213?comment=2233

Это вопрос управления рисками. Можно пользоваться простой ментальной моделью:

- cost_prepare — стоимость подготовки
Сколько нам будет стоить сделать систему масштабируемой заранее

- growth_probability — вероятность роста нагрузки
Каков шанс, что нагрузка повысится до такого уровня, что текущая архитектура не вывезет

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

Если мы заранее подготовим систему, то стоимость риска = cost_prepare

Если мы заранее не подготовим систему, то стоимость риска = growth_probability * cost_failure

cost_prepare > growth_probability * cost_failure => забиваем на преждевременное масштабирование

cost_prepare < growth_probability * cost_failure => заранее масштабируемся

---

Пример:
Текущая нагрузка 100rps, 1000 клиентов, сервис нормально работает

Допустим:
Стоимость доработать, чтобы сервис выдерживал 1000 rps = 100 человекочасов
Вероятность, что кол-во пользователей увеличится в 10 раз = 0.1
Стоимость экстренных доработок = 200 человекочасов

Если мы заранее подготовим систему, то стоимость риска = 100 человекочасов

Если мы заранее не подготовим систему, то стоимость риска = 0.1 * 200 человекочасов = 20 человекочасов

Значит заранее оптимизировать не очень осмысленно

---

На практике же нагрузка обычно редко вырастает одномоментно и зачастую есть пространство для маневра, чтобы адаптироваться

Поэтому я бы посоветовал:
- Всегда держать в голове модель выше и прикидывать примерные цифры
- Архитектуру не обязательно проектировать сразу масштабируемой. НО: у тебя всегда должен быть план, как ты будешь ее масштабировать, если припрет

Как завещал Клеппман
An architecture that is appropriate for one level of load is unlikely to cope with 10 times that load. If you are working on a fast-growing service, it is therefore likely that you will need to rethink your architecture on every order of magnitude load increase or perhaps even more often than that.
1👍36🔥112🤯2💅1