День 2627. #Карьера #SystemDesign
Как я Освоил Системное Проектирование. Окончание
Часть 1
Часть 2
Часть 3
Часть 4
Ресурсы
Существует множество видео, курсов и книг по теме. Платные курсы рекламировать не буду, решайте сами, стоят ли они того.
Книги:
1) «System Design. Подготовка к сложному интервью» Сюй А.
- Красивые диаграммы;
- Более 15 вариантов системного проектирования.
2) «Высоконагруженные приложения. Программирование, масштабирование, поддержка» Клеппман М.
- Глубокий технический анализ;
- Читать после изучения основ;
- Отличный справочник.
Сдвиг мышления, который изменил всё
Перед изучением системного проектирования.
Синдром самозванца:
- «Я недостаточно опытен»
- «Это слишком сложно»
- «Я никогда не пойму распределённые системы»
Тревога перед собеседованием:
- Надеюсь, я сталкивался с этой проблемой;
- Паника, когда застреваю;
- Заучивание решений.
Карьерный застой:
- Ограничен задачами по программированию;
- Неучастие в принятии архитектурных решений;
- Мимо руководящих должностей.
После освоения системного проектирования
Уверенность:
- Возможность обсуждать архитектуру с кем угодно;
- Понимание компромиссов;
- Мышление в масштабе.
Успех на собеседованиях:
- Возможность ответить на любой вопрос по проектированию;
- Сосредоточенность на размышлении, а не на запоминании.
На собеседованиях по системному проектированию (и в реальной работе):
Плохой ответ: «Мы будем использовать микросервисы и Kafka».
Хороший ответ: «Мы могли бы использовать микросервисы для независимого масштабирования, но, учитывая размер нашей команды (5 инженеров) и текущий масштаб (10 000 пользователей), хорошо структурированный монолит с чёткими границами модулей будет проще в обслуживании. Мы можем выделить сервисы позже, когда достигнем 100 тыс пользователей или нам потребуется независимое развёртывание».
Что изменилось:
- Продемонстрировал понимание компромиссов;
- Учел команду и масштаб;
- Предоставил обоснование;
- Обсудил эволюцию.
Интервьюерам не нужны идеальные проекты. Им нужно увидеть, как вы мыслите.
Итого
Проектирование систем — это навык, а не талант.
Вам не нужны:
- 10 лет опыта;
- Степень в области компьютерных наук;
- Фотографическая память;
- Природный талант.
Нужно только:
- Любопытство изучать, как всё работает;
- Готовность к систематическому обучению;
- Практика проектирования реальных систем;
- Терпение.
Проектирование систем — это не магия. Это навык, которому можно научиться, как приготовление пищи, вождение или игра на гитаре. Начните с основ. Практикуйтесь постоянно. Развивайте свои знания. Помните: каждый архитектор, каждый сеньор, каждый системный проектировщик начинал с того места, где вы сейчас.
Источник: https://medium.com/javarevisited/how-i-learned-system-design-861efe86f173
Как я Освоил Системное Проектирование. Окончание
Часть 1
Часть 2
Часть 3
Часть 4
Ресурсы
Существует множество видео, курсов и книг по теме. Платные курсы рекламировать не буду, решайте сами, стоят ли они того.
Книги:
1) «System Design. Подготовка к сложному интервью» Сюй А.
- Красивые диаграммы;
- Более 15 вариантов системного проектирования.
2) «Высоконагруженные приложения. Программирование, масштабирование, поддержка» Клеппман М.
- Глубокий технический анализ;
- Читать после изучения основ;
- Отличный справочник.
Сдвиг мышления, который изменил всё
Перед изучением системного проектирования.
Синдром самозванца:
- «Я недостаточно опытен»
- «Это слишком сложно»
- «Я никогда не пойму распределённые системы»
Тревога перед собеседованием:
- Надеюсь, я сталкивался с этой проблемой;
- Паника, когда застреваю;
- Заучивание решений.
Карьерный застой:
- Ограничен задачами по программированию;
- Неучастие в принятии архитектурных решений;
- Мимо руководящих должностей.
После освоения системного проектирования
Уверенность:
- Возможность обсуждать архитектуру с кем угодно;
- Понимание компромиссов;
- Мышление в масштабе.
Успех на собеседованиях:
- Возможность ответить на любой вопрос по проектированию;
- Сосредоточенность на размышлении, а не на запоминании.
На собеседованиях по системному проектированию (и в реальной работе):
Плохой ответ: «Мы будем использовать микросервисы и Kafka».
Хороший ответ: «Мы могли бы использовать микросервисы для независимого масштабирования, но, учитывая размер нашей команды (5 инженеров) и текущий масштаб (10 000 пользователей), хорошо структурированный монолит с чёткими границами модулей будет проще в обслуживании. Мы можем выделить сервисы позже, когда достигнем 100 тыс пользователей или нам потребуется независимое развёртывание».
Что изменилось:
- Продемонстрировал понимание компромиссов;
- Учел команду и масштаб;
- Предоставил обоснование;
- Обсудил эволюцию.
Интервьюерам не нужны идеальные проекты. Им нужно увидеть, как вы мыслите.
Итого
Проектирование систем — это навык, а не талант.
Вам не нужны:
- 10 лет опыта;
- Степень в области компьютерных наук;
- Фотографическая память;
- Природный талант.
Нужно только:
- Любопытство изучать, как всё работает;
- Готовность к систематическому обучению;
- Практика проектирования реальных систем;
- Терпение.
Проектирование систем — это не магия. Это навык, которому можно научиться, как приготовление пищи, вождение или игра на гитаре. Начните с основ. Практикуйтесь постоянно. Развивайте свои знания. Помните: каждый архитектор, каждый сеньор, каждый системный проектировщик начинал с того места, где вы сейчас.
Источник: https://medium.com/javarevisited/how-i-learned-system-design-861efe86f173
👍15
День 2628. #SystemDesign101
Что Происходит, Когда вы Вводите google.com в Браузере?
1. Вы вводите адрес веб-сайта в адресную строку браузера.
2. Браузер сначала проверяет свой кэш. Если адрес не найден в кэше, он должен найти IP-адрес.
3. Начинается поиск DNS (представьте, что вы ищете номер телефона). Запрос проходит через различные DNS-серверы (корневой, TLD - сервер имен доменов верхнего уровня - и авторитативный). Наконец, извлекается IP-адрес.
4. Браузер инициирует TCP-соединение, начиная с рукопожатия. Например, в случае HTTP 1.1 клиент и сервер выполняют трёхстороннее TCP-рукопожатие с сообщениями SYN, SYN-ACK и ACK.
5. Браузер отправляет HTTP-запрос на сервер, и сервер отвечает файлами HTML, CSS и JS.
6. Браузер обрабатывает ответ. Он анализирует HTML-документ и создаёт деревья DOM и CSSOM.
7. Браузер выполняет код JavaScript и отображает страницу, проходя через различные этапы (токенизатор, парсер, дерево рендеринга, компоновка и отрисовка).
8. Веб-страница появляется на экране.
Источник: https://bytebytego.com/guides/what-happens-when-you-type-google/
Что Происходит, Когда вы Вводите google.com в Браузере?
1. Вы вводите адрес веб-сайта в адресную строку браузера.
2. Браузер сначала проверяет свой кэш. Если адрес не найден в кэше, он должен найти IP-адрес.
3. Начинается поиск DNS (представьте, что вы ищете номер телефона). Запрос проходит через различные DNS-серверы (корневой, TLD - сервер имен доменов верхнего уровня - и авторитативный). Наконец, извлекается IP-адрес.
4. Браузер инициирует TCP-соединение, начиная с рукопожатия. Например, в случае HTTP 1.1 клиент и сервер выполняют трёхстороннее TCP-рукопожатие с сообщениями SYN, SYN-ACK и ACK.
5. Браузер отправляет HTTP-запрос на сервер, и сервер отвечает файлами HTML, CSS и JS.
6. Браузер обрабатывает ответ. Он анализирует HTML-документ и создаёт деревья DOM и CSSOM.
7. Браузер выполняет код JavaScript и отображает страницу, проходя через различные этапы (токенизатор, парсер, дерево рендеринга, компоновка и отрисовка).
8. Веб-страница появляется на экране.
Источник: https://bytebytego.com/guides/what-happens-when-you-type-google/
👍27
День 2629. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
30. Лидерство и наставничество
«Можете ли вы рассказать о своем подходе к лидерству и наставничеству в команде разработчиков? Как вы обеспечиваете положительное влияние вашего лидерства на результаты проекта и на развитие членов команды?»
Хороший ответ
Мой подход к лидерству и наставничеству в команде разработчиков основан на личном примере, развитии открытой коммуникации и активной поддержке профессионального роста членов команды. Вот как я реализую эти принципы.
- Лидерство личным примером: Я верю в демонстрацию поведения и практик, которые ожидаю от своей команды. Это включает в себя соблюдение лучших практик кодирования, поддержание высокого стандарта качества кода и демонстрацию приверженности целям проекта. Например, я обязательно участвую в проверках кода, вношу свой вклад в задачи кодирования и остаюсь в курсе последних технологий .NET, чтобы эффективно руководить командой.
- Развитие открытой коммуникации: Я поощряю регулярные командные встречи и индивидуальные консультации, чтобы обеспечить постоянную открытость каналов связи. Это помогает решать любые проблемы на ранних этапах, обмениваться идеями и совместно находить решения проблем.
- Профессиональный рост и наставничество: Я привержен профессиональному развитию членов своей команды. Это включает в себя регулярные сессии наставничества, поощрение посещения семинаров и конференций, а также предоставление членам команды возможностей принимать новые вызовы. Начинающих разработчиков я часто объединяю в пары с более опытными коллегами и чередую обязанности в проектах, чтобы расширить их кругозор и навыки.
- Обратная связь и признание: Я стараюсь предоставлять конструктивную обратную связь и отмечать достижения. Позитивное подкрепление и конструктивная критика помогают укрепить уверенность и улучшить навыки, что, в свою очередь, повышает производительность команды и моральный дух.
Внедряя эти стратегии, я стремлюсь создать благоприятную среду, которая не только способствует успеху проекта, но и развитию каждого члена команды.
Часто встречающийся неправильный ответ
«Как лидер, я принимаю все ключевые решения и определяю направление проекта. Я ожидаю, что моя команда будет следовать моему руководству и сосредоточится на своих задачах, чтобы проект был завершен вовремя».
Почему это неправильно
- Авторитарный подход: Этот ответ указывает на нисходящий, командно-административный подход к лидерству, который может подавлять творчество и снижать вовлеченность команды. Современное лидерство в разработке программного обеспечения часто требует инклюзивности и сотрудничества.
- Отсутствие делегирования: Не вовлекая команду в процесс принятия решений, лидер упускает ценные идеи и лишает членов команды возможности расти, преодолевая трудности и принимая решения.
- Пренебрежение наставничеством: Ответ не затрагивает ни один аспект наставничества или личностного развития членов команды, которые имеют решающее значение для поддержания мотивированной и квалифицированной команды.
Эта ошибка обычно проистекает из традиционного взгляда на лидерство, который может не соответствовать коллаборативной и динамичной природе современных команд разработчиков программного обеспечения, особенно в средах, поощряющих инновации и непрерывное обучение.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
30. Лидерство и наставничество
«Можете ли вы рассказать о своем подходе к лидерству и наставничеству в команде разработчиков? Как вы обеспечиваете положительное влияние вашего лидерства на результаты проекта и на развитие членов команды?»
Хороший ответ
Мой подход к лидерству и наставничеству в команде разработчиков основан на личном примере, развитии открытой коммуникации и активной поддержке профессионального роста членов команды. Вот как я реализую эти принципы.
- Лидерство личным примером: Я верю в демонстрацию поведения и практик, которые ожидаю от своей команды. Это включает в себя соблюдение лучших практик кодирования, поддержание высокого стандарта качества кода и демонстрацию приверженности целям проекта. Например, я обязательно участвую в проверках кода, вношу свой вклад в задачи кодирования и остаюсь в курсе последних технологий .NET, чтобы эффективно руководить командой.
- Развитие открытой коммуникации: Я поощряю регулярные командные встречи и индивидуальные консультации, чтобы обеспечить постоянную открытость каналов связи. Это помогает решать любые проблемы на ранних этапах, обмениваться идеями и совместно находить решения проблем.
- Профессиональный рост и наставничество: Я привержен профессиональному развитию членов своей команды. Это включает в себя регулярные сессии наставничества, поощрение посещения семинаров и конференций, а также предоставление членам команды возможностей принимать новые вызовы. Начинающих разработчиков я часто объединяю в пары с более опытными коллегами и чередую обязанности в проектах, чтобы расширить их кругозор и навыки.
- Обратная связь и признание: Я стараюсь предоставлять конструктивную обратную связь и отмечать достижения. Позитивное подкрепление и конструктивная критика помогают укрепить уверенность и улучшить навыки, что, в свою очередь, повышает производительность команды и моральный дух.
Внедряя эти стратегии, я стремлюсь создать благоприятную среду, которая не только способствует успеху проекта, но и развитию каждого члена команды.
Часто встречающийся неправильный ответ
«Как лидер, я принимаю все ключевые решения и определяю направление проекта. Я ожидаю, что моя команда будет следовать моему руководству и сосредоточится на своих задачах, чтобы проект был завершен вовремя».
Почему это неправильно
- Авторитарный подход: Этот ответ указывает на нисходящий, командно-административный подход к лидерству, который может подавлять творчество и снижать вовлеченность команды. Современное лидерство в разработке программного обеспечения часто требует инклюзивности и сотрудничества.
- Отсутствие делегирования: Не вовлекая команду в процесс принятия решений, лидер упускает ценные идеи и лишает членов команды возможности расти, преодолевая трудности и принимая решения.
- Пренебрежение наставничеством: Ответ не затрагивает ни один аспект наставничества или личностного развития членов команды, которые имеют решающее значение для поддержания мотивированной и квалифицированной команды.
Эта ошибка обычно проистекает из традиционного взгляда на лидерство, который может не соответствовать коллаборативной и динамичной природе современных команд разработчиков программного обеспечения, особенно в средах, поощряющих инновации и непрерывное обучение.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👎4👍2
День 2630. #ЗаметкиНаПолях
Проверяем Конфигурацию при Запуске. Начало
.NET-приложения со строго типизированной конфигурацией IOptions<T> и его вариантами предоставляют удобный способ привязки разделов appsettings.json к классам C#. Но привязка — это не то же самое, что проверка: отсутствие обязательного значения или число, выходящее за пределы допустимого диапазона, без проблем привяжутся и незаметно сломают приложение во время выполнения. IValidateOptions<T> — механизм, предоставляемый .NET для решения этой проблемы.
Рассмотрим типичный класс параметров:
Если в файле appsettings.json отсутствует Host, приложение запустится нормально. Ошибка проявится только при отправке email. Аннотации данных (
1. Они используют рефлексию, что приводит к:
- Затратам на производительность, что важно в сценариях с высокой нагрузкой и требованиям к быстрому запуску.
- Несовместимости с AOT и агрессивным триммингом (
Это можно исправить, используя генератор кода в .NET8+. Для активации генератора для конкретного класса валидатора параметров потребуется:
- Класс параметров, помеченный атрибутами Data Annotation.
- Частичный класс, реализующий IValidateOptions<T> и помеченный
Генератор заполнит тело класса при сборке.
2. Но иногда аннотация данных вовсе недостаточно, когда необходимы:
- Проверка нескольких свойств объекта конфигурации;
- Асинхронные или основанные на данных из БД проверки;
- Условная логика в зависимости от среды;
- Многократно используемые валидаторы, общие для нескольких типов конфигурации.
Здесь пригодится реализовать IValidateOptions<T>. Это интерфейс в Microsoft.Extensions.Options с единственным методом:
Вы реализуете этот интерфейс, регистрируете его в DI-контейнере, и инфраструктура Options вызывает его автоматически:
- лениво (при первом обращении),
- немедленно при запуске, если используется ValidateOnStart().
Вот простой валидатор для класса, описанного выше:
Регистрируем в Program.cs:
Если проверка не проходит, во время выполнения
Окончание следует…
Источник: https://bartwullems.blogspot.com/2026/03/validating-configuration-at-startup.html
Проверяем Конфигурацию при Запуске. Начало
.NET-приложения со строго типизированной конфигурацией IOptions<T> и его вариантами предоставляют удобный способ привязки разделов appsettings.json к классам C#. Но привязка — это не то же самое, что проверка: отсутствие обязательного значения или число, выходящее за пределы допустимого диапазона, без проблем привяжутся и незаметно сломают приложение во время выполнения. IValidateOptions<T> — механизм, предоставляемый .NET для решения этой проблемы.
Рассмотрим типичный класс параметров:
public class SmtpOptions
{
public string Host { get; set; } = "";
public int Port { get; set; }
public string FromAddress { get; set; } = "";
}
Если в файле appsettings.json отсутствует Host, приложение запустится нормально. Ошибка проявится только при отправке email. Аннотации данных (
[Required], [Range] и т.п.) в сочетании с вызовом ValidateDataAnnotations() помогают, но с ними есть 2 проблемы.1. Они используют рефлексию, что приводит к:
- Затратам на производительность, что важно в сценариях с высокой нагрузкой и требованиям к быстрому запуску.
- Несовместимости с AOT и агрессивным триммингом (
<PublishTrimmed>true</PublishTrimmed>). Могут быть исключены метаданные, от которых зависит рефлексия, что приводит к предупреждениям IL2026/IL3050 или к сбоям во время выполнения.Это можно исправить, используя генератор кода в .NET8+. Для активации генератора для конкретного класса валидатора параметров потребуется:
- Класс параметров, помеченный атрибутами Data Annotation.
- Частичный класс, реализующий IValidateOptions<T> и помеченный
[OptionsValidator], с пустым телом:using Microsoft.Extensions.Options;
[OptionsValidator]
public partial class ValidateSmtpOptions
: IValidateOptions<SmtpOptions>
{
}
Генератор заполнит тело класса при сборке.
2. Но иногда аннотация данных вовсе недостаточно, когда необходимы:
- Проверка нескольких свойств объекта конфигурации;
- Асинхронные или основанные на данных из БД проверки;
- Условная логика в зависимости от среды;
- Многократно используемые валидаторы, общие для нескольких типов конфигурации.
Здесь пригодится реализовать IValidateOptions<T>. Это интерфейс в Microsoft.Extensions.Options с единственным методом:
public interface IValidateOptions<TOptions>
where TOptions : class
{
ValidateOptionsResult Validate(
string? name, TOptions options);
}
Вы реализуете этот интерфейс, регистрируете его в DI-контейнере, и инфраструктура Options вызывает его автоматически:
- лениво (при первом обращении),
- немедленно при запуске, если используется ValidateOnStart().
Вот простой валидатор для класса, описанного выше:
public class SmtpOptionsValidator
: IValidateOptions<SmtpOptions>
{
public ValidateOptionsResult Validate(
string? name, SmtpOptions opts)
{
var errors = new List<string>();
if (string.IsNullOrWhiteSpace(opts.Host))
errors.Add("Требуется Host");
if (opts.Port is < 1 or > 65535)
errors.Add($"Port должен быть от 1 до 65535");
if (string.IsNullOrWhiteSpace(opts.FromAddress))
errors.Add("Требуется FromAddress");
return errors.Count > 0
? ValidateOptionsResult.Fail(errors)
: ValidateOptionsResult.Success;
}
}
Регистрируем в Program.cs:
builder.Services
.Configure<SmtpOptions>(
builder.Configuration.GetSection("Smtp"))
.AddSingleton<IValidateOptions<SmtpOptions>, SmtpOptionsValidator>()
.AddOptions<SmtpOptions>()
.ValidateOnStart();
Если проверка не проходит, во время выполнения
app.Run() генерируется исключение OptionsValidationException, поэтому сервис никогда не запустится с некорректной конфигурацией. Этот вариант «быстрого сбоя» обычно предпочтительнее, чем исключение NullReferenceException во время выполнения, возникающее где-то в глубинах бизнес-логики.Окончание следует…
Источник: https://bartwullems.blogspot.com/2026/03/validating-configuration-at-startup.html
👍24
День 2631. #ЗаметкиНаПолях
Проверяем Конфигурацию при Запуске. Окончание
Начало
Доступ к другим сервисам внутри валидатора
Поскольку валидатор является обычным классом, зарегистрированным в DI, вы можете внедрять зависимости. Это одно из ключевых преимуществ по сравнению с аннотациями данных. Кроме того, аннотации данных не могут выражать зависимости между свойствами. В валидаторе вы можете добавить любую логику.
Именованные конфигурации
IValidateOptions<T> поддерживает именованные конфигурации через параметр name. Это важно при регистрации нескольких экземпляров одного типа конфигурации — например, двух разных клиентов API:
Регистрация именованных настроек:
Использование в сочетании с OptionsBuilder<T>
API OptionsBuilder<T> (возвращаемый функцией AddOptions<T>()) имеет перегрузку метода .Validate(), которая принимает делегат. Это подходит для простых случаев. Для чего-либо более сложного предпочтительнее использовать реализацию IValidateOptions<T> — она позволяет тестировать логику проверки и не размещать её в Program.cs:
Вы можете объединить оба варианта в одном типе параметров — фреймворк запускает все зарегистрированные валидаторы и агрегирует ошибки.
Источник: https://bartwullems.blogspot.com/2026/03/validating-configuration-at-startup.html
Проверяем Конфигурацию при Запуске. Окончание
Начало
Доступ к другим сервисам внутри валидатора
Поскольку валидатор является обычным классом, зарегистрированным в DI, вы можете внедрять зависимости. Это одно из ключевых преимуществ по сравнению с аннотациями данных. Кроме того, аннотации данных не могут выражать зависимости между свойствами. В валидаторе вы можете добавить любую логику.
public class DatabaseOptionsValidator
: IValidateOptions<DatabaseOptions>
{
private readonly IHostEnvironment _env;
public DatabaseOptionsValidator(IHostEnvironment env)
=> _env = env;
public ValidateOptionsResult Validate(
string? name, DatabaseOptions opts)
{
var errors = new List<string>();
// Более строгие правила вне среды разработки
if (!_env.IsDevelopment())
{
…
}
…
}
}
Именованные конфигурации
IValidateOptions<T> поддерживает именованные конфигурации через параметр name. Это важно при регистрации нескольких экземпляров одного типа конфигурации — например, двух разных клиентов API:
public class ApiClientOptionsValidator
: IValidateOptions<ApiClientOptions>
{
public ValidateOptionsResult Validate(
string? name, ApiClientOptions opts)
{
…
// … Валидация общих свойств …
// Более строгие правила для API Payments
if (name == "Payments"
&& opts.Timeout > 30)
errors.Add($"Таймаут клиента [{name}] не должен превышать 30 сек.");
…
}
}
Регистрация именованных настроек:
builder.Services
.AddSingleton<IValidateOptions<ApiClientOptions>, ApiClientOptionsValidator>();
builder.Services
.AddOptions<ApiClientOptions>("Payments")
.BindConfiguration("ApiClients:Payments")
.ValidateOnStart();
builder.Services
.AddOptions<ApiClientOptions>("Shipping")
.BindConfiguration("ApiClients:Shipping")
.ValidateOnStart();
Использование в сочетании с OptionsBuilder<T>
API OptionsBuilder<T> (возвращаемый функцией AddOptions<T>()) имеет перегрузку метода .Validate(), которая принимает делегат. Это подходит для простых случаев. Для чего-либо более сложного предпочтительнее использовать реализацию IValidateOptions<T> — она позволяет тестировать логику проверки и не размещать её в Program.cs:
// Делегат — для простых проверок
builder.Services
.AddOptions<FeatureFlagOptions>()
.BindConfiguration("FeatureFlags")
.Validate(opts => opts.Percentage is >= 0 and <= 100,
"Percentage должен быть от 0 до 100")
.ValidateOnStart();
// Класс – для сложных проверок
builder.Services
.AddSingleton<IValidateOptions<SmtpOptions>, SmtpOptionsValidator>()
.AddOptions<SmtpOptions>()
.BindConfiguration("Smtp")
.ValidateOnStart();
Вы можете объединить оба варианта в одном типе параметров — фреймворк запускает все зарегистрированные валидаторы и агрегирует ошибки.
Источник: https://bartwullems.blogspot.com/2026/03/validating-configuration-at-startup.html
👍9
День 2632. #МоиИнструменты #PG
Инструменты Оптимизации Запросов в PostgreSQL. Часть 6
6. SQLFluff (SQL-линтер для производительности)
Что даёт: выявляет антипаттерны производительности до того, как запросы попадут в прод.
Зачем нужен: Большинство проблем с производительностью SQL возникают из-за распространённых ошибок: SELECT *, ненужные DISTINCT, отсутствие WHERE, неэффективные соединения. SQLFluff выявляет их на этапе разработки, до того, как они вызовут проблемы в продакшене.
Установка:
Использование
1. Линтинг SQL-файлов:
Пример вывода:
2. Автоматические исправления:
До SQLFluff:
Проблемы:
1. SELECT * (возвращает ненужные поля);
2. Неявное объединение (неясное намерение);
3. Сравнение даты без явного приведения типа.
После SQLFluff:
Когда использовать
- dbt-проекты (отлично интегрируется);
- Несколько инженеров, пишущих SQL-запросы (обеспечение согласованности);
- Предотвращение распространённых ошибок;
- Конвейер CI/CD для SQL.
Когда отказаться
- Разработчик-одиночка (меньшая ценность);
- ad-hoc запросы (избыточно);
- Нестандартный диалект SQL (ограниченная поддержка).
Скрытая функция
Пользовательские правила для антипаттернов в вашей команде:
Добавить в .sqlfluff.
С осторожностью
Слишком агрессивный линтинг убивает продуктивность:
Постепенно добавляйте правила со временем. Соблюдайте баланс между отловом проблем и сопротивлением разработчиков.
Источник: https://medium.com/@reliabledataengineering/15-sql-optimization-tools-that-make-queries-10x-faster-8629ac451d97
Инструменты Оптимизации Запросов в PostgreSQL. Часть 6
6. SQLFluff (SQL-линтер для производительности)
Что даёт: выявляет антипаттерны производительности до того, как запросы попадут в прод.
Зачем нужен: Большинство проблем с производительностью SQL возникают из-за распространённых ошибок: SELECT *, ненужные DISTINCT, отсутствие WHERE, неэффективные соединения. SQLFluff выявляет их на этапе разработки, до того, как они вызовут проблемы в продакшене.
Установка:
pip install sqlfluff
# Создаём конфиг
cat > .sqlfluff <<EOF
[sqlfluff]
dialect = postgres
templater = dbt
[sqlfluff:rules:L042]
# Запрет SELECT * в проде
select_
_targets = qualified
[sqlfluff:rules:L045]
# Обязательные алиасы полей
aliasing = explicit
[sqlfluff:rules:L052]
# Требование явных объединений
join_types = explicit
EOF
Использование
1. Линтинг SQL-файлов:
sqlfluff lint models/*.sql
Пример вывода:
== [models/orders.sql] FAIL
L: 5 | P: 8 | L042 | SELECT * is not allowed in production.
L: 12 | P: 15 | L031 | Avoid DISTINCT where possible (use GROUP BY).
L: 18 | P: 1 | L044 | Query is missing WHERE clause (full table scan).
L: 23 | P: 22 | L052 | JOIN should specify type (INNER, LEFT, etc.).
2. Автоматические исправления:
sqlfluff fix models/*.sql
До SQLFluff:
SELECT * FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE order_date > '2024-01-01';
Проблемы:
1. SELECT * (возвращает ненужные поля);
2. Неявное объединение (неясное намерение);
3. Сравнение даты без явного приведения типа.
После SQLFluff:
SELECT
o.order_id,
o.order_date,
o.total,
c.customer_name
FROM orders AS o
INNER JOIN customers AS c
ON o.customer_id = c.id
WHERE o.order_date > CAST('2024-01-01' AS DATE);
Когда использовать
- dbt-проекты (отлично интегрируется);
- Несколько инженеров, пишущих SQL-запросы (обеспечение согласованности);
- Предотвращение распространённых ошибок;
- Конвейер CI/CD для SQL.
Когда отказаться
- Разработчик-одиночка (меньшая ценность);
- ad-hoc запросы (избыточно);
- Нестандартный диалект SQL (ограниченная поддержка).
Скрытая функция
Пользовательские правила для антипаттернов в вашей команде:
# custom_rules/no_cross_joins.py
from sqlfluff.core.rules import BaseRule, LintResult
class Rule_Custom_NoCrossJoin(BaseRule):
"""Запрет CROSS JOIN (почти всегда необязательны)."""
def _eval(self, segment, **kwargs):
if segment.is_type("join_clause"):
if "CROSS" in segment.raw_upper:
return LintResult(
anchor=segment,
description="Найден CROSS JOIN. Почти всегда необязателен.",
fixes=[...] # Опционально: предложение исправления
)
Добавить в .sqlfluff.
С осторожностью
Слишком агрессивный линтинг убивает продуктивность:
# Слишком строго (инженеры возненавидят)
sqlfluff lint --rules all
# Лучше начать с критических правил
sqlfluff lint --rules L042,L044,L052
Постепенно добавляйте правила со временем. Соблюдайте баланс между отловом проблем и сопротивлением разработчиков.
Источник: https://medium.com/@reliabledataengineering/15-sql-optimization-tools-that-make-queries-10x-faster-8629ac451d97
👍3
День 2633. #TipsAndTricks
Вызываем Файлы из Репозитория Действий в GitHub Actions
GitHub Actions позволяет создавать многократно используемые действия. Один из самых простых способов сделать это — использовать составные действия (Composite Actions), которые позволяют объединять несколько шагов в одно действие. Это уменьшает дублирование кода в ваших рабочих процессах.
При создании составного действия вам может потребоваться сослаться на файлы, хранящиеся в репозитории действий, например, на скрипт PowerShell или Bash.
По умолчанию, когда выполняется действие, рабочий каталог — это корень репозитория, использующего действие. Это означает, что относительные пути разрешаются в репозиторий вызывающего объекта, а не в репозиторий действия.
Для доступа к файлам из репозитория действий используйте контекстную переменную github.action_path, которая содержит путь к каталогу, где находится ваше действие.
Вот пример того, как использовать её в файле
В этом примере файл
Дополнительные ресурсы:
- Контексты GitHub - github.action_path
- Создание составного действия
Источник: https://www.meziantou.net/accessing-files-from-the-action-repository-in-a-github-composite-action.htm
Вызываем Файлы из Репозитория Действий в GitHub Actions
GitHub Actions позволяет создавать многократно используемые действия. Один из самых простых способов сделать это — использовать составные действия (Composite Actions), которые позволяют объединять несколько шагов в одно действие. Это уменьшает дублирование кода в ваших рабочих процессах.
При создании составного действия вам может потребоваться сослаться на файлы, хранящиеся в репозитории действий, например, на скрипт PowerShell или Bash.
По умолчанию, когда выполняется действие, рабочий каталог — это корень репозитория, использующего действие. Это означает, что относительные пути разрешаются в репозиторий вызывающего объекта, а не в репозиторий действия.
Для доступа к файлам из репозитория действий используйте контекстную переменную github.action_path, которая содержит путь к каталогу, где находится ваше действие.
Вот пример того, как использовать её в файле
action.yml:name: 'My Composite Action'
description: 'An example'
runs:
using: "composite"
steps:
- name: Run script
run: ${{ github.action_path }}/script.sh
shell: bash
В этом примере файл
script.sh находится в корневом каталоге репозитория действий. Использование ${{ github.action_path }}/script.sh гарантирует, что исполнитель выполнит скрипт из правильного места.Дополнительные ресурсы:
- Контексты GitHub - github.action_path
- Создание составного действия
Источник: https://www.meziantou.net/accessing-files-from-the-action-repository-in-a-github-composite-action.htm
👍1
День 2634. #TipsAndTricks #Git
Команды Git, Которые Я Запускаю Перед Чтением Кода
Автор оригинала: Ally Piechowski
Я занимаюсь аудитом кодовых баз моих клиентов. Первое, что я обычно делаю, когда берусь за новую кодовую базу, открываю терминал и запускаю несколько команд Git. Я хочу понять, кто основные авторы, где сосредоточены проблемы, уверенно ли команда выпускает продукт или ходит по минному полю.
1. Что меняется чаще всего
20 самых часто изменяемых файлов за последний год. Файл, находящийся вверху списка, почти всегда тот, о котором говорят: «К нему все боятся прикасаться».
Высокая активность в файле - иногда это просто активная разработка. Но если авторов много — это явный признак проблемы. Это файл, где каждое изменение — это исправление исправления. Я беру 5 файлов из этого списка и сопоставляю их с командой поиска ошибок (см. п. 3). Файл с высокой активностью и большим количеством ошибок — это основной источник риска.
2. Кто авторы
Авторы по количеству коммитов. Если на одного человека приходится 60%+, это ваш «автобусный фактор». Если он ушёл полгода назад, это кризис. Если ведущий автор из списка не появляется в течение 6 месяцев (
Я также смотрю на хвост. 30 авторов, но только 3 были активны в течение последнего года. Т.е. люди, которые создали эту систему, не те, кто её поддерживает.
Замечание: рабочие процессы объединения коммитов сжимают информацию об авторстве. Если команда объединяет каждый пул-реквест в один коммит, этот результат отражает, кто объединил, а не кто написал. Стоит уточнить стратегию объединения, прежде чем делать выводы.
3. Места ошибок
Та же структура, что и у 1й команды, но отфильтрована по коммитам с ключевыми словами, связанными с ошибками. Файлы, которые появляются в обоих списках, — это код с самым высоким риском: они постоянно ломаются и постоянно исправляются, но никогда не исправляются должным образом.
Это зависит от дисциплины в сообщениях коммитов. Если команда пишет случайные сообщения коммитов, вы ничего не получите. Но даже приблизительная карта плотности ошибок лучше, чем её отсутствие.
4. Проект ускоряется или умирает?
Количество коммитов по месяцам. Стабильный ритм — это хорошо. Но что, если количество коммитов падает в 2 раза за месяц? Обычно - кто-то ушёл. Снижение в течение 6-12 месяцев говорит о том, что команда теряет темп. Периодические всплески, за которыми следуют спокойные месяцы, означают, что команда работает над релизами партиями, а не выпускает их непрерывно.
5. Как часто «тушат пожары»?
Частота откатов и хотфиксов. Несколько откатов за год нормально. Каждые пару недель - команда не доверяет своему процессу развёртывания. Это свидетельство более глубокой проблемы: ненадёжные тесты, отсутствие приёмочных тестов или конвейер развертывания, который усложняет откаты. Отсутствие результатов – сигнал, что либо команда стабильна, либо никто не пишет подробные сообщения о коммитах.
Итого
Выполнение этих команд займет пару минут. Они не покажут вам всего. Но вы будете знать, какой код читать в первую очередь и что искать, когда дойдёте до него. В этом разница между методичным изучением кодовой базы с первого дня и бесцельным блужданием по коду.
Источник: https://piechowski.io/post/git-commands-before-reading-code/
Команды Git, Которые Я Запускаю Перед Чтением Кода
Автор оригинала: Ally Piechowski
Я занимаюсь аудитом кодовых баз моих клиентов. Первое, что я обычно делаю, когда берусь за новую кодовую базу, открываю терминал и запускаю несколько команд Git. Я хочу понять, кто основные авторы, где сосредоточены проблемы, уверенно ли команда выпускает продукт или ходит по минному полю.
1. Что меняется чаще всего
git log --format=format: --name-only --since="1 year ago" | sort | uniq -c | sort -nr | head -20
20 самых часто изменяемых файлов за последний год. Файл, находящийся вверху списка, почти всегда тот, о котором говорят: «К нему все боятся прикасаться».
Высокая активность в файле - иногда это просто активная разработка. Но если авторов много — это явный признак проблемы. Это файл, где каждое изменение — это исправление исправления. Я беру 5 файлов из этого списка и сопоставляю их с командой поиска ошибок (см. п. 3). Файл с высокой активностью и большим количеством ошибок — это основной источник риска.
2. Кто авторы
git shortlog -sn --no-merges
Авторы по количеству коммитов. Если на одного человека приходится 60%+, это ваш «автобусный фактор». Если он ушёл полгода назад, это кризис. Если ведущий автор из списка не появляется в течение 6 месяцев (
git shortlog -sn --no-merges --since="6 months ago"), я немедленно сообщаю об этом клиенту.Я также смотрю на хвост. 30 авторов, но только 3 были активны в течение последнего года. Т.е. люди, которые создали эту систему, не те, кто её поддерживает.
Замечание: рабочие процессы объединения коммитов сжимают информацию об авторстве. Если команда объединяет каждый пул-реквест в один коммит, этот результат отражает, кто объединил, а не кто написал. Стоит уточнить стратегию объединения, прежде чем делать выводы.
3. Места ошибок
git log -i -E --grep="fix|bug|broken" --name-only --format='' | sort | uniq -c | sort -nr | head -20
Та же структура, что и у 1й команды, но отфильтрована по коммитам с ключевыми словами, связанными с ошибками. Файлы, которые появляются в обоих списках, — это код с самым высоким риском: они постоянно ломаются и постоянно исправляются, но никогда не исправляются должным образом.
Это зависит от дисциплины в сообщениях коммитов. Если команда пишет случайные сообщения коммитов, вы ничего не получите. Но даже приблизительная карта плотности ошибок лучше, чем её отсутствие.
4. Проект ускоряется или умирает?
git log --format='%ad' --date=format:'%Y-%m' | sort | uniq -c
Количество коммитов по месяцам. Стабильный ритм — это хорошо. Но что, если количество коммитов падает в 2 раза за месяц? Обычно - кто-то ушёл. Снижение в течение 6-12 месяцев говорит о том, что команда теряет темп. Периодические всплески, за которыми следуют спокойные месяцы, означают, что команда работает над релизами партиями, а не выпускает их непрерывно.
5. Как часто «тушат пожары»?
git log --oneline --since="1 year ago" | grep -iE 'revert|hotfix|emergency|rollback'
Частота откатов и хотфиксов. Несколько откатов за год нормально. Каждые пару недель - команда не доверяет своему процессу развёртывания. Это свидетельство более глубокой проблемы: ненадёжные тесты, отсутствие приёмочных тестов или конвейер развертывания, который усложняет откаты. Отсутствие результатов – сигнал, что либо команда стабильна, либо никто не пишет подробные сообщения о коммитах.
Итого
Выполнение этих команд займет пару минут. Они не покажут вам всего. Но вы будете знать, какой код читать в первую очередь и что искать, когда дойдёте до него. В этом разница между методичным изучением кодовой базы с первого дня и бесцельным блужданием по коду.
Источник: https://piechowski.io/post/git-commands-before-reading-code/
👍30
День 2635. #Карьера
Приём Повышения Продуктивности, Который Используют Сеньоры. Начало
В начале своей карьеры я думал, что продуктивность означает работать усерднее всех остальных. Больше часов. Больше кода. Больше закрытых задач. Если я выпускал пять новых функций за неделю, я чувствовал себя продуктивным. Но происходило что-то странное. Самые уважаемые инженеры в моей команде — те, к кому все обращались при инцидентах — казались совсем не занятыми. Они не писали код постоянно. Они не спешили закрывать задачи. Иногда они даже уходили раньше остальных. И всё же каким-то образом они всегда были самыми эффективными. Мне потребовались годы, чтобы понять, какой приём они используют.
Иллюзия продуктивности
Разработка ПО создаёт опасную иллюзию: активность выглядит как прогресс. Вы пишете код часами, проводите рефакторинг модулей, проверяете пул-реквесты, посещаете ежедневные совещания, планирования спринтов, ретроспективы, архитектурные совещания. В конце дня вы чувствуете себя измотанным. Но измотанность — это не продуктивность.
Опытные инженеры понимают то, чему большинство разработчиков учатся на собственном горьком опыте: самая сложная часть инженерной работы — это не написание кода. Это решение о том, чего делать не следует.
Секрет повышения продуктивности
Опытные инженеры тратят больше времени на размышления, чем на написание кода.
Не потому, что они ленивы. Потому что они знают, что один час размышлений может сэкономить недели работы. Когда возникает новая проблема, джуны часто сразу переходят к реализации. Сеньоры делают паузу. Они задают вопросы:
- Действительно ли это проблема?
- Есть ли более простое решение?
- Можем ли мы устранить проблему, вместо того чтобы её решать?
Именно в этой паузе кроется настоящая продуктивность.
Почему слишком быстрое написание кода опасно
Многие руководства по повышению продуктивности призывают разработчиков работать быстро. Но опытные инженеры знают, что скорость может нанести долгосрочный ущерб. Каждая строка кода вносит:
- поддержку в будущем,
- потенциальные ошибки,
- увеличение сложности,
- увеличение времени на отладку.
Опытные инженеры относятся к коду как к обузе, а не как к достижению.
Прежде чем что-либо писать, они задают себе простой вопрос: «Действительно ли нам нужен этот код?» Иногда ответ отрицательный. И когда ответ отрицательный, прирост продуктивности огромен.
Сила делать меньше
Одна из самых удивительных особенностей опытных инженеров — это то, как часто они удаляют код вместо того, чтобы писать его. Они упрощают системы, удаляют ненужные абстракции. Они устраняют сложность до того, как она распространится. Джун может реализовать остроумное решение. Опытный инженер может перепроектировать систему так, чтобы проблема полностью исчезла. Именно поэтому их работа на первый взгляд кажется медленнее, но со временем даёт гораздо лучшие результаты.
Окончание следует…
Источник: https://medium.com/@optimzationking2/the-productivity-trick-that-senior-engineers-use-but-never-talk-about-5549af0078f9
Приём Повышения Продуктивности, Который Используют Сеньоры. Начало
В начале своей карьеры я думал, что продуктивность означает работать усерднее всех остальных. Больше часов. Больше кода. Больше закрытых задач. Если я выпускал пять новых функций за неделю, я чувствовал себя продуктивным. Но происходило что-то странное. Самые уважаемые инженеры в моей команде — те, к кому все обращались при инцидентах — казались совсем не занятыми. Они не писали код постоянно. Они не спешили закрывать задачи. Иногда они даже уходили раньше остальных. И всё же каким-то образом они всегда были самыми эффективными. Мне потребовались годы, чтобы понять, какой приём они используют.
Иллюзия продуктивности
Разработка ПО создаёт опасную иллюзию: активность выглядит как прогресс. Вы пишете код часами, проводите рефакторинг модулей, проверяете пул-реквесты, посещаете ежедневные совещания, планирования спринтов, ретроспективы, архитектурные совещания. В конце дня вы чувствуете себя измотанным. Но измотанность — это не продуктивность.
Опытные инженеры понимают то, чему большинство разработчиков учатся на собственном горьком опыте: самая сложная часть инженерной работы — это не написание кода. Это решение о том, чего делать не следует.
Секрет повышения продуктивности
Опытные инженеры тратят больше времени на размышления, чем на написание кода.
Не потому, что они ленивы. Потому что они знают, что один час размышлений может сэкономить недели работы. Когда возникает новая проблема, джуны часто сразу переходят к реализации. Сеньоры делают паузу. Они задают вопросы:
- Действительно ли это проблема?
- Есть ли более простое решение?
- Можем ли мы устранить проблему, вместо того чтобы её решать?
Именно в этой паузе кроется настоящая продуктивность.
Почему слишком быстрое написание кода опасно
Многие руководства по повышению продуктивности призывают разработчиков работать быстро. Но опытные инженеры знают, что скорость может нанести долгосрочный ущерб. Каждая строка кода вносит:
- поддержку в будущем,
- потенциальные ошибки,
- увеличение сложности,
- увеличение времени на отладку.
Опытные инженеры относятся к коду как к обузе, а не как к достижению.
Прежде чем что-либо писать, они задают себе простой вопрос: «Действительно ли нам нужен этот код?» Иногда ответ отрицательный. И когда ответ отрицательный, прирост продуктивности огромен.
Сила делать меньше
Одна из самых удивительных особенностей опытных инженеров — это то, как часто они удаляют код вместо того, чтобы писать его. Они упрощают системы, удаляют ненужные абстракции. Они устраняют сложность до того, как она распространится. Джун может реализовать остроумное решение. Опытный инженер может перепроектировать систему так, чтобы проблема полностью исчезла. Именно поэтому их работа на первый взгляд кажется медленнее, но со временем даёт гораздо лучшие результаты.
Окончание следует…
Источник: https://medium.com/@optimzationking2/the-productivity-trick-that-senior-engineers-use-but-never-talk-about-5549af0078f9
👍31
День 2636. #Карьера
Приём Повышения Продуктивности, Который Используют Сеньоры. Окончание
Начало
Глубокая работа важнее постоянной активности
Ещё одна привычка, повышающая продуктивность — глубокая работа. Сеньоры избегают постоянных отвлечений. Они выделяют время для непрерывного обдумывания. Они знают, что сложные проблемы требуют длительных периодов сосредоточенности. Во многих командах продуктивность разрушается из-за:
- бесконечных сообщений в Slack,
- плотного графика совещаний,
- постоянного переключения контекста.
Старшие инженеры учатся защищать своё внимание. Не потому, что им не нравится сотрудничество. Потому что сложные инженерные проблемы требуют непрерывного обдумывания.
Совещания - тихий убийца продуктивности
Если вы внимательно посмотрите, как опытные инженеры управляют своим расписанием, вы заметите, что они крайне избирательны в отношении совещаний. Они посещают те, которые важны, и пропускают менее важные. Каждый час, проведённый на совещании, — это час, не потраченный на:
- проектирование систем,
- отладку проблем в проде,
- улучшение архитектуры,
- обдумывание компромиссов.
Джуны часто чувствуют давление, заставляющее их посещать все совещания. Сеньоры знают, что защита своего времени защищает качество их работы.
Реальный показатель производительности
Большинство команд измеряют производительность, используя такие показатели, как:
- выполненные задачи,
- стори-пойнты,
- смерженные пул-реквесты,
- коммиты в неделю.
Но эти показатели редко отражают реальное влияние. Старшие инженеры фокусируются на других результатах. Они задают такие вопросы, как:
- Сделало ли это изменение систему проще?
- Устранили ли мы технический долг?
- Предотвратили ли мы будущие инциденты?
- Улучшили ли мы надёжность?
Они измеряют производительность по долгосрочному состоянию системы, а не по краткосрочным результатам.
Почему об этом приёме редко говорят
Этот приём повышения продуктивности незаметен. Размышления не отражаются в метриках. Упрощение системы не выглядит впечатляюще на панели мониторинга. Удаление кода редко приветствуется. Но за месяцы и годы такой образ мышления создаёт огромные различия между инженерами. Самые продуктивные разработчики — это не те, кто печатает быстрее всех. Это те, кто принимает лучшие решения ещё до того, как что-либо напечатает.
Как это можно применить уже сегодня
Если вы хотите выработать эту привычку, начните с трёх небольших изменений.
1. Делайте паузу перед написанием кода
Потратьте время на глубокое понимание проблемы. Часто первое решение не является лучшим.
2. Сократите ненужную работу
Ищите возможности для упрощения, а не для добавления новых функций.
3. Защищайте свою концентрацию
Выделите время для глубокого размышления. Сложные инженерные задачи редко решаются за 5 минут.
Итого
Продуктивность в разработке ПО заключается не в том, чтобы работать усерднее. Она заключается в качестве работы. Лучшие инженеры не пишут больше кода. Они ясно мыслят, упрощая системы и решая правильные проблемы. И как только вы начнёте работать таким образом, вы сможете писать меньше кода, чем раньше. Но этот код будет иметь гораздо большее значение.
Источник: https://medium.com/@optimzationking2/the-productivity-trick-that-senior-engineers-use-but-never-talk-about-5549af0078f9
Приём Повышения Продуктивности, Который Используют Сеньоры. Окончание
Начало
Глубокая работа важнее постоянной активности
Ещё одна привычка, повышающая продуктивность — глубокая работа. Сеньоры избегают постоянных отвлечений. Они выделяют время для непрерывного обдумывания. Они знают, что сложные проблемы требуют длительных периодов сосредоточенности. Во многих командах продуктивность разрушается из-за:
- бесконечных сообщений в Slack,
- плотного графика совещаний,
- постоянного переключения контекста.
Старшие инженеры учатся защищать своё внимание. Не потому, что им не нравится сотрудничество. Потому что сложные инженерные проблемы требуют непрерывного обдумывания.
Совещания - тихий убийца продуктивности
Если вы внимательно посмотрите, как опытные инженеры управляют своим расписанием, вы заметите, что они крайне избирательны в отношении совещаний. Они посещают те, которые важны, и пропускают менее важные. Каждый час, проведённый на совещании, — это час, не потраченный на:
- проектирование систем,
- отладку проблем в проде,
- улучшение архитектуры,
- обдумывание компромиссов.
Джуны часто чувствуют давление, заставляющее их посещать все совещания. Сеньоры знают, что защита своего времени защищает качество их работы.
Реальный показатель производительности
Большинство команд измеряют производительность, используя такие показатели, как:
- выполненные задачи,
- стори-пойнты,
- смерженные пул-реквесты,
- коммиты в неделю.
Но эти показатели редко отражают реальное влияние. Старшие инженеры фокусируются на других результатах. Они задают такие вопросы, как:
- Сделало ли это изменение систему проще?
- Устранили ли мы технический долг?
- Предотвратили ли мы будущие инциденты?
- Улучшили ли мы надёжность?
Они измеряют производительность по долгосрочному состоянию системы, а не по краткосрочным результатам.
Почему об этом приёме редко говорят
Этот приём повышения продуктивности незаметен. Размышления не отражаются в метриках. Упрощение системы не выглядит впечатляюще на панели мониторинга. Удаление кода редко приветствуется. Но за месяцы и годы такой образ мышления создаёт огромные различия между инженерами. Самые продуктивные разработчики — это не те, кто печатает быстрее всех. Это те, кто принимает лучшие решения ещё до того, как что-либо напечатает.
Как это можно применить уже сегодня
Если вы хотите выработать эту привычку, начните с трёх небольших изменений.
1. Делайте паузу перед написанием кода
Потратьте время на глубокое понимание проблемы. Часто первое решение не является лучшим.
2. Сократите ненужную работу
Ищите возможности для упрощения, а не для добавления новых функций.
3. Защищайте свою концентрацию
Выделите время для глубокого размышления. Сложные инженерные задачи редко решаются за 5 минут.
Итого
Продуктивность в разработке ПО заключается не в том, чтобы работать усерднее. Она заключается в качестве работы. Лучшие инженеры не пишут больше кода. Они ясно мыслят, упрощая системы и решая правильные проблемы. И как только вы начнёте работать таким образом, вы сможете писать меньше кода, чем раньше. Но этот код будет иметь гораздо большее значение.
Источник: https://medium.com/@optimzationking2/the-productivity-trick-that-senior-engineers-use-but-never-talk-about-5549af0078f9
👍11
День 2637. #TipsAndTricks
Программно Подавляем Предупреждения Анализатора Roslyn
Анализаторы Roslyn отлично подходят для обеспечения соблюдения стандартов кодирования и поиска ошибок. Однако иногда они могут ложно срабатывать или выдавать предупреждения, которые вы хотите игнорировать в определённых контекстах. Вы можете подавить эти предупреждения с помощью директив
DiagnosticSuppressor — компонент Roslyn, который подавляет диагностические сообщения, сообщаемые другими анализаторами или компилятором. В отличие от обычного анализатора, он выдаёт новых диагностических сообщений, а проверяет существующие и решает, следует ли их подавить. Это особенно полезно, когда вам нужно контекстно-зависимое подавление без загромождения кода.
Для реализации DiagnosticSuppressor необходимо:
1. Создать класс, наследующий от DiagnosticSuppressor;
2. Декорировать класс с помощью
3. Определить SuppressionDescriptor для каждого правила, которое вы хотите подавить;
4. Переопределить SupportedSuppressions, чтобы возвращать поддерживаемые дескрипторы;
5. Переопределить ReportSuppressions, чтобы реализовать логику подавления.
Пример
Допустим, вы используете Newtonsoft.Json для сериализации классов. Правило анализатора CA1507 предлагает заменить строковые литералы выражениями nameof. Однако, когда вы используете атрибут
Вот как можно реализовать подавитель для этого сценария:
Источник: https://www.meziantou.net/suppressing-roslyn-analyzer-warnings-programmatically-using-diagnosticsuppressor.htm
Программно Подавляем Предупреждения Анализатора Roslyn
Анализаторы Roslyn отлично подходят для обеспечения соблюдения стандартов кодирования и поиска ошибок. Однако иногда они могут ложно срабатывать или выдавать предупреждения, которые вы хотите игнорировать в определённых контекстах. Вы можете подавить эти предупреждения с помощью директив
#pragma или атрибутов [SuppressMessage], но оба подхода требуют изменения исходного кода.DiagnosticSuppressor — компонент Roslyn, который подавляет диагностические сообщения, сообщаемые другими анализаторами или компилятором. В отличие от обычного анализатора, он выдаёт новых диагностических сообщений, а проверяет существующие и решает, следует ли их подавить. Это особенно полезно, когда вам нужно контекстно-зависимое подавление без загромождения кода.
Для реализации DiagnosticSuppressor необходимо:
1. Создать класс, наследующий от DiagnosticSuppressor;
2. Декорировать класс с помощью
[DiagnosticAnalyzer(LanguageNames.CSharp)];3. Определить SuppressionDescriptor для каждого правила, которое вы хотите подавить;
4. Переопределить SupportedSuppressions, чтобы возвращать поддерживаемые дескрипторы;
5. Переопределить ReportSuppressions, чтобы реализовать логику подавления.
Пример
Допустим, вы используете Newtonsoft.Json для сериализации классов. Правило анализатора CA1507 предлагает заменить строковые литералы выражениями nameof. Однако, когда вы используете атрибут
[JsonProperty("propertyName")], анализатор помечает строковый литерал как нарушение CA1507. Но в этом сценарии вам может быть удобнее использовать строку, т.к. это имя свойства JSON является частью контракта API и должно оставаться неизменным, даже если вы переименуете свойство C#.Вот как можно реализовать подавитель для этого сценария:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Immutable;
namespace Analyzer.Suppressors;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class CA1507Suppressor : DiagnosticSuppressor
{
// Определяем правило подавления
private static readonly
SuppressionDescriptor RuleJsonProperty = new(
id: "EXAMPLE0001", // ID «подавителя»
suppressedDiagnosticId: "CA1507", // ID правила
justification: "Suppress CA1507 on methods decorated with a [Newtonsoft.Json.JsonPropertyAttribute]."
);
// Регистрируем подавление
public override ImmutableArray<SuppressionDescriptor>
SupportedSuppressions => [RuleJsonProperty];
public override void ReportSuppressions(
SuppressionAnalysisContext ctx)
{
// Проверяем правила диагностики
foreach (var diag in ctx.ReportedDiagnostics)
{
// Получаем локацию диагностики и синтаксическое дерево
var location = diag.Location;
var tree = location?.SourceTree;
if (tree is null)
continue;
// Находим ноду в дереве
var root = tree.GetRoot(ctx.CancellationToken);
var node = root.FindNode(location!.SourceSpan);
if (node is null)
continue;
var parent = node.FirstAncestorOrSelf<AttributeSyntax>();
if (parent is null)
continue;
var semanticModel = ctx.GetSemanticModel(tree);
var info = semanticModel.GetSymbolInfo(parent, ctx.CancellationToken);
if (info.Symbol is not IMethodSymbol mtdSymb)
continue;
// Проверяем, что тип атрибута Newtonsoft.Json.JsonPropertyAttribute
var attrType = ctx.Compilation.GetTypeByMetadataName(
"Newtonsoft.Json.JsonPropertyAttribute");
if (attrType is null)
continue;
if (mtdSymb.ContainingType.Equals(attrType, SymbolEqualityComparer.Default))
{
// Подавляем
var suppr = Suppression.Create(RuleJsonProperty, diag);
ctx.ReportSuppression(suppr);
}
}
}
}
Источник: https://www.meziantou.net/suppressing-roslyn-analyzer-warnings-programmatically-using-diagnosticsuppressor.htm
👍5
День 2638. #МоиИнструменты #PG
Инструменты Оптимизации Запросов в PostgreSQL. Часть 7
7. pghero (Панель Управления Производительностью Postgres)
Что даёт: простой мониторинг производительности PostgreSQL для команд без выделенных администраторов баз данных.
Язык: Ruby.
Поддержка: очень активная.
Зачем нужен
Большинство инструментов мониторинга сложны и дороги. pghero предоставляет 80% функциональности в простом веб-интерфейсе: медленные запросы, отсутствующие индексы, раздувание таблиц, проблемы с подключением — всё на одной панели мониторинга.
Установка
1. Docker (самая простая)
2. Для Rails
За чем можно наблюдать
1. Медленные запросы (последние 24 часа):
SELECT … FROM orders JOIN … (avg: 45s, 234 calls)
→ Missing index on orders(customer_id, order_date)
UPDATE inventory … (avg: 12s, 5,432 calls)
→ Long-running, blocking other queries
2. Отсутствующие индексы:
- orders.customer_id (queries: 1,234/day, est. speedup: 100x)
- customers.email (queries: 456/day, est. speedup: 50x)
3. Раздувание таблиц:
- orders: 35% bloat (recommend VACUUM)
- customer_events: 62% bloat (recommend VACUUM FULL)
4. Проблемы с соединением:
- Max connections: 100
- Current: 87 (87% utilization) ⚠️
- Idle in transaction: 12 (potential locks)
5. Использование индексов:
- idx_orders_date: 2.3M scans/day ✅
- idx_orders_legacy: 0 scans/day (unused, consider dropping)
Когда использовать
- PostgreSQL в продакшене;
- нет выделенного администратора баз данных;
- нужен простой мониторинг;
- нужны действенные рекомендации (а не просто метрики).
Когда отказаться
- необходим мониторинг разных БД (pghero работает только с PostgreSQL);
- необходим подробный исторический анализ (pghero отображает текущее состояние + 24 часа);
- уже есть комплексный мониторинг (Datadog и т. д.).
Скрытая функция
Визуализация плана запроса. В pghero щёлкните на любой медленный запрос. Отобразится:
1. Текст запроса;
2. План выполнения (визуальный);
3. Рекомендуемые индексы;
4. Предложение по изменению запроса;
5. Создание индекса одним щелчком (генерирует DDL).
С осторожностью
Запросы pghero могут быть дорогими на больших базах данных. Настройка, чтобы снизить нагрузку:
Источник: https://medium.com/@reliabledataengineering/15-sql-optimization-tools-that-make-queries-10x-faster-8629ac451d97
Инструменты Оптимизации Запросов в PostgreSQL. Часть 7
7. pghero (Панель Управления Производительностью Postgres)
Что даёт: простой мониторинг производительности PostgreSQL для команд без выделенных администраторов баз данных.
Язык: Ruby.
Поддержка: очень активная.
Зачем нужен
Большинство инструментов мониторинга сложны и дороги. pghero предоставляет 80% функциональности в простом веб-интерфейсе: медленные запросы, отсутствующие индексы, раздувание таблиц, проблемы с подключением — всё на одной панели мониторинга.
Установка
1. Docker (самая простая)
docker run -ti -e DATABASE_URL=postgres://user:pass@host:5432/dbname \
-p 8080:8080 ankane/pghero
2. Для Rails
gem install pghero
pghero config:set DATABASE_URL=postgres://…
pghero server
За чем можно наблюдать
1. Медленные запросы (последние 24 часа):
SELECT … FROM orders JOIN … (avg: 45s, 234 calls)
→ Missing index on orders(customer_id, order_date)
UPDATE inventory … (avg: 12s, 5,432 calls)
→ Long-running, blocking other queries
2. Отсутствующие индексы:
- orders.customer_id (queries: 1,234/day, est. speedup: 100x)
- customers.email (queries: 456/day, est. speedup: 50x)
3. Раздувание таблиц:
- orders: 35% bloat (recommend VACUUM)
- customer_events: 62% bloat (recommend VACUUM FULL)
4. Проблемы с соединением:
- Max connections: 100
- Current: 87 (87% utilization) ⚠️
- Idle in transaction: 12 (potential locks)
5. Использование индексов:
- idx_orders_date: 2.3M scans/day ✅
- idx_orders_legacy: 0 scans/day (unused, consider dropping)
Когда использовать
- PostgreSQL в продакшене;
- нет выделенного администратора баз данных;
- нужен простой мониторинг;
- нужны действенные рекомендации (а не просто метрики).
Когда отказаться
- необходим мониторинг разных БД (pghero работает только с PostgreSQL);
- необходим подробный исторический анализ (pghero отображает текущее состояние + 24 часа);
- уже есть комплексный мониторинг (Datadog и т. д.).
Скрытая функция
Визуализация плана запроса. В pghero щёлкните на любой медленный запрос. Отобразится:
1. Текст запроса;
2. План выполнения (визуальный);
3. Рекомендуемые индексы;
4. Предложение по изменению запроса;
5. Создание индекса одним щелчком (генерирует DDL).
С осторожностью
Запросы pghero могут быть дорогими на больших базах данных. Настройка, чтобы снизить нагрузку:
# config/pghero.yml
databases:
primary:
url: <%= ENV["DATABASE_URL"] %>
# Снижение частоты запросов
slow_query_ms: 1000 # Только запросы длиннее 1с
slow_query_calls: 10 # Только 10+ вызовов
# Выключение дорогих проверок
index_bloat: false # Медленно на БД в 1TB+
Источник: https://medium.com/@reliabledataengineering/15-sql-optimization-tools-that-make-queries-10x-faster-8629ac451d97
👍10
День 2639. #ЗаметкиНаПолях
Первичные Конструкторы для Внедрения Зависимостей. Начало
Когда в C# 12 внедрили первичные конструкторы в классах, это было встречено скептически. Неявное изменяемое поле вместо явного поля только для чтения? Это казалось обменом безопасности на удобство. Но практика показала, что они значительно сокращают количество шаблонного кода в сервисных классах, а ловушка, которой все опасались, становится управляемой, если вы о ней знаете.
Классы сервисов
Вот как раньше выглядели классы сервисов:
И как теперь:
Объявления полей, тело конструктора, присваивания — всё исчезло. Параметры перехватываются и становятся доступны во всём теле класса.
Это наиболее распространённый вариант использования первичных конструкторов: внедрение зависимостей в классы сервисов. Вы объявляете то, что вам нужно, и используете это напрямую. Экономия на шаблонном коде быстро накапливается. Классам сервисов обычно не нужно проверять или преобразовывать свои зависимости. DI-контейнер предоставляет их, а вы их используете. Первичные конструкторы идеально подходят для этого.
Создание сущностей (с оговоркой)
Можно использовать первичные конструкторы для сущностей предметной области и объектов-значений, где необходимо обеспечить обязательность параметров во время создания:
Создать заказ без customerId и total невозможно. Первичный конструктор делает это ограничение видимым на уровне объявления типа.
Обратите внимание на ключевое отличие от шаблона класса сервиса: параметры первичного конструктора присваиваются свойствам с инициализаторами (= customerId). Это важно, и это приводит к самой большой проблеме первичных конструкторов.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/why-i-switched-to-primary-constructors-for-di-in-csharp
Первичные Конструкторы для Внедрения Зависимостей. Начало
Когда в C# 12 внедрили первичные конструкторы в классах, это было встречено скептически. Неявное изменяемое поле вместо явного поля только для чтения? Это казалось обменом безопасности на удобство. Но практика показала, что они значительно сокращают количество шаблонного кода в сервисных классах, а ловушка, которой все опасались, становится управляемой, если вы о ней знаете.
Классы сервисов
Вот как раньше выглядели классы сервисов:
public class OrderService
{
private readonly IOrderRepository _repo;
private readonly ILogger<OrderService> _logger;
public OrderService(
IOrderRepository repo,
ILogger<OrderService> logger)
{
_repo = repo;
_logger = logger;
}
public async Task<Order?> GetAsync(Guid id)
{
_logger.LogInformation("Получаем заказ {OrderId}", id);
return await _repo.GetByIdAsync(id);
}
}
И как теперь:
public class OrderService(
IOrderRepository repo,
ILogger<OrderService> logger)
{
public async Task<Order?> GetAsync(Guid id)
{
logger.LogInformation("Получаем заказ {OrderId}", id);
return await repo.GetByIdAsync(id);
}
}
Объявления полей, тело конструктора, присваивания — всё исчезло. Параметры перехватываются и становятся доступны во всём теле класса.
Это наиболее распространённый вариант использования первичных конструкторов: внедрение зависимостей в классы сервисов. Вы объявляете то, что вам нужно, и используете это напрямую. Экономия на шаблонном коде быстро накапливается. Классам сервисов обычно не нужно проверять или преобразовывать свои зависимости. DI-контейнер предоставляет их, а вы их используете. Первичные конструкторы идеально подходят для этого.
Создание сущностей (с оговоркой)
Можно использовать первичные конструкторы для сущностей предметной области и объектов-значений, где необходимо обеспечить обязательность параметров во время создания:
public class Order(Guid customerId, Money total)
{
public Guid Id { get; } = Guid.NewGuid();
public Guid CustomerId { get; } = customerId;
public Money Total { get; } = total;
public OrderStatus Status { get; private set; }
= OrderStatus.Pending;
public DateTime CreatedAt { get; }
= DateTime.UtcNow;
public void Confirm()
{
if (Status != OrderStatus.Pending)
{
throw new InvalidOperationException(
$"Нельзя подтвердить заказ в статусе {Status}.");
}
Status = OrderStatus.Confirmed;
}
}
Создать заказ без customerId и total невозможно. Первичный конструктор делает это ограничение видимым на уровне объявления типа.
Обратите внимание на ключевое отличие от шаблона класса сервиса: параметры первичного конструктора присваиваются свойствам с инициализаторами (= customerId). Это важно, и это приводит к самой большой проблеме первичных конструкторов.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/why-i-switched-to-primary-constructors-for-di-in-csharp
👍1
День 2640. #ЗаметкиНаПолях
Первичные Конструкторы для Внедрения Зависимостей. Окончание
Начало
Ловушка, которая отталкивает многих
Параметры первичного конструктора не являются полями только для чтения. Когда вы используете параметр первичного конструктора непосредственно в теле класса (как мы делали в классе сервиса), компилятор захватывает его как изменяемую переменную. За кулисами не генерируется поля только для чтения.
Т.е. вы можете изменить значение параметра:
Этот код компилируется без ошибок и предупреждений. Если вам необходимы гарантии неизменяемости, явно присвойте параметр полю только для чтения:
Но теперь мы потеряли большую часть преимуществ первичных конструкторов. Мы вернулись к объявлениям полей и присваиваниям, только с другим синтаксисом.
На практике вряд ли вы столкнётесь с этой проблемой в классе сервиса, получаемого из DI. Маловероятно, что вы случайно измените значение logger посреди метода. Но это может создать проблемы в классах сущностей или объектов-значений, где неизменяемость действительно важна. Это единственное место, где по-прежнему стоит проявлять осторожность.
Где использовать традиционные конструкторы
Вот случаи, когда стоит придерживаться традиционного подхода:
1. Сложная логика валидации
Если нужно проверять параметры перед их присваиванием, понадобится тело конструктора:
Первичные конструкторы не позволяют разместить логику валидации до выполнения тела класса.
2. Множественные перегрузки конструкторов
Первичные конструкторы поддерживают одну сигнатуру конструктора. Если вам нужны перегрузки, придётся связывать обычные конструкторы с помощью
3. Слишком много параметров
Как только вы достигнете 5 и более зависимостей, строка первичного конструктора станет трудночитаемой. Хотя, тогда класс, вероятно, будет иметь слишком много обязанностей, и рефакторинг будет лучшим решением, чем ухищрения с форматированием.
Итого
- Можно использовать первичные конструкторы для всех классов сервисов в DI. Экономия на шаблонном коде того стоит.
- Они полезны для создания сущностей, когда вы хотите обеспечить обязательность параметров на уровне типа.
- Параметры основного конструктора сохраняются как изменяемые переменные, а не поля только для чтения. Это единственное, о чём нужно помнить. Эта изменяемость не страшна в классах сервисов, потому что в этом контексте это вряд ли вызовет реальные ошибки.
- Стоит придерживаться традиционных конструкторов для типов с валидацией, множественными перегрузками или со слишком большим количеством зависимостей.
Переход того стоит. Классы сервисов короче, их проще сканировать, а ловушка изменяемости не так уж страшна.
Источник: https://www.milanjovanovic.tech/blog/why-i-switched-to-primary-constructors-for-di-in-csharp
Первичные Конструкторы для Внедрения Зависимостей. Окончание
Начало
Ловушка, которая отталкивает многих
Параметры первичного конструктора не являются полями только для чтения. Когда вы используете параметр первичного конструктора непосредственно в теле класса (как мы делали в классе сервиса), компилятор захватывает его как изменяемую переменную. За кулисами не генерируется поля только для чтения.
Т.е. вы можете изменить значение параметра:
public class OrderService(
IOrderRepository repo,
ILogger<OrderService> logger)
{
// …
public void SomeOtherMethod()
{
repo = null!;
logger = null!;
}
}
Этот код компилируется без ошибок и предупреждений. Если вам необходимы гарантии неизменяемости, явно присвойте параметр полю только для чтения:
public class OrderService(
IOrderRepository repo,
ILogger<OrderService> logger)
{
private readonly IOrderRepository _repo = repo;
private readonly ILogger<OrderService> _logger = logger;
// …
}
Но теперь мы потеряли большую часть преимуществ первичных конструкторов. Мы вернулись к объявлениям полей и присваиваниям, только с другим синтаксисом.
На практике вряд ли вы столкнётесь с этой проблемой в классе сервиса, получаемого из DI. Маловероятно, что вы случайно измените значение logger посреди метода. Но это может создать проблемы в классах сущностей или объектов-значений, где неизменяемость действительно важна. Это единственное место, где по-прежнему стоит проявлять осторожность.
Где использовать традиционные конструкторы
Вот случаи, когда стоит придерживаться традиционного подхода:
1. Сложная логика валидации
Если нужно проверять параметры перед их присваиванием, понадобится тело конструктора:
public class EmailAddress
{
private readonly string _value;
public EmailAddress(string value)
{
if (string.IsNullOrWhiteSpace(value)
|| !value.Contains('@'))
throw new ArgumentException(
"Неверный email.", nameof(value));
_value = value;
}
}
Первичные конструкторы не позволяют разместить логику валидации до выполнения тела класса.
2. Множественные перегрузки конструкторов
Первичные конструкторы поддерживают одну сигнатуру конструктора. Если вам нужны перегрузки, придётся связывать обычные конструкторы с помощью
this(…), что быстро приводит к беспорядку.3. Слишком много параметров
Как только вы достигнете 5 и более зависимостей, строка первичного конструктора станет трудночитаемой. Хотя, тогда класс, вероятно, будет иметь слишком много обязанностей, и рефакторинг будет лучшим решением, чем ухищрения с форматированием.
Итого
- Можно использовать первичные конструкторы для всех классов сервисов в DI. Экономия на шаблонном коде того стоит.
- Они полезны для создания сущностей, когда вы хотите обеспечить обязательность параметров на уровне типа.
- Параметры основного конструктора сохраняются как изменяемые переменные, а не поля только для чтения. Это единственное, о чём нужно помнить. Эта изменяемость не страшна в классах сервисов, потому что в этом контексте это вряд ли вызовет реальные ошибки.
- Стоит придерживаться традиционных конструкторов для типов с валидацией, множественными перегрузками или со слишком большим количеством зависимостей.
Переход того стоит. Классы сервисов короче, их проще сканировать, а ловушка изменяемости не так уж страшна.
Источник: https://www.milanjovanovic.tech/blog/why-i-switched-to-primary-constructors-for-di-in-csharp
👍1
День 2641. #ЗаметкиНаПолях
Паттерн «Интерпретатор» в .NET. Начало
Когда бизнес-правила похоронены в цепочках if-else.
Система скидок начиналась с простого: 10% на заказы свыше $100. Одно условие, одно правило. Затем отдел маркетинга захотел: "20% для VIP-клиентов И заказом свыше $200." Затем: "Бесплатную доставку товаров категории "Электроника" ИЛИ за заказ свыше $500." И т.д., и т.п.
Теперь метод предоставления скидок — это 150 строк вложенных условных операторов, к которым никто не хочет прикасаться:
Каждое новое правило требует модификации кода, повторного развёртывания и тестирования всей цепочки. Маркетинг не может обновлять правила без участия разработчиков. А логика совершенно непрозрачна — никто не может прочитать её и понять весь набор правил.
Проблема в том, что эти правила представляют собой бизнес-логику, которая часто меняется, но они заблокированы внутри скомпилированного кода. Нужен способ представить правила в виде данных — чего-то, что можно анализировать, компоновать и динамически оценивать.
Паттерн «Интерпретатор»
Паттерн «Интерпретатор» определяет грамматику для языка и предоставляет интерпретатор, который оценивает выражения на этом языке. Каждое правило в грамматике - класс. Сложные правила компонуются из простых.
Определим интерфейс выражений и атомарные выражения:
Теперь нетерминальные выражения – логические действия:
Составляем правила из выражений:
Правила — это компонуемые структуры данных. Вы можете хранить их, сериализовать и создавать из конфигурации.
Почему это лучше
1. Правила — это данные, а не код. Вы можете хранить определения правил в БД и создавать деревья выражений во время выполнения. Для новых правил не требуется повторное развёртывание.
2. Компонуемость. Операторы И, ИЛИ и НЕ объединяют любые выражения. Сложные правила строятся из простых, протестированных компонентов.
3. Тестируемость. Каждое выражение представляет собой отдельный класс с одним методом. Тестируйте OrderTotalGreaterThan независимо от всего остального.
Окончание следует…
Источник: https://thecodeman.net/posts/interpreter-pattern-in-dotnet
Паттерн «Интерпретатор» в .NET. Начало
Когда бизнес-правила похоронены в цепочках if-else.
Система скидок начиналась с простого: 10% на заказы свыше $100. Одно условие, одно правило. Затем отдел маркетинга захотел: "20% для VIP-клиентов И заказом свыше $200." Затем: "Бесплатную доставку товаров категории "Электроника" ИЛИ за заказ свыше $500." И т.д., и т.п.
Теперь метод предоставления скидок — это 150 строк вложенных условных операторов, к которым никто не хочет прикасаться:
public decimal CalculateDiscount(
Order order, Customer customer)
{
if (customer.IsVip && order.Total > 200)
return order.Total * 0.20m;
else if (order.Total > 100)
return order.Total * 0.10m;
else if (order.Items.Any(i => i.Category == "Electronics") || order.Total > 500)
order.Shipping = 0;
// … ещё 20 условий
return order.Total;
}
Каждое новое правило требует модификации кода, повторного развёртывания и тестирования всей цепочки. Маркетинг не может обновлять правила без участия разработчиков. А логика совершенно непрозрачна — никто не может прочитать её и понять весь набор правил.
Проблема в том, что эти правила представляют собой бизнес-логику, которая часто меняется, но они заблокированы внутри скомпилированного кода. Нужен способ представить правила в виде данных — чего-то, что можно анализировать, компоновать и динамически оценивать.
Паттерн «Интерпретатор»
Паттерн «Интерпретатор» определяет грамматику для языка и предоставляет интерпретатор, который оценивает выражения на этом языке. Каждое правило в грамматике - класс. Сложные правила компонуются из простых.
Определим интерфейс выражений и атомарные выражения:
// Контекст - данные, которые будут оцениваться в выражениях
public class RuleContext(Order order, Customer customer)
{
public Order Order { get; } = order;
public Customer Customer { get; } = customer;
}
// Абстрактное выражение
public interface IExpression
{
bool Interpret(RuleContext ctx);
}
// Окончательные выражения: атомарные условия
public class OrderTotalGreaterThan(decimal threshold)
: IExpression
{
public bool Interpret(RuleContext ctx)
=> context.Order.Total > threshold;
}
public class CustomerIsVip : IExpression
{
public bool Interpret(RuleContext ctx)
=> context.Customer.IsVip;
}
public class OrderContainsCategory(string category)
: IExpression
{
public bool Interpret(RuleContext ctx)
=> ctx.Order.Items.Any(i =>
i.Category.Equals(category,
StringComparison.OrdinalIgnoreCase));
}
// …
Теперь нетерминальные выражения – логические действия:
// И
public class AndExpression(IExpression left, IExpression right)
: IExpression
{
public bool Interpret(RuleContext ctx)
=> left.Interpret(ctx) && right.Interpret(ctx);
}
// ИЛИ
public class OrExpression(IExpression left, IExpression right)
: IExpression
{
public bool Interpret(RuleContext ctx)
=> left.Interpret(ctx) || right.Interpret(ctx);
}
// НЕ
public class NotExpression(IExpression expression)
: IExpression
{
public bool Interpret(RuleContext ctx)
=> !expression.Interpret(ctx);
}
Составляем правила из выражений:
// VIP-клиент и заказ > $200
var vipHighValueRule = new AndExpression(
new CustomerIsVip(),
new OrderTotalGreaterThan(200));
var ctx = new RuleContext(order, customer);
if (vipHighValueRule.Interpret(ctx))
discount = 0.20m;
Правила — это компонуемые структуры данных. Вы можете хранить их, сериализовать и создавать из конфигурации.
Почему это лучше
1. Правила — это данные, а не код. Вы можете хранить определения правил в БД и создавать деревья выражений во время выполнения. Для новых правил не требуется повторное развёртывание.
2. Компонуемость. Операторы И, ИЛИ и НЕ объединяют любые выражения. Сложные правила строятся из простых, протестированных компонентов.
3. Тестируемость. Каждое выражение представляет собой отдельный класс с одним методом. Тестируйте OrderTotalGreaterThan независимо от всего остального.
Окончание следует…
Источник: https://thecodeman.net/posts/interpreter-pattern-in-dotnet
👍6
День 2642. #ЗаметкиНаПолях
Паттерн «Интерпретатор» в .NET. Расширенное использование
Начало
1. Создание правил из конфигурации
Парсим правила из JSON или БД:
Определение правила в JSON:
Теперь отдел маркетинга может определять правила в UI панели управления. RuleEngine анализирует и оценивает их без изменения кода.
2. Парсинг выражений для вычисляемых полей
Аналогично можно создавать математические выражения:
Когда не использовать
1. Для сложных правил. Если нужны циклы, функции или сложный синтаксис, используйте подходящий генератор парсеров (ANTLR) или существующий скриптовый движок (Roslyn, Lua). Паттерн «Интерпретатор» плохо масштабируется для сложных языков.
2. Важна производительность. Каждый вызов Interpret() проходит по дереву выражений. Для часто используемых путей с миллионами вычислений скомпилированные выражения (Expression<T>.Compile()) на порядки быстрее.
3. Правила редко меняются. Если правила стабильны и меняются раз в год, затраты на создание интерпретатора не оправданы. Просто пишите условия напрямую.
«Интерпретатор» избыточен?
Для 3-4 фиксированных условий — да. Просто используйте if-else. Паттерн особенно эффективен, когда у вас более 20 правил, которые регулярно меняются и позволяют создавать комбинации без перекомпиляции кода.
Альтернативы
Для оценки во время выполнения деревья выражений C# Expression<T> компилируются в IL. Для простой оценки правил может подойти словарь делегатов или паттерн «Стратегия».
Источник: https://thecodeman.net/posts/interpreter-pattern-in-dotnet
Паттерн «Интерпретатор» в .NET. Расширенное использование
Начало
1. Создание правил из конфигурации
Парсим правила из JSON или БД:
public class RuleEngine
{
public IExpression BuildFromConfig(
RuleDefinition def) => def.Type switch
{
"OrderTotalGreaterThan" =>
new OrderTotalGreaterThan(
def.GetParam<decimal>("threshold")),
"CustomerIsVip" =>
new CustomerIsVip(),
"OrderContainsCategory" =>
new OrderContainsCategory(
def.GetParam<string>("category")),
"AND" =>
new AndExpression(
BuildFromConfig(def.Left!),
BuildFromConfig(def.Right!)),
"OR" =>
new OrExpression(
BuildFromConfig(def.Left!),
BuildFromConfig(def.Right!)),
"NOT" =>
new NotExpression(BuildFromConfig(def.Left!)),
_ => throw new InvalidOperationException(
$"Неизвестный тип: {def.Type}")
};
}
Определение правила в JSON:
{
"type": "AND",
"left": { "type": "CustomerIsVip" },
"right": { "type": "OrderTotalGreaterThan", "params": { "threshold": 200 } }
}Теперь отдел маркетинга может определять правила в UI панели управления. RuleEngine анализирует и оценивает их без изменения кода.
2. Парсинг выражений для вычисляемых полей
Аналогично можно создавать математические выражения:
public interface IMathExpression
{
decimal Evaluate(Dictionary<string, decimal> variables);
}
public class NumberLiteral(decimal value)
: IMathExpression
{
public decimal Evaluate(Dictionary<string, decimal> variables)
=> value;
}
public class Variable(string name)
: IMathExpression
{
public decimal Evaluate(Dictionary<string, decimal> variables)
=> variables[name];
}
public class Multiply(IMathExpression left, IMathExpression right)
: IMathExpression
{
public decimal Evaluate(Dictionary<string, decimal> variables)
=> left.Evaluate(variables) * right.Evaluate(variables);
}
// Выражение: price * quantity * (1 - discount)
var totalExpr = new Multiply(
new Multiply(new Variable("price"), new Variable("quantity")),
new Subtract(new NumberLiteral(1), new Variable("discountRate")));
var vars = new Dictionary<string, decimal>
{
["price"] = 29.99m, ["quantity"] = 3, ["discount"] = 0.10m
};
var total = totalExpr.Evaluate(vars); // 80.973
Когда не использовать
1. Для сложных правил. Если нужны циклы, функции или сложный синтаксис, используйте подходящий генератор парсеров (ANTLR) или существующий скриптовый движок (Roslyn, Lua). Паттерн «Интерпретатор» плохо масштабируется для сложных языков.
2. Важна производительность. Каждый вызов Interpret() проходит по дереву выражений. Для часто используемых путей с миллионами вычислений скомпилированные выражения (Expression<T>.Compile()) на порядки быстрее.
3. Правила редко меняются. Если правила стабильны и меняются раз в год, затраты на создание интерпретатора не оправданы. Просто пишите условия напрямую.
«Интерпретатор» избыточен?
Для 3-4 фиксированных условий — да. Просто используйте if-else. Паттерн особенно эффективен, когда у вас более 20 правил, которые регулярно меняются и позволяют создавать комбинации без перекомпиляции кода.
Альтернативы
Для оценки во время выполнения деревья выражений C# Expression<T> компилируются в IL. Для простой оценки правил может подойти словарь делегатов или паттерн «Стратегия».
Источник: https://thecodeman.net/posts/interpreter-pattern-in-dotnet
👍1
День 2643. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
31. Паттерн MVC
«Расскажите, что такое паттерн MVC и как вы его реализовали в своих проектах? Приведите пример того, как вы использовали MVC.»
Хороший ответ
Паттерн MVC (Model-View-Controller) — это принцип проектирования, который разделяет приложение на три взаимосвязанных компонента. Такое разделение помогает управлять сложностью, способствует организации кода и поддерживает масштабируемость:
1. Модель - слой данных и бизнес-логики приложения. Она отвечает за хранение данных, их обработку и определение бизнес-правил.
2. Представление - обрабатывает отображение данных, получая модель от контроллера. Оно только отображает информацию пользователю и отправляет команды пользователя контроллеру.
3. Контроллер - выступает в качестве посредника между моделью и представлением, получая данные из модели и решая, какое представление отображать.
В своих проектах я использовал паттерн MVC, чтобы обеспечить надлежащее разделение задач, что упрощает поддержку и расширение приложения. Например:
В этом примере ProductsController получает данные о продуктах, используя репозиторий IProductRepo, который абстрагирует логику доступа к данным. Полученные данные затем передаются в представление Index, которое отвечает за отображение информации о продуктах. Такое разделение позволяет вносить изменения в модель БД или бизнес-логику, не затрагивая логику представления, и наоборот, тем самым соблюдая принцип единственной ответственности.
Такой подход не только делает архитектуру яснее, но и повышает тестируемость приложения. Каждый компонент может быть протестирован независимо, что крайне важно для поддержания высокого качества кода по мере масштабирования приложения.
Часто встречающийся плохой ответ
«MVC — это просто обеспечение наличия моделей, представлений и контроллеров в проекте. Нужно поместить HTML-код в представления, код получения из БД в модели и использовать контроллеры для связи всего этого».
Почему это неправильно
- Чрезмерное упрощение ролей: этот ответ упрощает роли моделей, представлений и контроллеров, не понимая их конкретных обязанностей.
- Отсутствие разделения ответственности: ответ недостаточно рассматривает разделение ответственности, которое является ключевым преимуществом использования MVC. Просто связывая компоненты, разработчик упускает суть MVC, которая заключается в максимально возможной децентрализации этих компонентов для обеспечения независимой разработки, тестирования и сопровождения.
- Непонимание лучших практик в архитектуре MVC, таких как размещение бизнес-логики вне контроллеров и представлений.
Эта ошибка часто возникает из-за поверхностного понимания паттерна MVC, возможно, из-за ограниченного опыта работы над проектами, где MVC использовался эффективно, или из-за сосредоточенности на простом запуске приложения без понимания основных принципов проектирования MVC.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
31. Паттерн MVC
«Расскажите, что такое паттерн MVC и как вы его реализовали в своих проектах? Приведите пример того, как вы использовали MVC.»
Хороший ответ
Паттерн MVC (Model-View-Controller) — это принцип проектирования, который разделяет приложение на три взаимосвязанных компонента. Такое разделение помогает управлять сложностью, способствует организации кода и поддерживает масштабируемость:
1. Модель - слой данных и бизнес-логики приложения. Она отвечает за хранение данных, их обработку и определение бизнес-правил.
2. Представление - обрабатывает отображение данных, получая модель от контроллера. Оно только отображает информацию пользователю и отправляет команды пользователя контроллеру.
3. Контроллер - выступает в качестве посредника между моделью и представлением, получая данные из модели и решая, какое представление отображать.
В своих проектах я использовал паттерн MVC, чтобы обеспечить надлежащее разделение задач, что упрощает поддержку и расширение приложения. Например:
// Модель
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
// Контроллер
public class ProductsController : Controller
{
private readonly IProductRepo _repo;
public ProductsController(IProductRepo repo)
{
_repo = repo;
}
public async Task<IActionResult> Index()
{
var products = await _repo.GetAllAsync();
// Передаём модель в представление
return View(products);
}
}
// Представление (Index.cshtml)
@model IEnumerable<Product>
<h2>Продукты</h2>
<ul>
@foreach(var product in Model)
{
<li>@product.Name - $@product.Price</li>
}
</ul>
В этом примере ProductsController получает данные о продуктах, используя репозиторий IProductRepo, который абстрагирует логику доступа к данным. Полученные данные затем передаются в представление Index, которое отвечает за отображение информации о продуктах. Такое разделение позволяет вносить изменения в модель БД или бизнес-логику, не затрагивая логику представления, и наоборот, тем самым соблюдая принцип единственной ответственности.
Такой подход не только делает архитектуру яснее, но и повышает тестируемость приложения. Каждый компонент может быть протестирован независимо, что крайне важно для поддержания высокого качества кода по мере масштабирования приложения.
Часто встречающийся плохой ответ
«MVC — это просто обеспечение наличия моделей, представлений и контроллеров в проекте. Нужно поместить HTML-код в представления, код получения из БД в модели и использовать контроллеры для связи всего этого».
Почему это неправильно
- Чрезмерное упрощение ролей: этот ответ упрощает роли моделей, представлений и контроллеров, не понимая их конкретных обязанностей.
- Отсутствие разделения ответственности: ответ недостаточно рассматривает разделение ответственности, которое является ключевым преимуществом использования MVC. Просто связывая компоненты, разработчик упускает суть MVC, которая заключается в максимально возможной децентрализации этих компонентов для обеспечения независимой разработки, тестирования и сопровождения.
- Непонимание лучших практик в архитектуре MVC, таких как размещение бизнес-логики вне контроллеров и представлений.
Эта ошибка часто возникает из-за поверхностного понимания паттерна MVC, возможно, из-за ограниченного опыта работы над проектами, где MVC использовался эффективно, или из-за сосредоточенности на простом запуске приложения без понимания основных принципов проектирования MVC.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👎2
День 2644. #ЧтоНовенького
Первые Очертания Visual Studio 2027
Мадс Кристенсен из Microsoft на конференции VSLive! в Лас-Вегасе (плейлист докладов, кстати, вот тут) рассказал, что нового будет в Visual Studio 2027.
Модель непрерывного выпуска
Вместо того чтобы позиционировать 2027 как традиционную новую основную версию, которая выходит как отдельный продукт, представленная на VSLive! дорожная карта описывает более стабильное ежемесячное развитие с появлением новых возможностей по ходу.
Т.е. Microsoft переводит VS на модель непрерывного выпуска. Стабильный канал General Availability получает ежемесячные обновления, а канал Insider развивается - еженедельные, предоставляя разработчикам ранний доступ к функциям. Так переход от VS 2026 к VS 2027 – это обновление существующей среды на месте, а не переустановка, и он будет скорей последовательностью непрерывных обновлений, а не единым релизом. Теперь командам разработки не придётся планировать миграцию инструментов отдельно от обычных циклов обновлений. Поэтапные обновления также приблизят IDE к темпам изменений в рабочих процессах разработки .NET и C# (VS 2027 обещают выпустить вместе с релизом новой версии .NET в ноябре).
ИИ и профессиональный разработчик
Разработчики по-прежнему проводят время перед экранами, занимаясь написанием кода, отладкой приложений и управлением ПО, даже если они начинают использовать инструменты ИИ в некоторых аспектах своей работы. Существующие навыки остаются важными, поскольку системам ИИ по-прежнему не хватает более широкого контекста, который разработчики подразумевают в своих системах, организациях и планах на будущее. Хотя, в написании отдельных частей кода ИИ уже показывает неплохие результаты. Поэтому теперь разработчики, вероятно, будут тратить больше времени на проверку и оценку кода, независимо от того, написан ли он людьми или сгенерирован с помощью ИИ.
Проверка кода с помощью профилирования и агентов
Этот сдвиг повышает важность инструментов, помогающих разработчикам понимать и проверять ПО. Модульные и интеграционные тесты могут сократить объём ручной проверки, предоставляя разработчикам больше уверенности в том, что изменения не нарушают существующее поведение.
В демонстрации новой возможности VS «Профилирование с помощью Copilot» Copilot запустил модульный тест для популярной библиотеки QR Coder в профилировщике, установил базовый уровень производительности, проанализировал полученную трассировку, выявил ресурсоёмкие пути в коде (в частности, вычисления с плавающей запятой), предложил оптимизации, пересобрал проект, повторно запустил тест для подтверждения корректности и измерил результирующее улучшение производительности примерно на 63%.
Эта функция будет полезна даже без ИИ, поскольку разработчики теперь могут использовать модульные тесты в качестве отправной точки для профилирования с целью выявления ресурсоёмких участков кода. Добавление ИИ к этому рабочему процессу помогает сократить разрыв между обнаружением проблемы производительности и пониманием того, как её улучшить.
Ожидается, что VS будет развиваться областях тестирования, диагностики, такой как отладка и профилирование, ускорения процессов сборки и запуска, интеграции с другими системами, участвующими в доставке ПО, проверки кода и ИИ. Обозреватель тестов VS обновлялся не так быстро, как другие инструменты, и это ключевая область для модернизации.
Главный посыл сессии Кристенсена заключался в том, что VS формируется для среды разработки, в которой ИИ всё больше присутствует, но не в качестве замены разработчиков. Кристенсен утверждает, что разработчикам по-прежнему потребуются сильные навыки в программировании, отладке, тестировании и понимании того, как системы взаимодействуют друг с другом, — и что эти навыки могут стать ещё более важными по мере того, как ИИ будет всё больше использоваться в рабочем процессе.
Источник: https://visualstudiomagazine.com/articles/2026/04/17/at-vslive-mads-k-lets-the-cat-out-of-the-bag-on-visual-studio-2027.aspx
Первые Очертания Visual Studio 2027
Мадс Кристенсен из Microsoft на конференции VSLive! в Лас-Вегасе (плейлист докладов, кстати, вот тут) рассказал, что нового будет в Visual Studio 2027.
Модель непрерывного выпуска
Вместо того чтобы позиционировать 2027 как традиционную новую основную версию, которая выходит как отдельный продукт, представленная на VSLive! дорожная карта описывает более стабильное ежемесячное развитие с появлением новых возможностей по ходу.
Т.е. Microsoft переводит VS на модель непрерывного выпуска. Стабильный канал General Availability получает ежемесячные обновления, а канал Insider развивается - еженедельные, предоставляя разработчикам ранний доступ к функциям. Так переход от VS 2026 к VS 2027 – это обновление существующей среды на месте, а не переустановка, и он будет скорей последовательностью непрерывных обновлений, а не единым релизом. Теперь командам разработки не придётся планировать миграцию инструментов отдельно от обычных циклов обновлений. Поэтапные обновления также приблизят IDE к темпам изменений в рабочих процессах разработки .NET и C# (VS 2027 обещают выпустить вместе с релизом новой версии .NET в ноябре).
ИИ и профессиональный разработчик
Разработчики по-прежнему проводят время перед экранами, занимаясь написанием кода, отладкой приложений и управлением ПО, даже если они начинают использовать инструменты ИИ в некоторых аспектах своей работы. Существующие навыки остаются важными, поскольку системам ИИ по-прежнему не хватает более широкого контекста, который разработчики подразумевают в своих системах, организациях и планах на будущее. Хотя, в написании отдельных частей кода ИИ уже показывает неплохие результаты. Поэтому теперь разработчики, вероятно, будут тратить больше времени на проверку и оценку кода, независимо от того, написан ли он людьми или сгенерирован с помощью ИИ.
Проверка кода с помощью профилирования и агентов
Этот сдвиг повышает важность инструментов, помогающих разработчикам понимать и проверять ПО. Модульные и интеграционные тесты могут сократить объём ручной проверки, предоставляя разработчикам больше уверенности в том, что изменения не нарушают существующее поведение.
В демонстрации новой возможности VS «Профилирование с помощью Copilot» Copilot запустил модульный тест для популярной библиотеки QR Coder в профилировщике, установил базовый уровень производительности, проанализировал полученную трассировку, выявил ресурсоёмкие пути в коде (в частности, вычисления с плавающей запятой), предложил оптимизации, пересобрал проект, повторно запустил тест для подтверждения корректности и измерил результирующее улучшение производительности примерно на 63%.
Эта функция будет полезна даже без ИИ, поскольку разработчики теперь могут использовать модульные тесты в качестве отправной точки для профилирования с целью выявления ресурсоёмких участков кода. Добавление ИИ к этому рабочему процессу помогает сократить разрыв между обнаружением проблемы производительности и пониманием того, как её улучшить.
Ожидается, что VS будет развиваться областях тестирования, диагностики, такой как отладка и профилирование, ускорения процессов сборки и запуска, интеграции с другими системами, участвующими в доставке ПО, проверки кода и ИИ. Обозреватель тестов VS обновлялся не так быстро, как другие инструменты, и это ключевая область для модернизации.
Главный посыл сессии Кристенсена заключался в том, что VS формируется для среды разработки, в которой ИИ всё больше присутствует, но не в качестве замены разработчиков. Кристенсен утверждает, что разработчикам по-прежнему потребуются сильные навыки в программировании, отладке, тестировании и понимании того, как системы взаимодействуют друг с другом, — и что эти навыки могут стать ещё более важными по мере того, как ИИ будет всё больше использоваться в рабочем процессе.
Источник: https://visualstudiomagazine.com/articles/2026/04/17/at-vslive-mads-k-lets-the-cat-out-of-the-bag-on-visual-studio-2027.aspx
👍3