Теория ограничений (ТОС) - Элияху Голдратт
🔸 Что это такое?
Теория, которая фокусируется на выявлении и устранений узких мест (ботл неков) в системе для повышения ее эффективности.
Если мы говорим про разработку: ТОС должна помогать оптимизировать процесс доставки продукта, сократить время разработки и улучшить прогнозируемость.
🔸 Борьба с горлышками
Пять шагов фокуса:
1. Найти ограничение - определить что тормозит процесс.
2. Эксплуатировать ограничение - работать в сторону эффективного использования ресурсов (например, освободить SRE от рутины).
3. Подчинить всё остальное ограничению - выстроить процессы так, чтобы ограничение не простаивало (например, отдать в разработку автоматизацию рутинных задач для SRE).
4. Расширить ограничение - увеличить мощность узкого места (нанять больше SRE)
5. Вернуться к шагу 1
Показатели эффективности:
- Пропускная способность — скорость доставки ценности клиенту
- Запасы — незавершённые задачи (например, накопление неоттестированного кода)
- Операционные расходы — затраты на поддержание процессов
- Время цикла (Cycle Time) — как быстро задачи проходят весь процесс
- Простой ресурсов — например, разработчики ждут тестирования
Далее приведу пару проблем и их решение.
🔸 Впихнуть невпихуемое
Ситуация: в спринт включают 20 задач, хотя команда стабильно делает 10. Тогда:
Последствия:
- Накопление незавершённой работы
- Падение качества:
- Растёт количество багов
- Упрощаются код-ревью (или они вообще пропускаются)
- Команда перестаёт рефакторить технический долг.
- Снижение пропускной способности (парадокс):
- Контекстное переключение между задачами «съедает» время
- Ошибки из-за спешки требуют переделок
- Выгорание команды
- Потеря прогнозируемости
Что же делать?
- Ограничение — пропускная способность команды (10 задач/спринт). Брать строго 10 задач всегда!
- Буфер: оставить 20% времени на непредвиденные работы
- Убрать многозадачность, фокус на завершении задач.
🔸 Оценка +15%
Ситуация: на каждую задачу всегда добавляется +15% времени в разработке.
Последствия:
- Увеличение времени цикла (Cycle Time) (заполнения выделенного времени)
- Рост незавершённой работы: накопление задач с завышенной оценкой создаёт иллюзию «загруженности», но реальная пропускная способность (throughput) не увеличивается.
- Маскировка ограничений: локальные запасы мешают выявить реальные узкие места
Когда запас допустим?
- Глобальные буферы: выделить 20% времени спринта на непредвиденные работы
- Учёт вариативности через статистику: Использовать исторические данные, чтобы планировать сроки, а не «накидывать» проценты на каждую задачу.
🔸Метод DBR (Drum-Buffer-Rope)
это подход из Теории ограничений, который синхронизирует работу системы с её «узким местом»:
- Drum (Барабан) — ритм работы ограничения (самого медленного этапа в системе).
- Buffer (Буфер) — защитный запас задач перед ограничением, чтобы оно не простаивало.
- Rope (Верёвка) — механизм, который «привязывает» запуск новых задач к скорости ограничения, предотвращая перегрузку
🔸 Что почитать?
- Цель
- Проект феникс
- Джедайские техники
#management #teamlead
🔸 Что это такое?
Теория, которая фокусируется на выявлении и устранений узких мест (ботл неков) в системе для повышения ее эффективности.
Если мы говорим про разработку: ТОС должна помогать оптимизировать процесс доставки продукта, сократить время разработки и улучшить прогнозируемость.
🔸 Борьба с горлышками
Любые изменения, которые не исправляют горлышко - это иллюзия.
Пять шагов фокуса:
1. Найти ограничение - определить что тормозит процесс.
2. Эксплуатировать ограничение - работать в сторону эффективного использования ресурсов (например, освободить SRE от рутины).
3. Подчинить всё остальное ограничению - выстроить процессы так, чтобы ограничение не простаивало (например, отдать в разработку автоматизацию рутинных задач для SRE).
4. Расширить ограничение - увеличить мощность узкого места (нанять больше SRE)
5. Вернуться к шагу 1
Показатели эффективности:
- Пропускная способность — скорость доставки ценности клиенту
- Запасы — незавершённые задачи (например, накопление неоттестированного кода)
- Операционные расходы — затраты на поддержание процессов
- Время цикла (Cycle Time) — как быстро задачи проходят весь процесс
- Простой ресурсов — например, разработчики ждут тестирования
Далее приведу пару проблем и их решение.
🔸 Впихнуть невпихуемое
Ситуация: в спринт включают 20 задач, хотя команда стабильно делает 10. Тогда:
Последствия:
- Накопление незавершённой работы
- Падение качества:
- Растёт количество багов
- Упрощаются код-ревью (или они вообще пропускаются)
- Команда перестаёт рефакторить технический долг.
- Снижение пропускной способности (парадокс):
- Контекстное переключение между задачами «съедает» время
- Ошибки из-за спешки требуют переделок
- Выгорание команды
- Потеря прогнозируемости
Что же делать?
- Ограничение — пропускная способность команды (10 задач/спринт). Брать строго 10 задач всегда!
- Буфер: оставить 20% времени на непредвиденные работы
- Убрать многозадачность, фокус на завершении задач.
🔸 Оценка +15%
Ситуация: на каждую задачу всегда добавляется +15% времени в разработке.
Последствия:
- Увеличение времени цикла (Cycle Time) (заполнения выделенного времени)
- Рост незавершённой работы: накопление задач с завышенной оценкой создаёт иллюзию «загруженности», но реальная пропускная способность (throughput) не увеличивается.
- Маскировка ограничений: локальные запасы мешают выявить реальные узкие места
Когда запас допустим?
- Глобальные буферы: выделить 20% времени спринта на непредвиденные работы
- Учёт вариативности через статистику: Использовать исторические данные, чтобы планировать сроки, а не «накидывать» проценты на каждую задачу.
🔸Метод DBR (Drum-Buffer-Rope)
это подход из Теории ограничений, который синхронизирует работу системы с её «узким местом»:
- Drum (Барабан) — ритм работы ограничения (самого медленного этапа в системе).
- Buffer (Буфер) — защитный запас задач перед ограничением, чтобы оно не простаивало.
- Rope (Верёвка) — механизм, который «привязывает» запуск новых задач к скорости ограничения, предотвращая перегрузку
🔸 Что почитать?
- Цель
- Проект феникс
- Джедайские техники
#management #teamlead
👍2🫡2👌1
Паттерн API Gateway
Это обратный прокси-сервер, который служит единой точкой входа в микросервисной архитектуре.
Проблема: у нас есть несколько микросервисов со своим API (возможно на разных протоколах), чтобы начать их использовать мы должны знать все их адреса, в каждом иметь авторизацию и т.п.
Решение: разрабатываем отдельный сервис, который принимает все входящие API запросы и маршрутизирует их соответствующим микросервисам.
Основные функции, которые может выполнять API gateway:
- Маршрутизация (уже проговорили)
- Агрегация данных: объединения запросов и ответов к и от нескольких микросервисов
- Аутентификация: как пример, проверка jwt-токена
- Кеширование
- Логирование и метрики
- Ограничение RPS
- Трансформация протоколов: REST -> gRPC
Также gateway хорошо подходит для миграции с legacy кода, замена монолита микросервисами с сохранением старого API.
Недостатки этого решения:
- Единая точка отказа: gateway упал, вся система стала недоступной
- Бутылочное горлышка: весь трафик идет через один узел.
Последнее о чем хотелось бы поговорить, это сравнение API Gateway, Open Host Service(OHS) и Anti-Corruption Layer (ACL):
- API Gateway: когда нужно упростить доступ к нескольким микросервисам. Например, мобильное приложение.
- OHS: когда ваш контекст должны использовать соседние команды/сервисы. Например, сервис платежей предоставляет API для заказов.
- ACL: при интеграции с legacy-системой или внешним API, для защиты доменной модели.
Пример взаимодействия:
#patterns #DDD
Это обратный прокси-сервер, который служит единой точкой входа в микросервисной архитектуре.
Проблема: у нас есть несколько микросервисов со своим API (возможно на разных протоколах), чтобы начать их использовать мы должны знать все их адреса, в каждом иметь авторизацию и т.п.
Решение: разрабатываем отдельный сервис, который принимает все входящие API запросы и маршрутизирует их соответствующим микросервисам.
Основные функции, которые может выполнять API gateway:
- Маршрутизация (уже проговорили)
- Агрегация данных: объединения запросов и ответов к и от нескольких микросервисов
- Аутентификация: как пример, проверка jwt-токена
- Кеширование
- Логирование и метрики
- Ограничение RPS
- Трансформация протоколов: REST -> gRPC
Также gateway хорошо подходит для миграции с legacy кода, замена монолита микросервисами с сохранением старого API.
Недостатки этого решения:
- Единая точка отказа: gateway упал, вся система стала недоступной
- Бутылочное горлышка: весь трафик идет через один узел.
Последнее о чем хотелось бы поговорить, это сравнение API Gateway, Open Host Service(OHS) и Anti-Corruption Layer (ACL):
- API Gateway: когда нужно упростить доступ к нескольким микросервисам. Например, мобильное приложение.
- OHS: когда ваш контекст должны использовать соседние команды/сервисы. Например, сервис платежей предоставляет API для заказов.
- ACL: при интеграции с legacy-системой или внешним API, для защиты доменной модели.
Пример взаимодействия:
[ Клиент ]
↓ HTTP-запрос
[ API Gateway ] ← Аутентификация, кеширование
↓
[ Сервис A: OHS ] ← Стандартизированный API
↓
[ Сервис B: ACL ] ← Трансформация данных из старой системы
↓
[ Legacy System ]
#patterns #DDD
Telegram
От сердца к коду
Интеграция ограниченных контекстов (часть 2)
В прошлом посте мы разобрали, как быть с общими доменными моделями. В этом же рассмотрим несколько паттернов для ситуаций, когда один ограниченный контекст предоставляет сервис другому.
1️⃣ Anti-Corruption Layer…
В прошлом посте мы разобрали, как быть с общими доменными моделями. В этом же рассмотрим несколько паттернов для ситуаций, когда один ограниченный контекст предоставляет сервис другому.
1️⃣ Anti-Corruption Layer…
🔥3🫡3👍2
Когда мы растем
Здесь я имею ввиду - растем профессионально, как разработчик или менеджер.
Этот пост больше философский, моё имхо на тему.
Ниже я приведу некоторые характеристики правильного вектора своего развития.
1️⃣ Рост там, где тяжело
Очень сложно вырасти в комфорте. Лично для меня комфорт наступает примерно через 3-4 года работы в одном месте. Работа должна быть сложной, непонятной.
Приведу пример самого максимального роста из своего опыта: позвали работать лидом в команду, на новый язык программирования и абсолютно незнакомый мне домен. И через пару месяцев выставили KPI и решили меня уволить :) От KPI я сразу отказался и пошел искать работу, но в итоге, чудесным образом, я остался в компании, стал полноценным лидом, разобрался в домене и ЯП.
2️⃣ Команда
Сильная команда выращивает хороших разработчиков. Сошлюсь на книгу Человеческий фактор, там было исследование что 1 сеньор работает в 4 раза лучше мидла и в 10 раз лучше джуна. Из собственного опыта - примерно согласен.
Ищите команду с сеньорами, но это очень сложно сделать! Ибо, сеньорство в разных компаниях разное, и по своему опыту: чем сильнее руководство - тем сильнее разработка. И наоборот.
Относительно примера выше: я бы не затащил лидство+домен+яп, если бы меня не окружали крутые специалисты. Включая разработку, qa, а особенно ПО, который у меня там был (Кирилл, привет :).
3️⃣ Жопочасы
Как ни крути количество потраченного времени решает. Команда, как собственный тренер, подсказывает направление знаний. Если заниматься одному - придется потратить больше времени на блуждания.
Пример: я сейчас учусь играть на электрогитаре. Прямая зависимость на потраченное время. Если на упражнения на ПШ я трачу 10 минут в день, то и выхлопа будет на 10 минут в день (те кто понял, тот понял). И да, у меня конечно есть преподаватель.
4️⃣ Это невозможно
Такая, мысленная ловушка, когда тебе выпадает какая-то задача и ты сначала думаешь что ее невозможно выполнить. Сейчас, с опытом, вообще нет таких мыслей, есть понимание что сложные задачи требуют больше времени. И всё.
Пример: да, разработка любой вещи, сложной, которую ты никогда не делал. Просто сначала гуглишь, потом ИИ, потом начинаешь собирать все в кучу, дочитываешь доку. И в один день - херак, и всё работает. Проверено уже не 1 раз.
5️⃣ Постоянное обучение
Кто не развивается - тот лох :) Читаем книги, статьи. Смотрим видео, проходим курсы. Пристаем к ИИ с вопросами. Главное не забиваем на свои знания.
6️⃣ Рост незрим в моменте
В моменте ты думаешь, что тебе тяжело, возможно включается синдром самозванца. А спустя некоторое время, смотришь назад - а у тебя всё получилось. Разобрался и в DDD, и вроде как понял как выделять микросервисы, а еще и SDK какой-то напилил.
Наверное пока это всё, что я хотел сказать про рост: тяжело и страшно, крутая команда, постоянное обучение.
#разработка #management #философия
Здесь я имею ввиду - растем профессионально, как разработчик или менеджер.
Этот пост больше философский, моё имхо на тему.
Ниже я приведу некоторые характеристики правильного вектора своего развития.
1️⃣ Рост там, где тяжело
Очень сложно вырасти в комфорте. Лично для меня комфорт наступает примерно через 3-4 года работы в одном месте. Работа должна быть сложной, непонятной.
Приведу пример самого максимального роста из своего опыта: позвали работать лидом в команду, на новый язык программирования и абсолютно незнакомый мне домен. И через пару месяцев выставили KPI и решили меня уволить :) От KPI я сразу отказался и пошел искать работу, но в итоге, чудесным образом, я остался в компании, стал полноценным лидом, разобрался в домене и ЯП.
2️⃣ Команда
Сильная команда выращивает хороших разработчиков. Сошлюсь на книгу Человеческий фактор, там было исследование что 1 сеньор работает в 4 раза лучше мидла и в 10 раз лучше джуна. Из собственного опыта - примерно согласен.
Ищите команду с сеньорами, но это очень сложно сделать! Ибо, сеньорство в разных компаниях разное, и по своему опыту: чем сильнее руководство - тем сильнее разработка. И наоборот.
Относительно примера выше: я бы не затащил лидство+домен+яп, если бы меня не окружали крутые специалисты. Включая разработку, qa, а особенно ПО, который у меня там был (Кирилл, привет :).
3️⃣ Жопочасы
Как ни крути количество потраченного времени решает. Команда, как собственный тренер, подсказывает направление знаний. Если заниматься одному - придется потратить больше времени на блуждания.
Пример: я сейчас учусь играть на электрогитаре. Прямая зависимость на потраченное время. Если на упражнения на ПШ я трачу 10 минут в день, то и выхлопа будет на 10 минут в день (те кто понял, тот понял). И да, у меня конечно есть преподаватель.
4️⃣ Это невозможно
Такая, мысленная ловушка, когда тебе выпадает какая-то задача и ты сначала думаешь что ее невозможно выполнить. Сейчас, с опытом, вообще нет таких мыслей, есть понимание что сложные задачи требуют больше времени. И всё.
Пример: да, разработка любой вещи, сложной, которую ты никогда не делал. Просто сначала гуглишь, потом ИИ, потом начинаешь собирать все в кучу, дочитываешь доку. И в один день - херак, и всё работает. Проверено уже не 1 раз.
5️⃣ Постоянное обучение
Кто не развивается - тот лох :) Читаем книги, статьи. Смотрим видео, проходим курсы. Пристаем к ИИ с вопросами. Главное не забиваем на свои знания.
6️⃣ Рост незрим в моменте
В моменте ты думаешь, что тебе тяжело, возможно включается синдром самозванца. А спустя некоторое время, смотришь назад - а у тебя всё получилось. Разобрался и в DDD, и вроде как понял как выделять микросервисы, а еще и SDK какой-то напилил.
Наверное пока это всё, что я хотел сказать про рост: тяжело и страшно, крутая команда, постоянное обучение.
#разработка #management #философия
🔥8🫡4👍1😢1
Event Sourcing
Это паттерн проектирования, при котором состояние агрегата определяется цепочкой событий. Т.е. вместо сохранения состояние в БД, ES хранит серию неизменяемых событий.
Основные компоненты:
- Событие (Event): факт изменение состояния
- Агрегат (Aggregate): объект чье состояние хранится и восстанавливается из событий
- Event Store: БД оптимизированная для записи событий
- Проекция (Projection): представление агрегата (чтение), построенное из событий
Как это работает?
Изменение состояния:
- Система генерирует событие при любом изменении агрегата
- Событие сохраняется в Event Store
Восстановление состояния:
Чтобы получить текущее состояние агрегата, система последовательно применяет все его события
Создание проекций:
Чтобы каждый раз при чтении не перечитывать всю цепочку событий, их можно переложить в отдельную БД, в более удобный вид. Например в обычную таблицу. Здесь главное, что эту таблицу нельзя изменять, только читать из нее.
Преимущества и недостатки
Расскажу из опыта, мы начинали включать ES для сложных, агрегационных отчетах. Такой отчет очень легко пересчитывался и исправлялся в случае ошибки. Ведь вся история у нас есть, если есть ошибка в алгоритме расчета - просто исправляешь алгоритм и перечитываешь ивенты.
Следовательно преимущества:
- Полный аудит: есть вся история изменений.
- Простая отладка временных ошибок: можно воспроизвести состояние системы на любой момент.
- Очень гибко: легко создавать новые проекции.
При этом нужно было переосмыслить некоторые подходы к архитектуре, разобраться что такое CQRS и как с ним работать.
Я вполне представляю себе историю когда агрегат долго живет, у него очень много ивентов и его восстановление занимает какое-то время. Но это решается снэпшотом агрегата за какое-то стабильное время. Т.е. восстановили состояние агрегата и положили в реляционную БД. Далее при чтении сначала читаем снэпшот, а потом применяем к нему новые ивенты.
Где применять?
Там где важны исторические данные и точность - любые финансовые и медицинские системы.
Немного о технической реализации
Любое изменение агрегата должно генерировать ивент. Ивенты либо складываются во внутреннее поле агрегата, либо возвращаются из метода агрегата. Тут кому что нравится.
В данном случае пусть ивенты хранятся прям внутри агрегата.
Итого: использовать вообще везде ES я бы не стал, но есть некоторые контексты, где с ES жизнь была бы проще. Если уж решили использовать ES, то надо подумать о структуре ивентов, возможно версионировании ивента, миграциях.
#DDD
Это паттерн проектирования, при котором состояние агрегата определяется цепочкой событий. Т.е. вместо сохранения состояние в БД, ES хранит серию неизменяемых событий.
Основные компоненты:
- Событие (Event): факт изменение состояния
{ "type": "OrderCreated", "data": { "id": "123", "total": 100 } }- Агрегат (Aggregate): объект чье состояние хранится и восстанавливается из событий
- Event Store: БД оптимизированная для записи событий
- Проекция (Projection): представление агрегата (чтение), построенное из событий
Как это работает?
Изменение состояния:
- Система генерирует событие при любом изменении агрегата
- Событие сохраняется в Event Store
Восстановление состояния:
Чтобы получить текущее состояние агрегата, система последовательно применяет все его события
func RehydrateOrder(events []Event) *Order {
order := NewOrder()
for _, event := range events {
// Применяем событие к агрегату
order.Apply(event)
}
return order
}Создание проекций:
Чтобы каждый раз при чтении не перечитывать всю цепочку событий, их можно переложить в отдельную БД, в более удобный вид. Например в обычную таблицу. Здесь главное, что эту таблицу нельзя изменять, только читать из нее.
Преимущества и недостатки
Расскажу из опыта, мы начинали включать ES для сложных, агрегационных отчетах. Такой отчет очень легко пересчитывался и исправлялся в случае ошибки. Ведь вся история у нас есть, если есть ошибка в алгоритме расчета - просто исправляешь алгоритм и перечитываешь ивенты.
Следовательно преимущества:
- Полный аудит: есть вся история изменений.
- Простая отладка временных ошибок: можно воспроизвести состояние системы на любой момент.
- Очень гибко: легко создавать новые проекции.
При этом нужно было переосмыслить некоторые подходы к архитектуре, разобраться что такое CQRS и как с ним работать.
Я вполне представляю себе историю когда агрегат долго живет, у него очень много ивентов и его восстановление занимает какое-то время. Но это решается снэпшотом агрегата за какое-то стабильное время. Т.е. восстановили состояние агрегата и положили в реляционную БД. Далее при чтении сначала читаем снэпшот, а потом применяем к нему новые ивенты.
Где применять?
Там где важны исторические данные и точность - любые финансовые и медицинские системы.
Немного о технической реализации
Любое изменение агрегата должно генерировать ивент. Ивенты либо складываются во внутреннее поле агрегата, либо возвращаются из метода агрегата. Тут кому что нравится.
func (o *Order) Cancel() error {
if o.Status == "paid" {
return errors.New("невозможно отменить оплаченный заказ")
}
o.Status = "cancelled"
o.Version++
o.Events = append(o.Events, Event{
Type: "OrderCancelled",
Data: json.RawMessage(`{"reason": "client request"}`),
})
return nil
}В данном случае пусть ивенты хранятся прям внутри агрегата.
Итого: использовать вообще везде ES я бы не стал, но есть некоторые контексты, где с ES жизнь была бы проще. Если уж решили использовать ES, то надо подумать о структуре ивентов, возможно версионировании ивента, миграциях.
#DDD
Telegram
От сердца к коду
Тактические паттерны DDD: Aggregate
Ну вот мы и подошли к самому интересному. Кто-то считает что агрегаты это самое важное в технической части DDD. Кто-то их использует очень редко. Для меня это очень сложное понятие, давайте разбираться.
Если сущность…
Ну вот мы и подошли к самому интересному. Кто-то считает что агрегаты это самое важное в технической части DDD. Кто-то их использует очень редко. Для меня это очень сложное понятие, давайте разбираться.
Если сущность…
🫡3🔥2👍1
Делегирование
Перед любым руководителем, на любом из уровней, лежат какие-то задачи. Часть задач руководитель обязан решать сам, а часть задач нужно уметь делегировать. И умение правильно делегировать это очень важная тема.
Делегировать обязанность ради роста одного из разработчиков - очень полезно, а делегировать только рутину (скучные и низкоприоритетные задачи) - показать разработчику, что его работа не очень важна.
А что если не делегировать совсем? Скорее всего ты сам же и станешь узким местом, команда не будет расти, перестанет хватать времени и начнется выгорание. А еще, как тогда ходить в отпуск?
Для начала давайте разделим свои задачи на три категории:
1. Стратегические задачи. Например, архитектура. Оставляем себе, это наша зона ответственности.
2. Операционные задачи. Например, код-ревью или управление спринтами. Делегируем.
3. Рутина: автоматизируем.
Да, эти три категории относятся к руководителю выше техлида. Хотя, у техлида своя стратегия и операционка.
Ок, мы определились с операционными задачами, теперь надо решить кому их отдать? Было бы неплохо, если бы у нас был сотрудник, у которого сошлись бы сразу три следующих качества:
1. Скилл: хватает компетенций
2. Интерес: у сотрудника присутствует энтузиазм в отношении задачи.
3. Нагрузка: есть время и ресурсы.
Но конечно и из этого правила есть исключения, иногда можно намеренно делегировать задачу человеку с недостающим скиллом, но с высоким потенциалом. Тут просто надо понимать, что на первых порах от нас самих потребуется больше времени на поддержку.
Следующий этап: чётко поставить задачу:
- Цель: почему эта задача важна? Без почему - задача воспринимается как рутина.
- Ожидаемый результат: какой конечный результат вы хотите видеть?
- Сроки: если они есть.
- Ресурсы и полномочия: что есть, чтобы решить задачу. Какие решения сотрудник может принимать сам?
Дальше: не мешать! Не заниматься микроменеджментом! Это не значит, что надо полностью исчезнуть из процесса, быть всегда доступным для консультаций, но не вмешиваться без необходимости.
В одной из книг я прочитал, что делегирование, для лида, это не передача ответственности, а передача исполнения. Поэтому: контролируем, но не мешаем. Например, можно поставить еженедельные созвоны по проблеме, просто раз в день текстом пишет в чате о результатах, или договориться о демонстрации промежуточных результатов.
И последний шаг: после завершения задачи обязательно дать обратную связь сотруднику. Похвалить за успехи и указать что можно улучшить.
В целом я уверен в том, что любой руководитель должен всегда растить себе замену. Делегирование - это один из способов роста сотрудника. Кстати, я не говорил, что взятие дополнительных обязанностей на себя не должно быть отмечено материально. Если делегировали что-то действительно полезное для команды и важное, позаботьтесь и о финансовой мотивации также.
Выше, я написал что код-ревью это операционка. Для меня как для руководителя разработки - это так. А вот как быть с техлидом? Должен ли всегда только он делать код-ревью? Лично я считаю, что нет, часть ревью надо делегировать.
Итого, делегирование – это стратегический инструмент менеджера, а не просто сброс задач. Это инвестиция в рост команды и освобождение своего времени для того, чтобы заниматься стратегически важными задачами.
#management
Перед любым руководителем, на любом из уровней, лежат какие-то задачи. Часть задач руководитель обязан решать сам, а часть задач нужно уметь делегировать. И умение правильно делегировать это очень важная тема.
Делегировать обязанность ради роста одного из разработчиков - очень полезно, а делегировать только рутину (скучные и низкоприоритетные задачи) - показать разработчику, что его работа не очень важна.
А что если не делегировать совсем? Скорее всего ты сам же и станешь узким местом, команда не будет расти, перестанет хватать времени и начнется выгорание. А еще, как тогда ходить в отпуск?
Для начала давайте разделим свои задачи на три категории:
1. Стратегические задачи. Например, архитектура. Оставляем себе, это наша зона ответственности.
2. Операционные задачи. Например, код-ревью или управление спринтами. Делегируем.
3. Рутина: автоматизируем.
Да, эти три категории относятся к руководителю выше техлида. Хотя, у техлида своя стратегия и операционка.
Ок, мы определились с операционными задачами, теперь надо решить кому их отдать? Было бы неплохо, если бы у нас был сотрудник, у которого сошлись бы сразу три следующих качества:
1. Скилл: хватает компетенций
2. Интерес: у сотрудника присутствует энтузиазм в отношении задачи.
3. Нагрузка: есть время и ресурсы.
Но конечно и из этого правила есть исключения, иногда можно намеренно делегировать задачу человеку с недостающим скиллом, но с высоким потенциалом. Тут просто надо понимать, что на первых порах от нас самих потребуется больше времени на поддержку.
Следующий этап: чётко поставить задачу:
- Цель: почему эта задача важна? Без почему - задача воспринимается как рутина.
- Ожидаемый результат: какой конечный результат вы хотите видеть?
- Сроки: если они есть.
- Ресурсы и полномочия: что есть, чтобы решить задачу. Какие решения сотрудник может принимать сам?
Дальше: не мешать! Не заниматься микроменеджментом! Это не значит, что надо полностью исчезнуть из процесса, быть всегда доступным для консультаций, но не вмешиваться без необходимости.
В одной из книг я прочитал, что делегирование, для лида, это не передача ответственности, а передача исполнения. Поэтому: контролируем, но не мешаем. Например, можно поставить еженедельные созвоны по проблеме, просто раз в день текстом пишет в чате о результатах, или договориться о демонстрации промежуточных результатов.
И последний шаг: после завершения задачи обязательно дать обратную связь сотруднику. Похвалить за успехи и указать что можно улучшить.
В целом я уверен в том, что любой руководитель должен всегда растить себе замену. Делегирование - это один из способов роста сотрудника. Кстати, я не говорил, что взятие дополнительных обязанностей на себя не должно быть отмечено материально. Если делегировали что-то действительно полезное для команды и важное, позаботьтесь и о финансовой мотивации также.
Выше, я написал что код-ревью это операционка. Для меня как для руководителя разработки - это так. А вот как быть с техлидом? Должен ли всегда только он делать код-ревью? Лично я считаю, что нет, часть ревью надо делегировать.
Итого, делегирование – это стратегический инструмент менеджера, а не просто сброс задач. Это инвестиция в рост команды и освобождение своего времени для того, чтобы заниматься стратегически важными задачами.
#management
7💯5👍3🫡2
CQRS (часть 1)
Это архитектурный паттерн, который разделяет операций по двум типам:
1. Команды (Commands) - изменение состояния (Create, Update, Delete)
2. Запросы (Queries) - чтение данных (Read)
В CRUD-архитектурах операции чтения и записи часто используют одну и ту же модель данных, по сути один и тот же объект. Но если у нас Event Sourcing или мы оптимизировали запись, но логика чтения оказалось не эффективной для сложных запросов. Или нам надо масштабировать отдельно запись и чтение данных. И вот мы логически дошли до разных моделей данных на чтение и запись.
Еще немного усложним себе жизнь: пусть изменяем данные в одной БД, а читаем из другой. Кажется мы подошли к ключевому вопросу реализации CQRS. Решение:
1. Event Sourcing: write-база = event store (PostgreSQL), а read-модель строится путем применения этих событий и лежит, например в MongoDB (Eventual Consistency).
2. Триггеры на таблицах write-базы генерят события при изменении и обновляют read-базу
3. Domain Events, приложение явно генерирует события, отдельный процесс обрабатывает события и обновляет read-базу.
Давайте более внимательно остановимся на Event Sourcing и реализуем полный цикл на примере создания задачи.
Команда:
Обработчик:
Структура таблицы Event Store (PostgreSQL):
Сохраняем ивент:
Пришло время обработать новое событие (projection), для этого у нас будет отдельный worker (процесс):
Обновляем read-модель:
С обновлением понятно как продолжать - нужно добавить метод handleTaskUpdated, который найдем в монге правильную запись и относительно ивента domain.TaskUpdatedEvent обновит поля в ней.
to be continued...
#DDD
Это архитектурный паттерн, который разделяет операций по двум типам:
1. Команды (Commands) - изменение состояния (Create, Update, Delete)
2. Запросы (Queries) - чтение данных (Read)
В CRUD-архитектурах операции чтения и записи часто используют одну и ту же модель данных, по сути один и тот же объект. Но если у нас Event Sourcing или мы оптимизировали запись, но логика чтения оказалось не эффективной для сложных запросов. Или нам надо масштабировать отдельно запись и чтение данных. И вот мы логически дошли до разных моделей данных на чтение и запись.
Еще немного усложним себе жизнь: пусть изменяем данные в одной БД, а читаем из другой. Кажется мы подошли к ключевому вопросу реализации CQRS. Решение:
1. Event Sourcing: write-база = event store (PostgreSQL), а read-модель строится путем применения этих событий и лежит, например в MongoDB (Eventual Consistency).
2. Триггеры на таблицах write-базы генерят события при изменении и обновляют read-базу
3. Domain Events, приложение явно генерирует события, отдельный процесс обрабатывает события и обновляет read-базу.
Давайте более внимательно остановимся на Event Sourcing и реализуем полный цикл на примере создания задачи.
Команда:
POST /tasks
{
"title": "Разработать архитектуру",
"description": "Спроектировать систему на CQRS+ES"
}
Обработчик:
func (h *CreateTaskHandler) Handle(cmd CreateTaskCommand) error {
event := domain.NewTaskCreatedEvent(
uuid.New().String(),
cmd.Title,
cmd.Description,
time.Now(),
)
return h.eventStore.Append(event)
}Структура таблицы Event Store (PostgreSQL):
CREATE TABLE events (
id SERIAL PRIMARY KEY,
aggregate_id VARCHAR(255) NOT NULL, -- ID задачи
type VARCHAR(255) NOT NULL, -- Тип события
data JSONB NOT NULL, -- Данные события
version INT NOT NULL, -- Версия для оптимистичной блокировки
timestamp TIMESTAMPTZ NOT NULL
);
CREATE INDEX ON events (aggregate_id);
Сохраняем ивент:
INSERT INTO events (aggregate_id, type, data, version, timestamp)
VALUES (
'task-123',
'TaskCreated',
'{
"title": "Разработать архитектуру",
"description": "Спроектировать систему на CQRS+ES"
}',
1, -- начальная версия
NOW()
);
Пришло время обработать новое событие (projection), для этого у нас будет отдельный worker (процесс):
func (p *TaskProjection) Start() {
lastPosition := p.GetLastProcessedPosition()
for {
events := p.eventStore.LoadEvents(lastPosition, 100)
if len(events) == 0 {
time.Sleep(1 * time.Second)
continue
}
for _, event := range events {
p.HandleEvent(event)
lastPosition = event.ID
}
p.SavePosition(lastPosition)
}
}
func (p *TaskProjection) HandleEvent(event domain.Event) {
switch e := event.(type) {
case *domain.TaskCreatedEvent:
p.handleTaskCreated(e)
case *domain.TaskUpdatedEvent:
p.handleTaskUpdated(e)
case *domain.TaskCompletedEvent:
p.handleTaskCompleted(e)
}
}Обновляем read-модель:
func (p *TaskProjection) handleTaskCreated(event *domain.TaskCreatedEvent) {
doc := bson.M{
"_id": event.AggregateID,
"title": event.Title,
"description": event.Description,
"status": "new",
"created_at": event.Timestamp,
"updated_at": event.Timestamp,
"version": 1,
}
_, err := p.mongoCollection.InsertOne(context.Background(), doc)
if err != nil {
log.Printf("Ошибка вставки: %v", err)
}
}С обновлением понятно как продолжать - нужно добавить метод handleTaskUpdated, который найдем в монге правильную запись и относительно ивента domain.TaskUpdatedEvent обновит поля в ней.
to be continued...
#DDD
Telegram
От сердца к коду
Event Sourcing
Это паттерн проектирования, при котором состояние агрегата определяется цепочкой событий. Т.е. вместо сохранения состояние в БД, ES хранит серию неизменяемых событий.
Основные компоненты:
- Событие (Event): факт изменение состояния { "type":…
Это паттерн проектирования, при котором состояние агрегата определяется цепочкой событий. Т.е. вместо сохранения состояние в БД, ES хранит серию неизменяемых событий.
Основные компоненты:
- Событие (Event): факт изменение состояния { "type":…
🔥5🫡2
CQRS (часть 2)
О чем еще можно подумать?
- идемпотентность обработки ивентов
- пересборка read-модели
- А что если нам понадобится еще одна read-модель - без проблем, добавляем новую проекцию, при это не трогаем код команд.
Давайте еще посмотрим, пример структуры директорий приложения:
Кроме этого есть еще целые теории о том может ли команда возвращать результат или только ошибку? (имхо, может) Тут лучше вам самим углубиться в понимание этого паттерна и посмотреть на детали подробнее.
Итого, CQRS подходит для сложных систем с большим количеством бизнес-логики. Для приложений с высокой нагрузкой, где чтение и запись нужно масштабировать независимо.
Читаем базу:
- https://martinfowler.com/bliki/CQRS.html
- https://learn.microsoft.com/ru-ru/azure/architecture/patterns/cqrs
#DDD
О чем еще можно подумать?
- идемпотентность обработки ивентов
func (p *TaskProjection) HandleEvent(event domain.Event) {
// Проверяем, не обрабатывали ли мы уже это событие
if p.IsEventProcessed(event.ID) {
return
}
// ... логика обработки ...
p.MarkEventProcessed(event.ID)
}- пересборка read-модели
func (p *TaskProjection) Rebuild() {
// 1. Удаляем все данные в MongoDB
p.mongoCollection.DeleteMany({})
// 2. Читаем все события с начала
events := p.eventStore.LoadAllEvents()
// 3. Применяем события по порядку
for _, event := range events {
p.HandleEvent(event)
}
}- А что если нам понадобится еще одна read-модель - без проблем, добавляем новую проекцию, при это не трогаем код команд.
Давайте еще посмотрим, пример структуры директорий приложения:
cmd/
internal/
├── task/
│ ├── domain/
│ │ ├── task.go
│ │ └── events/
│ │ │ └── task_created.go
│ ├── application/
│ │ ├── commands/
│ │ │ └── create_task.go
│ │ ├── queries/
│ │ │ └── get_tasks.go
│ │ └── projections/
│ │ │ └── task_projection.go
└── infrastructure/
├── read_db/ # Репа для чтения
└── write_db/ # Репа для записи
Кроме этого есть еще целые теории о том может ли команда возвращать результат или только ошибку? (имхо, может) Тут лучше вам самим углубиться в понимание этого паттерна и посмотреть на детали подробнее.
Итого, CQRS подходит для сложных систем с большим количеством бизнес-логики. Для приложений с высокой нагрузкой, где чтение и запись нужно масштабировать независимо.
Читаем базу:
- https://martinfowler.com/bliki/CQRS.html
- https://learn.microsoft.com/ru-ru/azure/architecture/patterns/cqrs
#DDD
martinfowler.com
bliki: CQRS
CQRS (Command Query Responsibility Segregation) is the notion that you can use a different model to update information than the model you use to read information
🔥5🫡3
Модель зрелости команды
Процессы в командах и сами команды должны всегда расти. Как раз для этих целей и существует модель зрелости, которая:
1. Позволяет провести диагностику команд
2. Задает roadmap для развития команд
Эта модель помогает понять текущее состояние и целенаправленно двигаться к большей эффективности и предсказуемости. Впервые услышал об этом от своего друга, который работает в бигтехе, и пошел разбираться что же это такое. Кажется что небольшим кампаниям или стартапам эта модель не нужна, хотя посмотреть как сделано "у больших" будет очень полезно.
Итак, в чем же суть? У кампании есть видение идеальной команды: процессы, проактивность и т.п. И есть некоторые последовательные шаги (уровни), характеристики команды на этом уровне и задачи, которые команда должна выполнять, чтобы перейти на следующий уровень зрелости. Например:
🔸Уровень 1: Начальный. Хаос, отсутствие процессов , нет стандартов тестирование и написания кода, игнор техдолга. Лид занимается тушением пожаров. Техническая аналогия: монолит со спагетти-кодом.
🔸Уровень 2: Появляются базовые процессы (Scrum/Kanban), но они еще хрупкие. Появляются стандарты кода, CI/CD, но тестирование не полное. Коммуникации налажены, но не всегда эффективны. Команда предсказуема в спокойных условиях. Лид поддерживает процессы, контролирует качество и сроки и начинает делегировать операционку. Техническая аналогия: появились модули, интерфейсы, есть базовые юнит-тесты.
🔸Уровень 3: Стабильные, принятые командой процессы, высокий уровень автоматизации. Техдолг разбирается системно, коммуникации эффективны. Лид занимается развитием людей (менторство), фасилитация. Техническая аналогия: DDD, Чистая архитектура, слабая связанность и явные контракты. Полный набор автоматических тестов.
🔸Уровень 4: Процессы измеряются и постоянно улучшаются командой (вводятся метрики качества, скорости). Превентивная работа с рисками и техдолгом. Высокая автономность и ответственность. Команда сама идентифицирует проблемы и предлагает решения. Сильная культура feedback и обучения. Лид создает среду для роста, защищает команду от внешнего хаоса и развивает инженерную культуру. Техническая аналогия: продвинутый мониторинг, алерты, автоматическое масштабирование и постоянный рефакторинг как часть процесса.
🔸Уровень 5: Инновации. Команда проактивно ищет способы улучшить продукт, процессы и технологии. Экспериментирование, глубокая экспертиза в домене и технологиях. Сильная внутренняя мотивация. Лид занимается видением, построением связей, привлечением лучших талантов и защищает среду команды. Техническая аналогия: можно подумать про оупенсорс-вклад.
Для начала определяем текущий уровень, а дальше уже лид должен начинать работать с командой для перевода на следующий. Это не быстрый процесс, но у нас уже есть роадмап, не забиваем, идем по нему.
Очень хорошо описано и есть пример уровней зрелости в Авито: https://habr.com/ru/companies/avito/articles/578342/
#management
Процессы в командах и сами команды должны всегда расти. Как раз для этих целей и существует модель зрелости, которая:
1. Позволяет провести диагностику команд
2. Задает roadmap для развития команд
Эта модель помогает понять текущее состояние и целенаправленно двигаться к большей эффективности и предсказуемости. Впервые услышал об этом от своего друга, который работает в бигтехе, и пошел разбираться что же это такое. Кажется что небольшим кампаниям или стартапам эта модель не нужна, хотя посмотреть как сделано "у больших" будет очень полезно.
Итак, в чем же суть? У кампании есть видение идеальной команды: процессы, проактивность и т.п. И есть некоторые последовательные шаги (уровни), характеристики команды на этом уровне и задачи, которые команда должна выполнять, чтобы перейти на следующий уровень зрелости. Например:
🔸Уровень 1: Начальный. Хаос, отсутствие процессов , нет стандартов тестирование и написания кода, игнор техдолга. Лид занимается тушением пожаров. Техническая аналогия: монолит со спагетти-кодом.
🔸Уровень 2: Появляются базовые процессы (Scrum/Kanban), но они еще хрупкие. Появляются стандарты кода, CI/CD, но тестирование не полное. Коммуникации налажены, но не всегда эффективны. Команда предсказуема в спокойных условиях. Лид поддерживает процессы, контролирует качество и сроки и начинает делегировать операционку. Техническая аналогия: появились модули, интерфейсы, есть базовые юнит-тесты.
🔸Уровень 3: Стабильные, принятые командой процессы, высокий уровень автоматизации. Техдолг разбирается системно, коммуникации эффективны. Лид занимается развитием людей (менторство), фасилитация. Техническая аналогия: DDD, Чистая архитектура, слабая связанность и явные контракты. Полный набор автоматических тестов.
🔸Уровень 4: Процессы измеряются и постоянно улучшаются командой (вводятся метрики качества, скорости). Превентивная работа с рисками и техдолгом. Высокая автономность и ответственность. Команда сама идентифицирует проблемы и предлагает решения. Сильная культура feedback и обучения. Лид создает среду для роста, защищает команду от внешнего хаоса и развивает инженерную культуру. Техническая аналогия: продвинутый мониторинг, алерты, автоматическое масштабирование и постоянный рефакторинг как часть процесса.
🔸Уровень 5: Инновации. Команда проактивно ищет способы улучшить продукт, процессы и технологии. Экспериментирование, глубокая экспертиза в домене и технологиях. Сильная внутренняя мотивация. Лид занимается видением, построением связей, привлечением лучших талантов и защищает среду команды. Техническая аналогия: можно подумать про оупенсорс-вклад.
Для начала определяем текущий уровень, а дальше уже лид должен начинать работать с командой для перевода на следующий. Это не быстрый процесс, но у нас уже есть роадмап, не забиваем, идем по нему.
Очень хорошо описано и есть пример уровней зрелости в Авито: https://habr.com/ru/companies/avito/articles/578342/
#management
Хабр
Модель зрелости: как оценивать и растить инженерные команды
Мы продолжаем делиться внутренними документами Авито. Сегодня это будет модель зрелости. Она может пригодиться как трекер внедрения инженерных практик всем компаниям, где есть своя разработка. Внутри...
🔥5🫡1
Гексагональная архитектура (ports & adapters)
Давайте сделаем шаг назад. Прежде мы разобрали CQRS, как подход к организации архитектуры. Довольно непростой подход, для сложной бизнес-логики. А что если нам надо что-то попроще, но при этом хорошо ложащееся на DDD и Чистую архитектуру? - Гексагональная архитектура или ports & adapters.
В конце статьи будет несколько ссылок на хабр, там можно будет подробнее прочитать, а в этой статье будут лишь некоторые пояснения:
- Порты - это интерфейсы, вот в прямом смысле слова: type Reader interface {} - это порт. Некий контракт.
- Адаптеры - это реализация интерфейсов. Например, репозитории или клиент который ходит в какой-то внешний сервис.
- Ядро - чистая бизнес-логика, доменная модель. Например, расчет налогов, комиссий. Ничего не знает про внешние зависимости. Реализуется через агрегат, entity, value object, domain service. На этом уровне используются порты.
Преимущества:
- Ядро чистое и независимое от инфраструктуры.
- Легкая смена инфраструктурных компонентов, например, PostgreSQL на Mongo. Просто реализуем адаптер на нужный порт репозитория.
- Тестируемость. Всё ядро несложно закрыть тестами, мокаем порты, тестируем на моках. Репозитории тестируем на отдельной тестовой репе, HTTP-handler тестируем отдельно мокая сервисы.
- Четкое разделение ответственности: понятно что где должно лежать.
- Легко поддается эволюции, отлично сочетается с DDD и Чистой архитектурой, позволяет легко ограничивать контекст. Да и качественно продуманная гексагональная архитектура является фундаментом для перехода на микросервисы (ядро = потенциальный сервис).
- Кстати, DDD не запрещает в разных ограниченных контекстах иметь разные архитектурные подходы. Где-то гексагон, а где-то CQRS.
Лично моё мнение: везде, где CQRS слишком сложный, нужно использовать гексагон.
Ну и последнее - давайте посмотрим на структуру директорий для проекта на golang:
Ну и в конце , что бы почитать: https://habr.com/ru/companies/timeweb/articles/771338/
Давайте сделаем шаг назад. Прежде мы разобрали CQRS, как подход к организации архитектуры. Довольно непростой подход, для сложной бизнес-логики. А что если нам надо что-то попроще, но при этом хорошо ложащееся на DDD и Чистую архитектуру? - Гексагональная архитектура или ports & adapters.
В конце статьи будет несколько ссылок на хабр, там можно будет подробнее прочитать, а в этой статье будут лишь некоторые пояснения:
- Порты - это интерфейсы, вот в прямом смысле слова: type Reader interface {} - это порт. Некий контракт.
- Адаптеры - это реализация интерфейсов. Например, репозитории или клиент который ходит в какой-то внешний сервис.
- Ядро - чистая бизнес-логика, доменная модель. Например, расчет налогов, комиссий. Ничего не знает про внешние зависимости. Реализуется через агрегат, entity, value object, domain service. На этом уровне используются порты.
Преимущества:
- Ядро чистое и независимое от инфраструктуры.
- Легкая смена инфраструктурных компонентов, например, PostgreSQL на Mongo. Просто реализуем адаптер на нужный порт репозитория.
- Тестируемость. Всё ядро несложно закрыть тестами, мокаем порты, тестируем на моках. Репозитории тестируем на отдельной тестовой репе, HTTP-handler тестируем отдельно мокая сервисы.
- Четкое разделение ответственности: понятно что где должно лежать.
- Легко поддается эволюции, отлично сочетается с DDD и Чистой архитектурой, позволяет легко ограничивать контекст. Да и качественно продуманная гексагональная архитектура является фундаментом для перехода на микросервисы (ядро = потенциальный сервис).
- Кстати, DDD не запрещает в разных ограниченных контекстах иметь разные архитектурные подходы. Где-то гексагон, а где-то CQRS.
Лично моё мнение: везде, где CQRS слишком сложный, нужно использовать гексагон.
Ну и последнее - давайте посмотрим на структуру директорий для проекта на golang:
project-name/
├── cmd/ # Точки входа в приложение
│ └── api/
│ └── main.go
├── internal/ # Внутренний код приложения
│ ├── core/ # Бизнес-логика (домен)
│ │ ├── domain/ # Доменные модели
│ │ │ ├── user.go
│ │ │ └── product.go
│ │ ├── ports/ # Порты (интерфейсы)
│ │ │ ├── input/ # Входные порты (primary/driving)
│ │ │ │ └── service.go
│ │ │ └── output/ # Выходные порты (secondary/driven)
│ │ │ └── repository.go
│ │ └── services/ # Реализация сервисов
│ │ └── user_service.go
│ ├── adapters/ # Адаптеры
│ │ ├── primary/ # Входные адаптеры
│ │ │ ├── http/ # HTTP handlers
│ │ │ └── grpc/ # gRPC handlers
│ │ └── secondary/ # Выходные адаптеры
│ │ ├── postgres/ # PostgreSQL репозиторий
│ │ └── redis/ # Redis кэш
│ └── config/ # Конфигурация приложения
│ └── config.go
├── pkg/ # Публичные пакеты
│ └── common/ # Общие утилиты
├── api/ # API документация
│ └── swagger/
├── test/ # Тесты
│ ├── integration/
│ └── e2e/
├── docs/ # Документация проекта
├── scripts/ # Скрипты для сборки и развертывания
├── go.mod
├── go.sum
├── Makefile
└── README.md
Ну и в конце , что бы почитать: https://habr.com/ru/companies/timeweb/articles/771338/
Telegram
От сердца к коду
Антипаттерны доменной модели
🔸Анемичная модель (Anemic Domain Model)
Сущность не содержит никакой бизнес-логики (методов). По сути, сущность используется как DTO.
Это влечет за собой нарушение инкапсуляции, логика уезжает на прикладной уровень.
🔸ORM-анемия…
🔸Анемичная модель (Anemic Domain Model)
Сущность не содержит никакой бизнес-логики (методов). По сути, сущность используется как DTO.
Это влечет за собой нарушение инкапсуляции, логика уезжает на прикладной уровень.
🔸ORM-анемия…
👍6🫡4❤2😢1
Гексагональная vs Чистая vs CQRS
Давайте коротенько подведем итог разным видам архитектурных подходов.
Гексагональная или Чистая архитектуры. Для меня это синонимы, чистая - немного переосмысленная гексагональная. Основная идея в том, чтобы сделать приложение независимым от инфраструктуры. Я уже говорил что везде где не CQRS использую гексагон (чистую):
- Изолирую доменный слой
- Придерживаюсь SOLID
- Перекрываюсь везде интерфейсами
- Пишу изолированные тесты по слоям
CQRS - разделение операций чтения и записи. Основная идея в том, что чтение и запись могут использовать разные модели данных и обрабатываться отдельно. Причины использования:
- Денежные или медицинские данные
- Появляются проблемы с производительностью на чтение или запись
- Есть очень сложные запросы
- Нужна подробная история изменений
- Принимаем Eventual Consistency
Эти подходы не взаимоисключающие, их можно комбинировать:
- Также изолируем бизнес-логику в доменном слое
- Адаптеры работают на инфраструктурном слое и реализуют порты
- CQRS работает на application уровне
#DDD
Давайте коротенько подведем итог разным видам архитектурных подходов.
Гексагональная или Чистая архитектуры. Для меня это синонимы, чистая - немного переосмысленная гексагональная. Основная идея в том, чтобы сделать приложение независимым от инфраструктуры. Я уже говорил что везде где не CQRS использую гексагон (чистую):
- Изолирую доменный слой
- Придерживаюсь SOLID
- Перекрываюсь везде интерфейсами
- Пишу изолированные тесты по слоям
CQRS - разделение операций чтения и записи. Основная идея в том, что чтение и запись могут использовать разные модели данных и обрабатываться отдельно. Причины использования:
- Денежные или медицинские данные
- Появляются проблемы с производительностью на чтение или запись
- Есть очень сложные запросы
- Нужна подробная история изменений
- Принимаем Eventual Consistency
Эти подходы не взаимоисключающие, их можно комбинировать:
- Также изолируем бизнес-логику в доменном слое
- Адаптеры работают на инфраструктурном слое и реализуют порты
- CQRS работает на application уровне
#DDD
Telegram
От сердца к коду
Гексагональная архитектура (ports & adapters)
Давайте сделаем шаг назад. Прежде мы разобрали CQRS, как подход к организации архитектуры. Довольно непростой подход, для сложной бизнес-логики. А что если нам надо что-то попроще, но при этом хорошо ложащееся…
Давайте сделаем шаг назад. Прежде мы разобрали CQRS, как подход к организации архитектуры. Довольно непростой подход, для сложной бизнес-логики. А что если нам надо что-то попроще, но при этом хорошо ложащееся…
❤3👍2🫡2
Оставить команду или менять?
Давайте обсудим ситуацию: вы выходите на позицию лида команды — что делать с текущим составом? Удерживать всех или менять? Пройдемся по вариантам.
1️⃣ Удерживаем людей любыми силами.
Раньше я был поклонником теории Ицхака Адизеса, если кратко, то он считает, что сила команды в ее разнообразии. В разнообразии людей разных архетипов: кто-то доводит задачу до конца, кто-то смотрит вперед и в будущее, кто-то бюрократ (в хорошем смысле этого слова), а у кого-то просто высокие софт скиллы.
Даже на наших собеседованиях на работу я пользовался этой теорией и давал шанс людям сильно отличающимся от меня. Соответственно, отталкиваясь от этого, надо стараться удержать разных людей в своей команде, стараться найти с ними общий язык, договориться.
И совсем недавно я понял, а потом и подтвердил в выводах некоторых книг по менеджменту, что это плохой путь, по крайней мере лично у меня в команде он не работает. Хотите вот такой солянки из разных людей - готовьтесь постоянно спорить по любой мелочи. По сути команда вроде бы и работает, но лид тратит кучу времени на убеждение, лишнее нытье и споры. Лично я понял, что это не мой путь.
2️⃣ Заменить всех.
На мой взгляд, это другая крайность. Рискуем и разработкой, и репутацией компании. Быстрая замена остановит поставку — новых ещё надо нанять и онбордить. Кажется очевидным: это не наш путь.
3️⃣ Частичная замена.
Вот оно! Чувствуете?) Вы приходите в уже состоявшуюся команду со своими требованиями и видением процессов разработки. Вы лид, вы должны, как минимум технически вести команду, а кроме того и постоянно следить за состоянием людей. Вы начинаете заводить свои процессы, сталкиваетесь со спорами и сопротивлением, но вы не отступаете и постепенно проводите то, что вы считаете важным. Половина команды принимает новые правила и требования, другая часть пытается их саботировать. Несколько раз вы пытаетесь их убедить, объяснить, почему следовать новым требованиям это важно, но они то лучше знают, как это надо делать или как они привыкли.
А дальше все просто: обращаетесь к местным HR, открываете вакансию на подбор разработчика и начинаете постепенно менять саботажников на новых людей. Новых с первого дня погружаете в процессы и культуру команды.
Выводы:
- Замена лидера почти всегда ведёт к частичной замене команды — и это нормально, если цель не «заменить людей», а повысить соответствие целям кампании.
- При приеме на работу лида в команду нужно сразу уточнять вопрос об увольнении/найме
- Не тратьте много времени на убеждение саботажников
- Подбирайте в свою команду людей с общими для вас ценностями и идеями, не только по техническим скиллам.
Давайте обсудим ситуацию: вы выходите на позицию лида команды — что делать с текущим составом? Удерживать всех или менять? Пройдемся по вариантам.
1️⃣ Удерживаем людей любыми силами.
Раньше я был поклонником теории Ицхака Адизеса, если кратко, то он считает, что сила команды в ее разнообразии. В разнообразии людей разных архетипов: кто-то доводит задачу до конца, кто-то смотрит вперед и в будущее, кто-то бюрократ (в хорошем смысле этого слова), а у кого-то просто высокие софт скиллы.
Даже на наших собеседованиях на работу я пользовался этой теорией и давал шанс людям сильно отличающимся от меня. Соответственно, отталкиваясь от этого, надо стараться удержать разных людей в своей команде, стараться найти с ними общий язык, договориться.
И совсем недавно я понял, а потом и подтвердил в выводах некоторых книг по менеджменту, что это плохой путь, по крайней мере лично у меня в команде он не работает. Хотите вот такой солянки из разных людей - готовьтесь постоянно спорить по любой мелочи. По сути команда вроде бы и работает, но лид тратит кучу времени на убеждение, лишнее нытье и споры. Лично я понял, что это не мой путь.
2️⃣ Заменить всех.
На мой взгляд, это другая крайность. Рискуем и разработкой, и репутацией компании. Быстрая замена остановит поставку — новых ещё надо нанять и онбордить. Кажется очевидным: это не наш путь.
3️⃣ Частичная замена.
Вот оно! Чувствуете?) Вы приходите в уже состоявшуюся команду со своими требованиями и видением процессов разработки. Вы лид, вы должны, как минимум технически вести команду, а кроме того и постоянно следить за состоянием людей. Вы начинаете заводить свои процессы, сталкиваетесь со спорами и сопротивлением, но вы не отступаете и постепенно проводите то, что вы считаете важным. Половина команды принимает новые правила и требования, другая часть пытается их саботировать. Несколько раз вы пытаетесь их убедить, объяснить, почему следовать новым требованиям это важно, но они то лучше знают, как это надо делать или как они привыкли.
А дальше все просто: обращаетесь к местным HR, открываете вакансию на подбор разработчика и начинаете постепенно менять саботажников на новых людей. Новых с первого дня погружаете в процессы и культуру команды.
Выводы:
- Замена лидера почти всегда ведёт к частичной замене команды — и это нормально, если цель не «заменить людей», а повысить соответствие целям кампании.
- При приеме на работу лида в команду нужно сразу уточнять вопрос об увольнении/найме
- Не тратьте много времени на убеждение саботажников
- Подбирайте в свою команду людей с общими для вас ценностями и идеями, не только по техническим скиллам.
👍7👏2🫡2
Компромиссы
Было ли у вас такое, что придя на новую работу, вы понимали, что попали в очень слабую команду? На собесе все выглядели сеньорами, а по факту и говнокод и кривые процессы?
Если вы зашли сеньором - не тратьте время, просто сразу уходите на рынок. С этой должности очень трудно менять ситуацию, придется слишком много всем доказывать, что ты не дурак.
А вот что делать, если вы зашли лидом?
1. Если вы еще на собеседовании не уточнили, насколько сильно можете влиять на процессы и команду - уточните это прямо сейчас (увольнение, влияние на ЗП, изменение процессов).
2. Начинайте выстраивать процессы, смотреть на команду, находить тех кто надежнее и делегировать.
3. Не идите на компромиссы! По-другому, не соглашайтесь на худшее, когда вы знаете, как сделать лучше. Или - не понижайте планку своего качества. Вот тут и остановимся подробнее, ради этого пункта и писался весь пост.
Не идите на компромиссы с самим собой: не отдавайте плохой код в ревью, пока пускай так, а потом поправлю, когда время будет. И не идите на компромиссы в отношении кого-то: например при ревью чьего-то кода, не делайте поблажек, это медвежья услуга.
У меня был опыт, когда я несколько лет пытался вырастить разработчика, а он продолжал делать одни и те же ошибки, а я всё ждал: ну вот следующий МР будет лучше. Не будет. Во-первых, сразу показываем новой команде новые стандарты работы. Во-вторых, лучше сразу жестко обозначить свои требования к коду.
Обратной стороной "не хождения на компромиссы" может стать токсичная и пассивно-агрессивная обратная связь. Признаюсь, бывало и у меня, приходилось извиняться. Прикрываясь компромиссами можно перейти черту адекватного фидбэка. В целом здоровой обратной связи нужно еще поучиться.
4. Пишем доку "Требования к качеству разработки", в которой расписываем:
- чеклист на ревью
- code style
- документирование кода
- требования к написанию тестов и рефакторингу
Отдаем всё это добро на ознакомление команде, предупреждаем, что есть пару недель на разгон, а далее МРы будут отклоняться. За это время впиливаем в CICD нужные пайплайны и уже начинаем ревьюить, а кода пайплайны готовы - останавливаем деплой.
Далее команда начинает зреть, в ней появятся люди, которым можно доверять и делегировать качественное ревью, ну а тех кто саботирует новые стандарты - мы не держим.
#management
Было ли у вас такое, что придя на новую работу, вы понимали, что попали в очень слабую команду? На собесе все выглядели сеньорами, а по факту и говнокод и кривые процессы?
Если вы зашли сеньором - не тратьте время, просто сразу уходите на рынок. С этой должности очень трудно менять ситуацию, придется слишком много всем доказывать, что ты не дурак.
А вот что делать, если вы зашли лидом?
1. Если вы еще на собеседовании не уточнили, насколько сильно можете влиять на процессы и команду - уточните это прямо сейчас (увольнение, влияние на ЗП, изменение процессов).
2. Начинайте выстраивать процессы, смотреть на команду, находить тех кто надежнее и делегировать.
3. Не идите на компромиссы! По-другому, не соглашайтесь на худшее, когда вы знаете, как сделать лучше. Или - не понижайте планку своего качества. Вот тут и остановимся подробнее, ради этого пункта и писался весь пост.
Не идите на компромиссы с самим собой: не отдавайте плохой код в ревью, пока пускай так, а потом поправлю, когда время будет. И не идите на компромиссы в отношении кого-то: например при ревью чьего-то кода, не делайте поблажек, это медвежья услуга.
У меня был опыт, когда я несколько лет пытался вырастить разработчика, а он продолжал делать одни и те же ошибки, а я всё ждал: ну вот следующий МР будет лучше. Не будет. Во-первых, сразу показываем новой команде новые стандарты работы. Во-вторых, лучше сразу жестко обозначить свои требования к коду.
Обратной стороной "не хождения на компромиссы" может стать токсичная и пассивно-агрессивная обратная связь. Признаюсь, бывало и у меня, приходилось извиняться. Прикрываясь компромиссами можно перейти черту адекватного фидбэка. В целом здоровой обратной связи нужно еще поучиться.
4. Пишем доку "Требования к качеству разработки", в которой расписываем:
- чеклист на ревью
- code style
- документирование кода
- требования к написанию тестов и рефакторингу
Отдаем всё это добро на ознакомление команде, предупреждаем, что есть пару недель на разгон, а далее МРы будут отклоняться. За это время впиливаем в CICD нужные пайплайны и уже начинаем ревьюить, а кода пайплайны готовы - останавливаем деплой.
Далее команда начинает зреть, в ней появятся люди, которым можно доверять и делегировать качественное ревью, ну а тех кто саботирует новые стандарты - мы не держим.
#management
1👍9🔥3🫡3❤1