День 2748. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
42. Шаблоны взаимодействия в микросервисах
«Можете ли вы рассказать о различных шаблонах взаимодействия, используемых в микросервисной архитектуре, и как бы вы реализовали их в приложении .NET?»
Хороший ответ
Эффективная коммуникация между микросервисами имеет решающее значение для успеха архитектуры. Существует несколько распространённых шаблонов коммуникации, каждый из которых подходит для разных сценариев:
- HTTP/REST/gRPC: наиболее распространённый метод синхронной коммуникации, при котором сервисы используют HTTP-запросы для связи. Он прост и не имеет состояния.
- Очереди сообщений: используются для децентрализованной, надёжной асинхронной коммуникации. Они помогают справляться с пиковыми нагрузками и обеспечивают механизм, гарантирующий, что данные не будут потеряны при передаче.
- Событийно-ориентированная модель «публикация/подписка»: эта модель усиливает децентрализацию сервисов, позволяя сервисам подписываться на определённые события, не зная источника этих событий.
Преимущества
- Децентрализация: сервисы не зависят друг от друга напрямую, что повышает отказоустойчивость и масштабируемость.
- Масштабируемость: асинхронные и событийно-ориентированные подходы позволяют системам эффективно обрабатывать изменяющиеся нагрузки.
- Надёжность: очереди сообщений гарантируют доставку сообщений даже если части системы выходят из строя.
В таких реализациях крайне важно обрабатывать сбои, повторные попытки и идемпотентность, особенно в асинхронных сценариях, чтобы обеспечить надёжность и согласованность системы.
Часто встречающийся ошибочный ответ
«Для связи между микросервисами используются HTTP-запросы. Это просто и гарантирует, что сервисы могут общаться в режиме реального времени».
Почему это неправильно
- Чрезмерная зависимость от синхронной связи: этот подход игнорирует преимущества асинхронных моделей связи. Хотя HTTP прост и эффективен для определённых сценариев, он может создавать тесную взаимосвязь и плохо масштабируется при высокой нагрузке или в сложных системах.
- Игнорирование преимуществ очередей сообщений: не используя очереди сообщений или архитектуры, управляемые событиями, ответ упускает из виду устойчивость, которую предлагают эти модели, особенно с точки зрения надёжности, слабой связанности и асинхронной обработки.
- Риск системных сбоев: упор исключительно на HTTP-запросы может привести к сбоям, если какая-либо отдельная часть системы станет недоступной, что повлияет на доступность всей системы.
Эта ошибка часто возникает из-за недостаточного понимания лучших архитектурных практик в микросервисах или чрезмерного упрощения потребностей в коммуникации в распределённых системах. Это отражает необходимость более глубокого знания различных стратегий коммуникации и соответствующих сценариев их использования.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
42. Шаблоны взаимодействия в микросервисах
«Можете ли вы рассказать о различных шаблонах взаимодействия, используемых в микросервисной архитектуре, и как бы вы реализовали их в приложении .NET?»
Хороший ответ
Эффективная коммуникация между микросервисами имеет решающее значение для успеха архитектуры. Существует несколько распространённых шаблонов коммуникации, каждый из которых подходит для разных сценариев:
- HTTP/REST/gRPC: наиболее распространённый метод синхронной коммуникации, при котором сервисы используют HTTP-запросы для связи. Он прост и не имеет состояния.
- Очереди сообщений: используются для децентрализованной, надёжной асинхронной коммуникации. Они помогают справляться с пиковыми нагрузками и обеспечивают механизм, гарантирующий, что данные не будут потеряны при передаче.
- Событийно-ориентированная модель «публикация/подписка»: эта модель усиливает децентрализацию сервисов, позволяя сервисам подписываться на определённые события, не зная источника этих событий.
Преимущества
- Децентрализация: сервисы не зависят друг от друга напрямую, что повышает отказоустойчивость и масштабируемость.
- Масштабируемость: асинхронные и событийно-ориентированные подходы позволяют системам эффективно обрабатывать изменяющиеся нагрузки.
- Надёжность: очереди сообщений гарантируют доставку сообщений даже если части системы выходят из строя.
В таких реализациях крайне важно обрабатывать сбои, повторные попытки и идемпотентность, особенно в асинхронных сценариях, чтобы обеспечить надёжность и согласованность системы.
Часто встречающийся ошибочный ответ
«Для связи между микросервисами используются HTTP-запросы. Это просто и гарантирует, что сервисы могут общаться в режиме реального времени».
Почему это неправильно
- Чрезмерная зависимость от синхронной связи: этот подход игнорирует преимущества асинхронных моделей связи. Хотя HTTP прост и эффективен для определённых сценариев, он может создавать тесную взаимосвязь и плохо масштабируется при высокой нагрузке или в сложных системах.
- Игнорирование преимуществ очередей сообщений: не используя очереди сообщений или архитектуры, управляемые событиями, ответ упускает из виду устойчивость, которую предлагают эти модели, особенно с точки зрения надёжности, слабой связанности и асинхронной обработки.
- Риск системных сбоев: упор исключительно на HTTP-запросы может привести к сбоям, если какая-либо отдельная часть системы станет недоступной, что повлияет на доступность всей системы.
Эта ошибка часто возникает из-за недостаточного понимания лучших архитектурных практик в микросервисах или чрезмерного упрощения потребностей в коммуникации в распределённых системах. Это отражает необходимость более глубокого знания различных стратегий коммуникации и соответствующих сценариев их использования.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👍4👎3
День 2749. #BestPractices #SQL
Как Оптимизировать SQL-запросы. Часть 1
Медленный SQL-запрос — один из самых простых способов испортить быстрое приложение. У вас может быть чистая архитектура, отличный кэш и мощный сервер — и всё равно страница может зависать из-за того, что один запрос сканирует миллион строк без индекса. Большинство успехов достигается за счёт одного и того же небольшого набора методов, применяемых снова и снова. Некоторые из них очевидны. Некоторые противоречат советам, которые вы, вероятно, уже слышали. Советы можно условно разделить на 6 групп.
Замечание: здесь мы рассматриваем PostgreSQL. Те же принципы применимы и к другим БД, хотя точный синтаксис может отличаться.
Группа I. Написание запросов, удобных для индексации
Индекс полезен только в том случае, если ваш запрос позволяет БД его использовать.
1. Разумно используйте индексы
Индексы — самый мощный метод повышения производительности чтения. Это отсортированная структура данных, которая позволяет БД находить строки, не сканируя всю таблицу, подобно тому, как оглавление книги избавляет вас от необходимости пролистывать все страницы.
Создавайте индексы по столбцам, по которым вы чаще всего выполняете фильтрацию, соединение, сортировку и группировку — столбцам в
Этот индекс ускоряет запросы, фильтрующие по статусу, а также по статусу и дате заказа. Порядок столбцов имеет значение: индекс по
Вы также можете создать покрывающий индекс, который хранит дополнительные значения столбцов внутри индекса. Это полезно для небольших частых запросов на поиск, когда запросу нужны только столбцы, доступные в индексе, чтобы БД могла избежать чтения фактических строк таблицы:
Замечание: индексы не бесплатны. Каждый индекс необходимо обновлять при каждой вставке, обновлении и удалении, и это занимает место на диске. Индексируйте столбцы, которые фактически используются вашими запросами, а не каждый столбец.
2. Избегайте функций в WHERE
Обёртывание столбца в функцию — один из наиболее распространённых способов случайно отключить индекс. Когда вы вызываете функцию для столбца, БД должна вычислить значение функции для каждой строки, прежде чем сможет сравнить его, поэтому она не может использовать индекс для исходного столбца:
Перепишите условие так, чтобы оно сравнивало исходный столбец с диапазоном:
Оба запроса возвращают одни и те же строки, но только второй может использовать индекс по
То же правило применяется к
3. Избегайте символов подстановки в начале запроса LIKE
Шаблон LIKE, начинающийся с символа подстановки, не может использовать обычный индекс. БД считывает индекс слева направо, поэтому ей необходимо знать начало значения. Шаблон типа
Если вам действительно нужно выполнить contains-поиск в тексте, используйте полнотекстовый поиск или триграммный индекс (расширение pg_trgm в PostgreSQL), созданный специально для этой задачи.
4. Точное соответствие типов данных
Сравнение двух разных типов данных заставляет БД преобразовывать один из них, и это преобразование может незаметно отключить индекс.
Если столбец является целым числом, но вы сравниваете его со строкой, или соединяете int-ключ с bigint-ключом, БД добавляет неявное приведение типов — и индекс по исходному столбцу может быть пропущен. Сохраняйте одинаковые типы с обеих сторон каждого соединения и фильтрации:
Определите
Продолжение следует…
https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
Как Оптимизировать SQL-запросы. Часть 1
Медленный SQL-запрос — один из самых простых способов испортить быстрое приложение. У вас может быть чистая архитектура, отличный кэш и мощный сервер — и всё равно страница может зависать из-за того, что один запрос сканирует миллион строк без индекса. Большинство успехов достигается за счёт одного и того же небольшого набора методов, применяемых снова и снова. Некоторые из них очевидны. Некоторые противоречат советам, которые вы, вероятно, уже слышали. Советы можно условно разделить на 6 групп.
Замечание: здесь мы рассматриваем PostgreSQL. Те же принципы применимы и к другим БД, хотя точный синтаксис может отличаться.
Группа I. Написание запросов, удобных для индексации
Индекс полезен только в том случае, если ваш запрос позволяет БД его использовать.
1. Разумно используйте индексы
Индексы — самый мощный метод повышения производительности чтения. Это отсортированная структура данных, которая позволяет БД находить строки, не сканируя всю таблицу, подобно тому, как оглавление книги избавляет вас от необходимости пролистывать все страницы.
Создавайте индексы по столбцам, по которым вы чаще всего выполняете фильтрацию, соединение, сортировку и группировку — столбцам в
WHERE, JOIN, ORDER BY и GROUP BY. Когда используется несколько столбцов одновременно, один составной индекс, охватывающий их, намного лучше, чем отдельные индексы по одному столбцу:-- Составной индекс для частой фильтрации по статусу и дате
CREATE INDEX idx_orders_status_order_date
ON orders (status, order_date);
Этот индекс ускоряет запросы, фильтрующие по статусу, а также по статусу и дате заказа. Порядок столбцов имеет значение: индекс по
(status, order_date) помогает запросам, которые сначала фильтруют по статусу, но не запросам, которые фильтруют только по дате заказа.Вы также можете создать покрывающий индекс, который хранит дополнительные значения столбцов внутри индекса. Это полезно для небольших частых запросов на поиск, когда запросу нужны только столбцы, доступные в индексе, чтобы БД могла избежать чтения фактических строк таблицы:
-- Добавляем часто читаемые данные
CREATE INDEX idx_orders_status_order_date_covering
ON orders (status, order_date)
INCLUDE (customer_id, total_amount);
SELECT customer_id, total_amount
FROM orders
WHERE status = 'paid'
AND order_date >= DATE '2026-01-01';
Замечание: индексы не бесплатны. Каждый индекс необходимо обновлять при каждой вставке, обновлении и удалении, и это занимает место на диске. Индексируйте столбцы, которые фактически используются вашими запросами, а не каждый столбец.
2. Избегайте функций в WHERE
Обёртывание столбца в функцию — один из наиболее распространённых способов случайно отключить индекс. Когда вы вызываете функцию для столбца, БД должна вычислить значение функции для каждой строки, прежде чем сможет сравнить его, поэтому она не может использовать индекс для исходного столбца:
-- Плохо: функция по order_date отключает сканирование по индексу
SELECT * FROM orders
WHERE EXTRACT(YEAR FROM order_date) = 2025;
Перепишите условие так, чтобы оно сравнивало исходный столбец с диапазоном:
-- Хорошо: диапазон по чистому значению столбца
SELECT * FROM orders
WHERE order_date >= '2025-01-01'
AND order_date < '2026-01-01';
Оба запроса возвращают одни и те же строки, но только второй может использовать индекс по
order_date.То же правило применяется к
LOWER(email), CAST(…) и арифметическим операциям по столбцу. Если вам часто нужно фильтровать по вычисляемому значению, создайте вместо этого функциональный индекс для этого конкретного выражения.3. Избегайте символов подстановки в начале запроса LIKE
Шаблон LIKE, начинающийся с символа подстановки, не может использовать обычный индекс. БД считывает индекс слева направо, поэтому ей необходимо знать начало значения. Шаблон типа
'%son' скрывает начало и заставляет выполнять полное сканирование:-- Плохо: полное сканирование таблицы
SELECT * FROM customers
WHERE last_name LIKE '%son';
-- Хорошо: сканирование индекса по известному префиксу
SELECT * FROM customers
WHERE last_name LIKE 'Anders%';
Если вам действительно нужно выполнить contains-поиск в тексте, используйте полнотекстовый поиск или триграммный индекс (расширение pg_trgm в PostgreSQL), созданный специально для этой задачи.
4. Точное соответствие типов данных
Сравнение двух разных типов данных заставляет БД преобразовывать один из них, и это преобразование может незаметно отключить индекс.
Если столбец является целым числом, но вы сравниваете его со строкой, или соединяете int-ключ с bigint-ключом, БД добавляет неявное приведение типов — и индекс по исходному столбцу может быть пропущен. Сохраняйте одинаковые типы с обеих сторон каждого соединения и фильтрации:
CREATE TABLE logs (
log_id int PRIMARY KEY,
event_date timestamptz NOT NULL,
user_id int NOT NULL
);
Определите
logs.user_id так, чтобы он соответствовал типу users.id, и тогда соединение будет использовать индекс с обеих сторон. Продолжение следует…
https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
👍14
День 2750. #BestPractices #SQL
Как оптимизировать SQL-запросы. Часть 2
Часть 1
Часть II. Извлекайте только необходимые данные
Быстрее всего обрабатываются данные, которые вы не читаете. Каждый столбец и каждая строка, которые вы извлекаете, требуют операций ввода-вывода на диске, памяти и сетевого времени. Следующие методы позволяют максимально сократить результирующий набор данных на ранней стадии.
1. Прекратите использовать SELECT *
Явное указание столбцов также безопаснее. Ваш запрос не изменит свою форму и не сломается незаметно, когда кто-то добавит или изменит порядок столбцов.
2. Фильтрация на ранних этапах
Чем меньше набор данных, с которым вы работаете, тем быстрее происходит обработка данных на последующих этапах. Возможно, вы слышали распространённый совет: «Применяйте наиболее избирательные фильтры в начале, чтобы сократить количество строк до того, как их обработают соединения и агрегирования».
Замечание: в большинстве случаев не нужно размещать эти фильтры вручную. Современный стоимостной планировщик сам размещает предикаты в
3. Keyset-пагинация вместо OFFSET, когда возможно
Стоимость каждой страницы одинакова, будь то страница 2 или страница 2000, поскольку индекс переходит непосредственно к
Компромисс: keyset-пагинация обеспечивает быструю навигацию по следующей и предыдущей страницам, но вы не можете реализовать случайные переходы к любому номеру страницы.
4. Запрашивайте только то, что изменилось
Повторное чтение всей таблицы при каждом запуске неэффективно, если изменилось всего несколько строк. Отслеживайте «водяной знак» — последнюю обработанную точку — и извлекайте только строки, более новые, чем он:
Это превращает полное сканирование таблицы в небольшое чтение диапазона с помощью индекса. Индексируйте
Продолжение следует…
Источник: https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
Как оптимизировать SQL-запросы. Часть 2
Часть 1
Часть II. Извлекайте только необходимые данные
Быстрее всего обрабатываются данные, которые вы не читаете. Каждый столбец и каждая строка, которые вы извлекаете, требуют операций ввода-вывода на диске, памяти и сетевого времени. Следующие методы позволяют максимально сократить результирующий набор данных на ранней стадии.
1. Прекратите использовать SELECT *
SELECT * извлекает все столбцы, включая те, которые вам не нужны. Это означает больше данных для чтения с диска, больше данных для передачи по сети и больше памяти для хранения — всё это для столбцов, которые ваш код игнорирует. Это также блокирует покрывающие индексы, когда индекс сам по себе может ответить на запрос, не затрагивая таблицу:-- Плохо: извлечение всех столбцов
SELECT * FROM customers;
-- Хорошо: только используемые столбцы
SELECT customer_id, first_name, last_name
FROM customers;
Явное указание столбцов также безопаснее. Ваш запрос не изменит свою форму и не сломается незаметно, когда кто-то добавит или изменит порядок столбцов.
2. Фильтрация на ранних этапах
Чем меньше набор данных, с которым вы работаете, тем быстрее происходит обработка данных на последующих этапах. Возможно, вы слышали распространённый совет: «Применяйте наиболее избирательные фильтры в начале, чтобы сократить количество строк до того, как их обработают соединения и агрегирования».
SELECT o.order_id, o.total
FROM orders o
WHERE o.completed = true
AND o.order_date >= '2026-01-01'
AND o.order_date < '2026-02-01';
Замечание: в большинстве случаев не нужно размещать эти фильтры вручную. Современный стоимостной планировщик сам размещает предикаты в
WHERE как можно раньше и самостоятельно переупорядочивает соединения. Изменение порядка в тексте предложения WHERE редко меняет план. На самом деле помогает предоставление планировщику селективного фильтра и индекса для его применения. Поэтому сосредоточьтесь на том, чтобы сделать фильтр удобным для индекса, а не на том, где он в запросе.3. Keyset-пагинация вместо OFFSET, когда возможно
OFFSET кажется простым способом реализации пагинации, но чем глубже вы углубляетесь, тем медленнее она становится. Чтобы вернуть OFFSET 100000, БД прочитает и отбросит 100 000 строк перед ней. Keyset-пагинация (также называемая поисковой пагинацией) запоминает последнее увиденное значение и переходит непосредственно за него:-- Keyset-пагинация по индексированному столбцу
SELECT * FROM orders
WHERE order_id > 1000
ORDER BY order_id
LIMIT 10;
Стоимость каждой страницы одинакова, будь то страница 2 или страница 2000, поскольку индекс переходит непосредственно к
order_id > 1000.Компромисс: keyset-пагинация обеспечивает быструю навигацию по следующей и предыдущей страницам, но вы не можете реализовать случайные переходы к любому номеру страницы.
4. Запрашивайте только то, что изменилось
Повторное чтение всей таблицы при каждом запуске неэффективно, если изменилось всего несколько строк. Отслеживайте «водяной знак» — последнюю обработанную точку — и извлекайте только строки, более новые, чем он:
-- Читаем только записи, обновлённые с последнего запуска
SELECT * FROM records
WHERE modified_date > '2026-08-10 00:00';
Это превращает полное сканирование таблицы в небольшое чтение диапазона с помощью индекса. Индексируйте
modified_date, сохраняйте новую точку последнего изменения после каждого запуска, и ваша задача синхронизации останется быстрой даже при росте таблицы.Продолжение следует…
Источник: https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
👍4
День 2751. #BestPractices #SQL
Как оптимизировать SQL-запросы. Часть 3
Часть 1
Часть 2
Часть III. Упростите операции соединения и подзапросы
Это место, где запросы становятся дорогостоящими и где скрываются самые большие возможности улучшения. Цель в том, чтобы заставить БД выполнять меньше работы и представить эту работу в форме, с которой она лучше всего справляется.
1. Уменьшайте сложность операций соединения
Каждая операция соединения — это дополнительная работа. Чем меньше таблиц базе нужно coединить, тем быстрее запрос. Распространённая ошибка — соединение таблицы, из которой вы фактически не читаете данные:
Удалите ненужное соединение:
Прочитайте столбцы в
2. Выбирайте правильный тип соединения
Тип соединения влияет как на результат, так и на стоимость. Используйте
3. Заменяйте избыточные подзапросы соединениями (JOIN) или CTE
Подзапрос выполняется для каждой строки, что может быть крайне неэффективно при работе с большим набором результатов. В данном случае подзапрос выполняется для каждого заказа, просто чтобы найти имя клиента:
Простое соединение выполнит ту же работу один раз:
Когда один и тот же подзапрос требуется несколько раз в одном запросе, преобразуйте его в общее табличное выражение (CTE) с помощью оператора
А вот более быстрый запрос, использующий CTE:
4. EXISTS лучше, чем IN
EXISTS может привести к прерыванию обработки: он останавливается на первой совпадающей строке, в то время как IN может сначала сформировать полный список значений:
Примечание: не следует воспринимать «
Продолжение следует…
Источник: https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
Как оптимизировать SQL-запросы. Часть 3
Часть 1
Часть 2
Часть III. Упростите операции соединения и подзапросы
Это место, где запросы становятся дорогостоящими и где скрываются самые большие возможности улучшения. Цель в том, чтобы заставить БД выполнять меньше работы и представить эту работу в форме, с которой она лучше всего справляется.
1. Уменьшайте сложность операций соединения
Каждая операция соединения — это дополнительная работа. Чем меньше таблиц базе нужно coединить, тем быстрее запрос. Распространённая ошибка — соединение таблицы, из которой вы фактически не читаете данные:
-- Плохо: соединение с suppliers, которая не используется
SELECT p.product_name, c.category_name
FROM products p
JOIN categories c ON p.category_id = c.category_id
JOIN suppliers s ON p.supplier_id = s.supplier_id;
Удалите ненужное соединение:
SELECT p.product_name, c.category_name
FROM products p
JOIN categories c ON p.category_id = c.category_id;
Прочитайте столбцы в
SELECT и WHERE и удалите все соединения с таблицами, на которые нет ссылок.2. Выбирайте правильный тип соединения
Тип соединения влияет как на результат, так и на стоимость. Используйте
INNER JOIN, когда нужны совпадающие строки с обеих сторон, LEFT JOIN только тогда, когда действительно нужны и несовпадающие строки, и EXISTS, когда просто нужно узнать, существует ли совпадение:-- Проверка существования: EXISTS останавливается на первом совпадении
SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id
AND o.total > 100
);
LEFT JOIN часто используется там, где подошло бы INNER JOIN — оно заставляет базу сохранять несовпадающие строки, которые будут проигнорированы позже.3. Заменяйте избыточные подзапросы соединениями (JOIN) или CTE
Подзапрос выполняется для каждой строки, что может быть крайне неэффективно при работе с большим набором результатов. В данном случае подзапрос выполняется для каждого заказа, просто чтобы найти имя клиента:
-- Плохо: подзапрос на каждую строку
SELECT o.order_id,
(SELECT c.name FROM customers c
WHERE c.customer_id = o.customer_id) AS customer_name
FROM orders o;
Простое соединение выполнит ту же работу один раз:
-- Хорошо: одно соединение
SELECT o.order_id, c.name AS customer_name
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id;
Когда один и тот же подзапрос требуется несколько раз в одном запросе, преобразуйте его в общее табличное выражение (CTE) с помощью оператора
WITH, чтобы он был написан один раз и его было легче читать. Вот пример медленного запроса:-- Плохо: подзапрос повторяется
SELECT
c.customer_id,
c.name,
(
SELECT SUM(o.total_amount)
FROM orders o
WHERE o.customer_id = c.customer_id
AND o.order_date >= DATE '2026-01-01'
) AS total_spent,
(
SELECT COUNT(*)
FROM orders o
WHERE o.customer_id = c.customer_id
AND o.order_date >= DATE '2026-01-01'
) AS order_count
FROM customers c;
А вот более быстрый запрос, использующий CTE:
-- Хорошо: считаем результат один раз и переиспользуем
WITH customer_order_totals AS (
SELECT
customer_id,
SUM(total_amount) AS total_spent,
COUNT(*) AS order_count
FROM orders
WHERE order_date >= DATE '2026-01-01'
GROUP BY customer_id
)
SELECT
c.customer_id, c.name,
COALESCE(t.total_spent, 0) AS total_spent,
COALESCE(t.order_count, 0) AS order_count
FROM customers c
LEFT JOIN customer_order_totals t ON t.customer_id = c.customer_id;
4. EXISTS лучше, чем IN
EXISTS может привести к прерыванию обработки: он останавливается на первой совпадающей строке, в то время как IN может сначала сформировать полный список значений:
SELECT p.product_id, p.product_name
FROM products p
WHERE EXISTS (
SELECT 1 FROM order_details od
WHERE od.product_id = p.product_id
);
Примечание: не следует воспринимать «
EXISTS всегда лучше IN» как жёсткое правило. В современных версиях PostgreSQL запросы IN, EXISTS и даже некоторые соединения часто переписываются в один и тот же план выполнения, поэтому они могут работать идентично. Однако проблема всё ещё возникает с NOT IN в подзапросе, который может возвращать NULL — это приводит к неожиданным результатам и худшему плану выполнения, поэтому в этом случае предпочтительнее использовать NOT EXISTS. Как всегда, проверяйте план выполнения, а не гадайте.Продолжение следует…
Источник: https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
День 2752. #BestPractices #SQL
Как оптимизировать SQL-запросы. Части 4-5
Части 1, 2, 3
Часть IV. Разрабатывайте схему для чтения
Некоторые запросы работают медленно, независимо от способа их написания, потому что они постоянно пересчитывают один и тот же ресурсоёмкий результат. Решение заключается в изменении структуры данных.
1. Нормализуйте данные с умом
Нормализация поддерживает чистоту и согласованность данных и является правильным вариантом по умолчанию. Однако полностью нормализованные данные могут медленно читаться, когда часто выполняемый запрос должен соединять и агрегировать одни и те же таблицы при каждом запросе. Для путей с интенсивным чтением допустимо денормализовывать данные: предварительно агрегировать данные и сохранять их.
Теперь чтение представляет собой простой поиск, а не агрегацию в реальном времени по всей таблице
2. Использование материализованных представлений
Материализованное представление физически хранит результат запроса, поэтому чтение обращается к предварительно вычисленным строкам, а не пересчитывает их. Это вариант сводной таблицы, описанной выше:
Запросы к
Часть V. Повышение эффективности операций записи и транзакций
Медленная запись и длительные транзакции вызывают конфликты блокировок, заставляя остальные запросы ждать.
1. Пакетная обработка больших операций
Выполнение оператора для каждой строки приводит к перегрузке БД запросами и транзакционными издержками. Но один оператор, затрагивающий миллионы строк, также представляет проблему — он удерживает блокировки в течение длительного времени и может привести к переполнению журнала предварительной записи (WAL).
Промежуточным решением является пакетная обработка: обработка фиксированного фрагмента за раз. Следующий запрос перемещает строки в архивную таблицу по 1000 за раз, удаляя каждый фрагмент после его копирования:
Запустите его в цикле, пока он не станет затрагивать 0 строк. Каждая партия фиксируется быстро, удерживает мало блокировок и поддерживает отзывчивость системы во время выполнения основной задачи.
2. Сокращайте транзакции
Транзакция удерживает блокировки до момента фиксации, и все другие запросы, которым нужны эти строки, должны ждать. Чем дольше транзакция остаётся открытой, тем больше конкуренции она создаёт. Держите транзакцию открытой только для операций записи, а медленные операции — вызовы API, файловый ввод-вывод, ресурсоёмкие вычисления — выполняйте вне её:
Эта транзакция открывается, выполняет две связанные операции записи и немедленно фиксируется. Никогда не оставляйте транзакцию открытой, ожидая ввода пользователя или ответа по сети.
Окончание следует…
Источник: https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
Как оптимизировать SQL-запросы. Части 4-5
Части 1, 2, 3
Часть IV. Разрабатывайте схему для чтения
Некоторые запросы работают медленно, независимо от способа их написания, потому что они постоянно пересчитывают один и тот же ресурсоёмкий результат. Решение заключается в изменении структуры данных.
1. Нормализуйте данные с умом
Нормализация поддерживает чистоту и согласованность данных и является правильным вариантом по умолчанию. Однако полностью нормализованные данные могут медленно читаться, когда часто выполняемый запрос должен соединять и агрегировать одни и те же таблицы при каждом запросе. Для путей с интенсивным чтением допустимо денормализовывать данные: предварительно агрегировать данные и сохранять их.
-- Сохраняем агрегированные данные в сводной таблице
CREATE TABLE sales_summary AS
SELECT product_id, SUM(quantity) AS total_sold
FROM order_details
GROUP BY product_id;
Теперь чтение представляет собой простой поиск, а не агрегацию в реальном времени по всей таблице
order_details. Компромисс заключается в необходимости синхронизации сводной таблицы — её обновления по расписанию или при изменении исходных данных. Денормализацию следует проводить целенаправленно, для конкретных часто используемых запросов, а не повсеместно.2. Использование материализованных представлений
Материализованное представление физически хранит результат запроса, поэтому чтение обращается к предварительно вычисленным строкам, а не пересчитывает их. Это вариант сводной таблицы, описанной выше:
-- Храним агрегированные данные
CREATE MATERIALIZED VIEW mv_total_sales AS
SELECT product_id, SUM(quantity) AS total_qty
FROM order_details
GROUP BY product_id;
-- Уникальный индекс позволяет представлению обновляться без блокирования чтения
CREATE UNIQUE INDEX idx_mv_total_sales_product
ON mv_total_sales (product_id);
-- Обновление по расписанию; читатели будут получать старые данные до завершения обновления
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_total_sales;
Запросы к
mv_total_sales выполняются быстро, потому что агрегация уже была выполнена. Данные актуальны только на момент последнего обновления, поэтому используйте материализованные представления для ресурсоёмких агрегаций, которые могут допускать небольшое устаревание — панели мониторинга, отчёты и таблицы лидеров.Часть V. Повышение эффективности операций записи и транзакций
Медленная запись и длительные транзакции вызывают конфликты блокировок, заставляя остальные запросы ждать.
1. Пакетная обработка больших операций
Выполнение оператора для каждой строки приводит к перегрузке БД запросами и транзакционными издержками. Но один оператор, затрагивающий миллионы строк, также представляет проблему — он удерживает блокировки в течение длительного времени и может привести к переполнению журнала предварительной записи (WAL).
Промежуточным решением является пакетная обработка: обработка фиксированного фрагмента за раз. Следующий запрос перемещает строки в архивную таблицу по 1000 за раз, удаляя каждый фрагмент после его копирования:
WITH batch AS (
DELETE FROM source_table
WHERE ctid IN (
SELECT ctid
FROM source_table
WHERE processed = false
LIMIT 1000
)
RETURNING col1, col2
)
INSERT INTO archive_table (col1, col2)
SELECT col1, col2 FROM batch;
Запустите его в цикле, пока он не станет затрагивать 0 строк. Каждая партия фиксируется быстро, удерживает мало блокировок и поддерживает отзывчивость системы во время выполнения основной задачи.
2. Сокращайте транзакции
Транзакция удерживает блокировки до момента фиксации, и все другие запросы, которым нужны эти строки, должны ждать. Чем дольше транзакция остаётся открытой, тем больше конкуренции она создаёт. Держите транзакцию открытой только для операций записи, а медленные операции — вызовы API, файловый ввод-вывод, ресурсоёмкие вычисления — выполняйте вне её:
BEGIN;
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;
INSERT INTO transactions (account_id, amount)
VALUES (1, -100);
COMMIT;
Эта транзакция открывается, выполняет две связанные операции записи и немедленно фиксируется. Никогда не оставляйте транзакцию открытой, ожидая ввода пользователя или ответа по сети.
Окончание следует…
Источник: https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
👍5
День 2753. #BestPractices #SQL
Как оптимизировать SQL-запросы. Часть 6
Части 1, 2, 3, 4-5
Часть VI. Пусть БД поможет вам: измерение, поддержка и доверие оптимизатору
Планировщик запросов умнее, чем принято считать. Ваша задача — предоставить ему достоверную информацию, а затем проверить результат.
1. Прочитайте план выполнения
План выполнения — это то, как база будет выполнять ваш запрос. Прежде чем что-либо оптимизировать, посмотрите на план. В PostgreSQL команда
Ознакомьтесь с ним, чтобы понять суть дорогостоящих операций: последовательное сканирование большой таблицы обычно означает отсутствие или неиспользуемый индекс, а большой разрыв между расчётным и фактическим количеством строк указывает на устаревшую статистику. План превращает оптимизацию из догадок в подтверждение. Вы исправляете то, что, по данным плана, работает медленно, без гадания.
2. Поддерживайте актуальность статистики
Планировщик выбирает между сканированием и поиском по индексу на основе статистики ваших данных — количества строк, количества уникальных значений и их распределения. Когда эта статистика устарела, планировщик делает неверные предположения и выбирает неверные планы, даже при идеальных индексах.
PostgreSQL использует автоочистку (autovacuum) для поддержания актуальности статистики, но после большой загрузки данных или массового обновления стоит самостоятельно запустить
3. Используйте подсказки запросов экономно
Подсказка запроса заставляет базу выполнять запрос по вашему алгоритму, а не по алгоритму планировщика. PostgreSQL намеренно поставляется без синтаксиса подсказок. Его философия в том, что вы должны исправлять первопричину — индексы, статистику, форму запроса — а не быть умнее планировщика. Вы можете подкрутить его с помощью настроек сессии, но рассматривайте это только как диагностику в среде разработки:
Если вам действительно нужны подсказки, расширение
4. Непрерывный мониторинг и настройка
Оптимизация — не разовая задача. Объём данных растёт, шаблоны доступа меняются, и вчерашний быстрый запрос становится сегодняшним узким местом. Отслеживайте, какие запросы на самом деле обходятся дороже всего. Расширение
Современный планировщик запросов уже выполняет умные переписывания — перенос предикатов вниз, изменение порядка соединений, преобразование
Источник: https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
Как оптимизировать SQL-запросы. Часть 6
Части 1, 2, 3, 4-5
Часть VI. Пусть БД поможет вам: измерение, поддержка и доверие оптимизатору
Планировщик запросов умнее, чем принято считать. Ваша задача — предоставить ему достоверную информацию, а затем проверить результат.
1. Прочитайте план выполнения
План выполнения — это то, как база будет выполнять ваш запрос. Прежде чем что-либо оптимизировать, посмотрите на план. В PostgreSQL команда
EXPLAIN ANALYZE выполняет запрос и сообщает план и что произошло на самом деле:EXPLAIN ANALYZE
SELECT * FROM orders WHERE total > 100;
Ознакомьтесь с ним, чтобы понять суть дорогостоящих операций: последовательное сканирование большой таблицы обычно означает отсутствие или неиспользуемый индекс, а большой разрыв между расчётным и фактическим количеством строк указывает на устаревшую статистику. План превращает оптимизацию из догадок в подтверждение. Вы исправляете то, что, по данным плана, работает медленно, без гадания.
2. Поддерживайте актуальность статистики
Планировщик выбирает между сканированием и поиском по индексу на основе статистики ваших данных — количества строк, количества уникальных значений и их распределения. Когда эта статистика устарела, планировщик делает неверные предположения и выбирает неверные планы, даже при идеальных индексах.
ANALYZE обновляет статистику:ANALYZE orders;
PostgreSQL использует автоочистку (autovacuum) для поддержания актуальности статистики, но после большой загрузки данных или массового обновления стоит самостоятельно запустить
ANALYZE. Движок может оптимизировать запрос настолько хорошо, насколько это позволяют его статистические данные. Хорошая статистика гораздо важнее точной формулировки запроса.3. Используйте подсказки запросов экономно
Подсказка запроса заставляет базу выполнять запрос по вашему алгоритму, а не по алгоритму планировщика. PostgreSQL намеренно поставляется без синтаксиса подсказок. Его философия в том, что вы должны исправлять первопричину — индексы, статистику, форму запроса — а не быть умнее планировщика. Вы можете подкрутить его с помощью настроек сессии, но рассматривайте это только как диагностику в среде разработки:
-- Только для диагностики: посмотреть, как выглядит план без последовательного сканирования
SET enable_seqscan = off;
Если вам действительно нужны подсказки, расширение
pg_hint_plan добавит их — но используйте это как последнюю меру. Подсказка фиксирует решение, которое сегодня кажется правильным, но может оказаться неверным после увеличения объёма данных, так что завтра оно незаметно превратится в медленный запрос.4. Непрерывный мониторинг и настройка
Оптимизация — не разовая задача. Объём данных растёт, шаблоны доступа меняются, и вчерашний быстрый запрос становится сегодняшним узким местом. Отслеживайте, какие запросы на самом деле обходятся дороже всего. Расширение
pg_stat_statements агрегирует статистику выполнения по всей вашей рабочей нагрузке:-- Самые медленные запросы по среднему времени
SELECT query, calls, mean_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;
Современный планировщик запросов уже выполняет умные переписывания — перенос предикатов вниз, изменение порядка соединений, преобразование
IN в JOIN. Вы мало выиграете, вручную настраивая текст предложения WHERE. Большая выгода достигается за счёт предоставления планировщику того, что ему нужно: селективных предикатов, актуальной статистики и правильных индексов — а затем анализа плана для подтверждения.Источник: https://antondevtips.com/blog/how-to-optimize-sql-queries-20-proven-best-practices
👍5
День 2754. #Оффтоп #AI
Программисты разучились думать?
Доброй субботы вам, дорогие подписчики.
Сегодня порекомендую вам видео от Фила Ранжина на хайповую тему «Программисты разучились думать – во всём виноват ИИ».
- Как ИИ влияет на когнитивные способности? Действительно ли мы меньше думаем, когда пользуемся ИИ?
- Суперпроизводительность с ИИ. Правда ли агенты сделали нас 10х инженерами?
- Мы пишем гораздо больше кода с помощью ИИ. А нужен ли весь этот код?
- Какие навыки атрофируются у разработчиков и насколько это опасно?
- Как ИИ влияет на командную работу и как на это всё смотрит бизнес?
- Что делать с тоннами нейрослопа, которые принёс тебе коллега на ревью?
- Стратегии борьбы с «ИИ-слабоумием». К чему всё это приведёт в будущем?
В общем, по-моему, довольно интересный ролик, заставляющий задуматься о своём месте в этом несущемся вперёд с дикой скоростью мире ИТ.
Приятного просмотра.
https://www.youtube.com/watch?v=hU82W64jn9I
Программисты разучились думать?
Доброй субботы вам, дорогие подписчики.
Сегодня порекомендую вам видео от Фила Ранжина на хайповую тему «Программисты разучились думать – во всём виноват ИИ».
- Как ИИ влияет на когнитивные способности? Действительно ли мы меньше думаем, когда пользуемся ИИ?
- Суперпроизводительность с ИИ. Правда ли агенты сделали нас 10х инженерами?
- Мы пишем гораздо больше кода с помощью ИИ. А нужен ли весь этот код?
- Какие навыки атрофируются у разработчиков и насколько это опасно?
- Как ИИ влияет на командную работу и как на это всё смотрит бизнес?
- Что делать с тоннами нейрослопа, которые принёс тебе коллега на ревью?
- Стратегии борьбы с «ИИ-слабоумием». К чему всё это приведёт в будущем?
В общем, по-моему, довольно интересный ролик, заставляющий задуматься о своём месте в этом несущемся вперёд с дикой скоростью мире ИТ.
Приятного просмотра.
https://www.youtube.com/watch?v=hU82W64jn9I
YouTube
Программисты разучились думать — во всем виноват ИИ - Фил Ранжин
Разбираем, почему разработчики разучились думать и как не стать бесплатным придатком к нейросети
Наш курс "Как стать известным в индустрии IT: статьи, подкасты, видео, выступления, соц. сети"
https://it-media-kurs.framer.media/
Присоединяйтесь к чату, ответим…
Наш курс "Как стать известным в индустрии IT: статьи, подкасты, видео, выступления, соц. сети"
https://it-media-kurs.framer.media/
Присоединяйтесь к чату, ответим…
👎3
День 2755. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
43. Устойчивость и обработка кратковременных сбоев
«Расскажите, как бы вы реализовали устойчивость и обработку кратковременных сбоев. Приведите примеры методов и инструментов, которые вы бы использовали, чтобы приложение могло корректно обрабатывать сбои и восстанавливаться после них».
Хороший ответ
При создании отказоустойчивых приложений крайне важно предвидеть и эффективно обрабатывать кратковременные сбои. Это гарантирует, что приложение останется отзывчивым и доступным даже в неблагоприятных условиях. Вот шаги и инструменты, которые я бы использовал:
- Политики повторных попыток: пакет Microsoft.Extensions.Http.Polly обеспечивает устойчивость и обработку кратковременных сбоев путём реализации политики повторных попыток. Это помогает в ситуациях, когда операции могут иногда завершаться с ошибкой из-за временных проблем, таких как проблемы с сетевым подключением или тайм-аутом сервера:
- Паттерн Прерыватель Цепи (Circuit Breaker): предотвращает повторные сбои из-за одной и той же ошибки. Этот паттерн полезен для обработки случаев, когда продолжение выполнения действия может нанести системе больший вред. Polly также можно настроить для работы с ним:
- Тайм-ауты: явные тайм-ауты для всех внешних HTTP-запросов гарантируют, что приложение не зависнет на неопределённое время, если сервис, от которого оно зависит, работает медленно или не отвечает.
- Резервные (Fallback) политики: для предоставления значений по умолчанию или упрощённых ответов в случае сбоя вызова внешнего сервиса:
Преимущества
- Благодаря эффективной обработке кратковременных сбоев приложение остается более доступным и отзывчивым.
- Обеспечивает более стабильный и надёжный пользовательский опыт.
- Помогает предотвратить каскадные сбои (например, перегрузку замедлившегося API запросами) из-за неполадок в одной части системы.
Часто встречающийся плохой ответ
«Нужно просто поставить повыше значение тайм-аута для внешних вызовов, чтобы дать сервисам достаточно времени для реагирования и снизить вероятность сбоев.»
Почему это неправильно
- Непонимание обработки ошибок: простое увеличение значений тайм-аута не устраняет первопричины кратковременных сбоев и может привести к ухудшению пользовательского опыта из-за увеличения времени ожидания.
- Отсутствие комплексной стратегии: этот подход не учитывает возможность внедрения комплексной стратегии повторных попыток, прерываний и резервных ответов для эффективной обработки различных типов сбоев.
- Потенциальная повышенная нагрузка на систему: более длительные тайм-ауты могут усугубить проблемы, занимая системные ресурсы на длительные периоды.
Эта ошибка обычно является результатом недостаточного понимания принципов обработки кратковременных ошибок или чрезмерно упрощённого подхода к обработке ошибок и исключений.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
43. Устойчивость и обработка кратковременных сбоев
«Расскажите, как бы вы реализовали устойчивость и обработку кратковременных сбоев. Приведите примеры методов и инструментов, которые вы бы использовали, чтобы приложение могло корректно обрабатывать сбои и восстанавливаться после них».
Хороший ответ
При создании отказоустойчивых приложений крайне важно предвидеть и эффективно обрабатывать кратковременные сбои. Это гарантирует, что приложение останется отзывчивым и доступным даже в неблагоприятных условиях. Вот шаги и инструменты, которые я бы использовал:
- Политики повторных попыток: пакет Microsoft.Extensions.Http.Polly обеспечивает устойчивость и обработку кратковременных сбоев путём реализации политики повторных попыток. Это помогает в ситуациях, когда операции могут иногда завершаться с ошибкой из-за временных проблем, таких как проблемы с сетевым подключением или тайм-аутом сервера:
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddHttpClient("ApiClient", c =>
{
c.BaseAddress = new Uri("https://apiurl.com");
})
.AddTransientHttpErrorPolicy(p =>
p.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
})
);
var app = builder.Build();
app.MapGet("/external-api",
async (IHttpClientFactory hcf) =>
{
var client = hcf.CreateClient("ApiClient");
var response = await client.GetAsync("/data");
return resp.IsSuccessStatusCode
? Results.Ok(await resp.Content.ReadAsStringAsync())
: Results.Problem();
});
app.Run();
- Паттерн Прерыватель Цепи (Circuit Breaker): предотвращает повторные сбои из-за одной и той же ошибки. Этот паттерн полезен для обработки случаев, когда продолжение выполнения действия может нанести системе больший вред. Polly также можно настроить для работы с ним:
.AddTransientHttpErrorPolicy(p =>
p.CircuitBreakerAsync(3, TimeSpan.FromMinutes(1)));
- Тайм-ауты: явные тайм-ауты для всех внешних HTTP-запросов гарантируют, что приложение не зависнет на неопределённое время, если сервис, от которого оно зависит, работает медленно или не отвечает.
- Резервные (Fallback) политики: для предоставления значений по умолчанию или упрощённых ответов в случае сбоя вызова внешнего сервиса:
.AddTransientHttpErrorPolicy(p =>
p.Or<TimeoutException>()
.FallbackAsync(
new HttpResponseMessage(HttpStatusCode.OK)
{ Content = new StringContent("Резервный ответ") })
);
Преимущества
- Благодаря эффективной обработке кратковременных сбоев приложение остается более доступным и отзывчивым.
- Обеспечивает более стабильный и надёжный пользовательский опыт.
- Помогает предотвратить каскадные сбои (например, перегрузку замедлившегося API запросами) из-за неполадок в одной части системы.
Часто встречающийся плохой ответ
«Нужно просто поставить повыше значение тайм-аута для внешних вызовов, чтобы дать сервисам достаточно времени для реагирования и снизить вероятность сбоев.»
Почему это неправильно
- Непонимание обработки ошибок: простое увеличение значений тайм-аута не устраняет первопричины кратковременных сбоев и может привести к ухудшению пользовательского опыта из-за увеличения времени ожидания.
- Отсутствие комплексной стратегии: этот подход не учитывает возможность внедрения комплексной стратегии повторных попыток, прерываний и резервных ответов для эффективной обработки различных типов сбоев.
- Потенциальная повышенная нагрузка на систему: более длительные тайм-ауты могут усугубить проблемы, занимая системные ресурсы на длительные периоды.
Эта ошибка обычно является результатом недостаточного понимания принципов обработки кратковременных ошибок или чрезмерно упрощённого подхода к обработке ошибок и исключений.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👍5
День 2756. #ЧтоНовенького #NET11 #CSharp15
Вышли Превью 6 и 7 .NET 11
Посмотрим на самые интересные новинки в языке C#.
1. Индексаторы-расширения
Члены-расширения теперь включают индексаторы, что позволяет разработчикам добавлять доступ к существующему типу из блока расширения. Т.е. можно добавлять доступ
2. Метки break и continue
Теперь функции
Аналогично для
Метка должна быть прикреплена непосредственно к оператору
3. Шаблоны объединений сопоставляют объединение или его значение
Теперь для типов объединений используется подход сопоставления «попробуй оба варианта»: при применении шаблона к значению объединения компилятор сначала проверяет шаблон на соответствие самому экземпляру объединения, а если эта проверка не удалась, то и на соответствие содержащемуся в объединении значению. Это согласовывает семантику сопоставления с обновлённым спецификатором объединений и применяется к шаблонам типа, переменной, объявления, списка и рекурсивным шаблонам:
4. Полнота проверки параметров типа, ограниченных закрытым типом
Полнота проверки параметров с помощью
Компилятор решает, что
Источники:
- https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/preview6/csharp.md
- https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/preview7/csharp.md
Вышли Превью 6 и 7 .NET 11
Посмотрим на самые интересные новинки в языке C#.
1. Индексаторы-расширения
Члены-расширения теперь включают индексаторы, что позволяет разработчикам добавлять доступ к существующему типу из блока расширения. Т.е. можно добавлять доступ
this[…] к типу из блока расширения. Индексатор-расширение объявляется как индексатор экземпляра внутри блока расширения. В следующем примере добавляется индексация с конца к IReadOnlyList<T>, в котором определён индексатор this[int], но нет индексатора this[Index]:using System;
using System.Collections.Generic;
IReadOnlyList<string> log = ["start", "work", "done"];
Console.WriteLine(log[^1]); // done
Console.WriteLine(log[^2]); // work
public static class ReadOnlyListExtensions
{
extension<T>(IReadOnlyList<T> list)
{
public T this[Index i] =>
list[i.GetOffset(list.Count)];
}
}
2. Метки break и continue
Теперь функции
break и continue могут указывать на окружающий их цикл или оператор switch, поэтому вы можете выйти из внешней конструкции или продолжить её выполнение непосредственно из внутренней, не передавая состояние через флаг и не прибегая к goto. Добавьте метку к целевому циклу, а затем ссылайтесь на него из break или continue:string? found = null;
outer: for (int x = 0; x < xMax; x++)
{
for (int y = 0; y < yMax; y++)
{
if (GetValue(x, y) is { } value && value == target)
{
found = value;
break outer;
}
}
}
Аналогично для
continue:row: for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
if (ShouldSkipRestOfRow(i, j)) continue row;
Process(i, j);
}
}
Метка должна быть прикреплена непосредственно к оператору
for, foreach, while, do или switch, а break/continue с меткой должен находиться внутри этого оператора.3. Шаблоны объединений сопоставляют объединение или его значение
Теперь для типов объединений используется подход сопоставления «попробуй оба варианта»: при применении шаблона к значению объединения компилятор сначала проверяет шаблон на соответствие самому экземпляру объединения, а если эта проверка не удалась, то и на соответствие содержащемуся в объединении значению. Это согласовывает семантику сопоставления с обновлённым спецификатором объединений и применяется к шаблонам типа, переменной, объявления, списка и рекурсивным шаблонам:
public record class Dog(string Name);
public record class Cat(int Lives);
public union Pet(Dog, Cat);
Pet pet = new Cat(9);
// Сопоставление с типом объединения
if (pet is Pet) // true
Console.WriteLine("Это питомец");
// Сопоставление со значением
if (pet is Cat { Lives: > 0 } cat) // true
Console.WriteLine($"У кошки {cat.Lives} жизней");
4. Полнота проверки параметров типа, ограниченных закрытым типом
Полнота проверки параметров с помощью
switch теперь учитывает параметры обобщённых типов, ограниченные закрытым типом. Когда обрабатывается каждый прямой подтип закрытого базового типа, компилятор больше не предупреждает, что оператор switch не является исчерпывающим — даже когда входные данные указываются в качестве параметра типа:public closed record class Shape;
public record class Circle(double Radius) : Shape;
public record class Square(double Side) : Shape;
static double Area<T>(T shape)
where T : Shape => shape switch
{
Circle(var r) => Math.PI * r * r,
Square(var s) => s * s
};
Компилятор решает, что
T не может вводить никаких дополнительных случаев, поскольку каждый производный от Shape тип уже охвачен.Источники:
- https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/preview6/csharp.md
- https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/preview7/csharp.md
👍11
День 2757. #ЗаметкиНаПолях #DDD
Сохраняем Богатую Доменную Модель в EF Core. Начало
С богатой доменной моделью всегда была проблема. В теории это хорошо, но EF Core нужны публичные сеттеры и публичный конструктор без параметров. ORM вроде как навязывает нам анемичную модель. Однако сейчас это не так: EF Core с удовольствием сохранит полностью инкапсулированный агрегат, и результатом станет доменная модель, которая обеспечивает соблюдение инвариантов в одном месте, а ORM будет делать свою часть работы. Проанализируем агрегат от начала до конца и рассмотрим все места, где, предположительно, EF Core и инкапсуляция могут конфликтовать.
Агрегат, который мы хотим сохранить
Наш домен - домашняя пивоварня (потому что заказы товаров уже надоели). Batch - партия брожения. Вы измеряете плотность (gravity) в
Дочерние сущности агрегата – записи, поэтому равенство по значению достаётся нам бесплатно.
Три особенности здесь напрямую заимствованы из архитектуры агрегатов:
1.
2.
3.
Каждая из особенностей, предположительно, «ломает» EF Core, поэтому далее рассмотрим их по порядку.
Продолжение следует…
Источник: https://milanjovanovic.tech/blog/persisting-a-rich-domain-model-with-ef-core
Сохраняем Богатую Доменную Модель в EF Core. Начало
С богатой доменной моделью всегда была проблема. В теории это хорошо, но EF Core нужны публичные сеттеры и публичный конструктор без параметров. ORM вроде как навязывает нам анемичную модель. Однако сейчас это не так: EF Core с удовольствием сохранит полностью инкапсулированный агрегат, и результатом станет доменная модель, которая обеспечивает соблюдение инвариантов в одном месте, а ORM будет делать свою часть работы. Проанализируем агрегат от начала до конца и рассмотрим все места, где, предположительно, EF Core и инкапсуляция могут конфликтовать.
Агрегат, который мы хотим сохранить
Наш домен - домашняя пивоварня (потому что заказы товаров уже надоели). Batch - партия брожения. Вы измеряете плотность (gravity) в
AddReading(), и как только она стабилизируется, разливаете пиво по бутылкам в Bottle(). Вот модель партии, без публичных сеттеров, создающаяся через фабричный метод Start() и изменяющая состояние через методы:public sealed class Batch
{
private readonly List<FermentationReading>
_readings = [];
private readonly List<IDomainEvent>
_events = [];
private DateTime? _bottledAt;
private Batch() { } // Для EF Core
private Batch(BatchId id,
RecipeId recipeId, Volume volume)
{
Id = id;
RecipeId = recipeId;
Volume = volume;
Status = Status.Fermenting;
}
public BatchId Id { get; private set; }
public RecipeId RecipeId { get; private set; }
public BatchStatus Status { get; private set; }
public Volume Volume { get; private set; }
public IReadOnlyCollection<FermentationReading>
Readings => _readings.AsReadOnly();
public IReadOnlyCollection<IDomainEvent>
DomainEvents => _events.AsReadOnly();
public static Batch Start(
RecipeId recipeId, Volume volume) =>
new(new BatchId(Guid.CreateVersion7()), recipeId, volume);
public void AddReading(
Gravity gravity, DateTime takenAt)
{
if (Status != Status.Fermenting)
throw new DomainException("Измерения доступны только в процессе ферментации.");
_readings.Add(new FermentationReading(gravity, takenAt));
}
public void Bottle(TimeProvider tp)
{
if (Status != Status.Fermenting)
throw new DomainException("Только ферментированная партия может быть бутилирована.");
Gravity[] lastTwo = _readings
.OrderBy(r => r.TakenAt)
.TakeLast(2)
.Select(r => r.Gravity)
.ToArray();
if (lastTwo.Length < 2 || lastTwo[0] != lastTwo[1])
throw new DomainException("Плотность должна быть одинаковой в последних 2х измерениях.");
Status = Status.Bottled;
_bottledAt = tp.GetUtcNow().UtcDateTime;
_events.Add(new BatchBottled(Id));
}
public void ClearDomainEvents() => _events.Clear();
}
public readonly record struct BatchId(Guid Value);
public readonly record struct RecipeId(Guid Value);
public readonly record struct Gravity(decimal Value);
public sealed record Volume(decimal Amount, string Unit);
public sealed record FermentationReading(decimal Gravity, DateTime TakenAt);
Дочерние сущности агрегата – записи, поэтому равенство по значению достаётся нам бесплатно.
Три особенности здесь напрямую заимствованы из архитектуры агрегатов:
1.
RecipeId ссылается на другой агрегат только по ID;2.
FermentationReading — дочерняя сущность, которая существует и исчезает вместе с агрегатом (Batch);3.
_bottledAt — приватное поле без каких-либо свойств.Каждая из особенностей, предположительно, «ломает» EF Core, поэтому далее рассмотрим их по порядку.
Продолжение следует…
Источник: https://milanjovanovic.tech/blog/persisting-a-rich-domain-model-with-ef-core
👍9👎1
День 2758. #ЗаметкиНаПолях #DDD
Сохраняем Богатую Доменную Модель в EF Core. Продолжение
Начало
Теперь разберём, как EF Core сохраняет и загружает такой богатый агрегат (см. код в предыдущем посте).
Приватные конструкторы и приватные сеттеры просто работают
Когда EF Core материализует сущность, он не использует публичный API. Он вызывает приватный конструктор без параметров и записывает данные в свойства через их базовые поля, приватные сеттеры и т.п.. Поэтому пустой приватный конструктор - единственная уступка, которую доменная модель делает ORM. Загрузка и изменение партии выглядят обычно:
Трекер изменений считывает те же самые базовые поля, поэтому приватные сеттеры ничего не скрывают от
EF может даже использовать привязку через параметризованный конструктор, сопоставляя параметры с отображаемыми свойствами по имени и типу, независимо от того, приватные они или нет. Но это работает только для скалярных типов. Свойства навигации не могут быть привязаны через конструктор, поэтому агрегату с коллекцией по-прежнему нужен конструктор без параметров. EF также пропускает фабричные валидации при загрузке, что правильно: повторное выполнение валидации во время материализации сделало бы старые объекты недоступными для загрузки при изменении правил в будущем.
Типизированные ID и ссылки на другие агрегаты
Инкапсулированная коллекция
По соглашению, EF находит поле
Состояние без свойств
Однако, чтобы отфильтровать по этому полю, придётся добавлять в запрос «костыль»:
Поэтому, если в проекте есть фильтрация по полю, лучше добавить свойство с приватным сеттером, а если полем пользуется только агрегат, можно оставить его без свойства.
Окончание следует…
Источник: https://milanjovanovic.tech/blog/persisting-a-rich-domain-model-with-ef-core
Сохраняем Богатую Доменную Модель в EF Core. Продолжение
Начало
Теперь разберём, как EF Core сохраняет и загружает такой богатый агрегат (см. код в предыдущем посте).
Приватные конструкторы и приватные сеттеры просто работают
Когда EF Core материализует сущность, он не использует публичный API. Он вызывает приватный конструктор без параметров и записывает данные в свойства через их базовые поля, приватные сеттеры и т.п.. Поэтому пустой приватный конструктор - единственная уступка, которую доменная модель делает ORM. Загрузка и изменение партии выглядят обычно:
Batch batch = await context.Batches
.SingleAsync(b => b.Id == batchId);
batch.AddReading(new Gravity(1.012m), timeProvider.GetUtcNow().UtcDateTime);
await context.SaveChangesAsync();
Трекер изменений считывает те же самые базовые поля, поэтому приватные сеттеры ничего не скрывают от
SaveChanges.EF может даже использовать привязку через параметризованный конструктор, сопоставляя параметры с отображаемыми свойствами по имени и типу, независимо от того, приватные они или нет. Но это работает только для скалярных типов. Свойства навигации не могут быть привязаны через конструктор, поэтому агрегату с коллекцией по-прежнему нужен конструктор без параметров. EF также пропускает фабричные валидации при загрузке, что правильно: повторное выполнение валидации во время материализации сделало бы старые объекты недоступными для загрузки при изменении правил в будущем.
Типизированные ID и ссылки на другие агрегаты
BatchId и RecipeId — строго типизированные идентификаторы, сопоставляемые с помощью преобразования значений:public sealed class BatchConfiguration
: IEntityTypeConfiguration<Batch>
{
public void Configure(EntityTypeBuilder<Batch> bld)
{
bld.ToTable("batches");
bld.HasKey(b => b.Id);
bld.Property(b => b.Id)
.HasConversion(id => id.Value,
value => new BatchId(value))
.ValueGeneratedNever();
bld.Property(b => b.RecipeId)
.HasConversion(id => id.Value,
value => new RecipeId(value));
}
}
ValueGeneratedNever важно: мы генерируем Guid.CreateVersion7() в фабрике, поэтому говорим EF не вмешиваться.RecipeId не является свойством навигации для Recipe. Рецепт — это самостоятельный агрегат, но здесь он нам не нужен.Инкапсулированная коллекция
По соглашению, EF находит поле
_readings для навигации по Readings. Но его лучше настроить явно, чтобы сопоставление сохранялось даже после переименования:// продолжение BatchConfiguration.Configure()
…
bld.HasMany<FermentationReading>("_readings")
.WithOne()
.HasForeignKey("batch_id");
bld.Navigation("_readings")
.UsePropertyAccessMode(PropertyAccessMode.Field)
.AutoInclude();
PropertyAccessMode.Field указывает EF читать и записывать поле, не обращаясь к публичному представлению Readings. AutoInclude() важно добавлять, т.к. метод Bottle() читает _readings, поэтому использование частично загруженного Batch небезопасно. WithOne() без аргументов означает, что дочерний элемент не имеет навигации обратно к Batch; внешний ключ batch_id существует только как теневое свойство.Состояние без свойств
_bottledAt не имеет свойств, только приватное поле, и EF всё равно его сопоставляет:bld.Property<DateTime?>("_bottledAt")
.HasColumnName("bottled_at");Однако, чтобы отфильтровать по этому полю, придётся добавлять в запрос «костыль»:
var thisWeek = await context.Batches
.Where(b => EF.Property<DateTime?>(b, "_bottledAt") >= weekAgo)
.ToListAsync();
Поэтому, если в проекте есть фильтрация по полю, лучше добавить свойство с приватным сеттером, а если полем пользуется только агрегат, можно оставить его без свойства.
Окончание следует…
Источник: https://milanjovanovic.tech/blog/persisting-a-rich-domain-model-with-ef-core
👎2👍1
День 2759. #ЗаметкиНаПолях #DDD
Сохраняем Богатую Доменную Модель в EF Core. Окончание
Начало
Продолжение
Продолжаем разбирать, как EF Core сохраняет и загружает богатый агрегат (см. код в предыдущем посте).
Объекты-значения: принадлежащие (Owned), комплексные (Complex) типы и преобразования
Комплексные типы были добавлены в EF Core 8 (без необязательных свойств и коллекций); в EF Core 10 эти ограничения сняты. В EF 8 или 9 коллекцию объектов-значений можно преобразовать в коллекцию принадлежащего типа, которая работает везде (но содержит скрытый теневой ключ, т.к. EF рассматривает их как сущности, притворяющиеся значениями). Допустим, наш Batch имел бы коллекцию объектов-значений - лог добавлений хмеля, тогда в EF 8 и 9 мы могли бы связать его с отдельной таблицей:
Перечисления конвертируются в простой текст, так что БД остаётся читаемой:
Преобразованные свойства имеют один общий недостаток: LINQ работает с типом провайдера, поэтому сортировка по статусу даёт алфавитный порядок строк (Bottled, Dumped, Fermenting), а не порядок задуманного нами жизненного цикла.
События домена не должны попадать в схему
IDomainEvent не является сущностью, поэтому укажите EF игнорировать коллекцию DomainEvents:
Конечно, доменные события должны быть обработаны где-то. И лучшее место для этого – перехватчик сохранения изменений:
Домен не ссылается на EF Core
Все конфигурации сопоставлений находятся в
DbContext находится на уровне инфраструктуры и получает всю конфигурацию из своей сборки:
Итого
Возражение о том, что «EF Core заставляет создавать анемичные модели», устарело много лет назад. Приватный конструктор, вспомогательные поля, преобразование значений и комплексные типы покрывают все потребности полностью инкапсулированного агрегата, и всё это находится в классах конфигурации, которые домен никогда не видит. Реальные ограничения сводятся к двум:
- свойства сложных типов и свойства навигации не могут быть привязаны через параметры конструктора,
- преобразованные поля или поля без свойств транслируются в тип провайдера в запросах.
ORM никогда не заставлял вашу доменную модель быть анемичной. Это задача сопоставления, выполняемая один раз для каждого агрегата.
Источник: https://milanjovanovic.tech/blog/persisting-a-rich-domain-model-with-ef-core
Сохраняем Богатую Доменную Модель в EF Core. Окончание
Начало
Продолжение
Продолжаем разбирать, как EF Core сохраняет и загружает богатый агрегат (см. код в предыдущем посте).
Объекты-значения: принадлежащие (Owned), комплексные (Complex) типы и преобразования
Volume — объект-значение, а многосвойственные объекты-значения отображаются как комплексные типы, хранящие свои элементы непосредственно в таблице владельца (без объединения, без отдельной идентификации):// продолжение BatchConfiguration.Configure()
…
bld.ComplexProperty(b => b.Volume, vol =>
{
vol.Property(v => v.Amount)
.HasColumnName("vol_amount");
vol.Property(v => v.Unit)
.HasColumnName("vol_unit");
});
Комплексные типы были добавлены в EF Core 8 (без необязательных свойств и коллекций); в EF Core 10 эти ограничения сняты. В EF 8 или 9 коллекцию объектов-значений можно преобразовать в коллекцию принадлежащего типа, которая работает везде (но содержит скрытый теневой ключ, т.к. EF рассматривает их как сущности, притворяющиеся значениями). Допустим, наш Batch имел бы коллекцию объектов-значений - лог добавлений хмеля, тогда в EF 8 и 9 мы могли бы связать его с отдельной таблицей:
bld.OwnsMany(b => b.HopAdditions, hop =>
{
hop.ToTable("hop_additions");
hop.WithOwner()
.HasForeignKey("batch_id");
});
Перечисления конвертируются в простой текст, так что БД остаётся читаемой:
bld.Property(b => b.Status)
.HasConversion<string>()
.HasMaxLength(20);
Преобразованные свойства имеют один общий недостаток: LINQ работает с типом провайдера, поэтому сортировка по статусу даёт алфавитный порядок строк (Bottled, Dumped, Fermenting), а не порядок задуманного нами жизненного цикла.
События домена не должны попадать в схему
IDomainEvent не является сущностью, поэтому укажите EF игнорировать коллекцию DomainEvents:
bld.Ignore(b => b.DomainEvents);
Конечно, доменные события должны быть обработаны где-то. И лучшее место для этого – перехватчик сохранения изменений:
public class DomainEventsInterceptor(
// внедряем диспетчер доменных событий
) : SaveChangesInterceptor
{
public override async ValueTask<int> SavedChangesAsync(
SaveChangesCompletedEventData data,
int result,
CancellationToken ct = default)
{
var events = data.Context!.ChangeTracker
.Entries<Batch>()
.SelectMany(e =>
{
var ev = e.Entity.DomainEvents.ToList();
e.Entity.ClearDomainEvents();
return ev;
})
.ToList();
// … обрабатываем события с помощью диспетчера …
return await base.SavedChangesAsync(data, result, ct);
}
}
Домен не ссылается на EF Core
Все конфигурации сопоставлений находятся в
BatchConfiguration, ни один из них не находится в Batch: никаких атрибутов сопоставления, никакого базового класса ORM! DbContext находится на уровне инфраструктуры и получает всю конфигурацию из своей сборки:
public sealed class BreweryDbContext(
DbContextOptions<BreweryDbContext> opts)
: DbContext(opts)
{
public DbSet<Batch> Batches => Set<Batch>();
protected override void OnModelCreating(ModelBuilder mb)
{
mb.ApplyConfigurationsFromAssembly(
typeof(BreweryDbContext).Assembly);
}
}
Итого
Возражение о том, что «EF Core заставляет создавать анемичные модели», устарело много лет назад. Приватный конструктор, вспомогательные поля, преобразование значений и комплексные типы покрывают все потребности полностью инкапсулированного агрегата, и всё это находится в классах конфигурации, которые домен никогда не видит. Реальные ограничения сводятся к двум:
- свойства сложных типов и свойства навигации не могут быть привязаны через параметры конструктора,
- преобразованные поля или поля без свойств транслируются в тип провайдера в запросах.
ORM никогда не заставлял вашу доменную модель быть анемичной. Это задача сопоставления, выполняемая один раз для каждого агрегата.
Источник: https://milanjovanovic.tech/blog/persisting-a-rich-domain-model-with-ef-core
👍3👎1
День 2760. #Карьера
Основные Правила Разработки ПО. Начало
Я уже не раз приводил здесь подобные советы от опытных разработчиков. Gérald Barré (aka meziantou) также составил свой список.
1. Вам платят не за код, а за решение проблем
Написание кода — не цель, а средство её достижения. Ваша ценность как инженера в понимании бизнес-проблем и предоставлении решений, которые создают ценность для пользователей и организации.
Иногда лучшее решение - отсутствие кода. Возможно, изменение процесса сработает лучше, существующий инструмент можно настроить по-другому, а может проблему и не нужно решать. Прежде чем приступать к реализации, уделите время пониманию сути проблемы, проанализируйте ситуацию, исходя из желаемого результата, и рассмотрите все возможные подходы.
2. Нет «лучшего» решения, есть только компромиссы
Разработка ПО – это принятие решений на основе неполной информации. У каждого вашего выбора есть плюсы и минусы, и то, что подходит для одного проекта, может оказаться ужасным для другого.
- Микросервисы? Зависит от размера команды, потребностей в масштабируемости и операционной сложности, которая вам под силу.
- SQL/NoSQL? Зависит от структуры данных, шаблонов запросов и требований к согласованности.
- TDD? Зависит от контекста, опыта команды и ограничений проекта.
Опытный инженер не ищет «лучшее» решение. Он оценивает компромиссы, исходя из текущих ограничений: навыков команды, сроков, бюджета, потребностей в масштабируемости и требований к сопровождению. Задокументируйте, почему вы выбрали тот или иной вариант. В будущем вы (или коллеги) оценят понимание контекста, лежащего в основе принятых решений. Всегда знайте, что именно вы оптимизируете: производительность, удобство сопровождения, время выхода на рынок, скорость работы команды и т.п.
Когда вы ходите по кругу, не находя чёткого «хорошего решения», это обычно означает, что вы недостаточно определили ограничения для проблемы. Добавьте больше ограничений: бюджет, дедлайн, размер команды, допустимый уровень технического долга, ожидаемый масштаб.
3. Чем меньше кода, тем лучше
Больше кода - больше работы по поддержке, тестированию, отладке и поиску ошибок. Лучший код часто тот, который вы не написали. Каждая строка должна оправдывать своё существование.
Прежде чем добавлять новую функцию или абстракцию, спросите себя: действительно ли это необходимо? Могу ли я решить это с помощью существующего кода? Решаю ли я существующую проблему или ту, которая, как я думаю, может возникнуть в будущем? Немедленно удаляйте неиспользуемый код. Мёртвый код создает путаницу, увеличивает нагрузку на поддержку и создаёт ложное представление о том, что система на самом деле делает.
4. Каждая зависимость — это обуза
Зависимости могут быть заброшены, содержать уязвимости безопасности, незаметно ломаться при обновлениях или приводить к появлению транзитивных зависимостей, которые вы никогда не планировали включать, а также могут препятствовать обновлению других пакетов из-за несовместимости.
Регулярно проверяйте зависимости. Удаляйте неиспользуемые. Оценивайте состояние проектов, от которых вы зависите (последние коммиты, активные сопровождающие, история безопасности). Учитывайте общую стоимость владения, а не только первоначальное удобство.
5. Выпускайте рано, итерируйте часто
Идеальность — враг завершённости. Чем дольше вы оттягиваете выпуск, тем дольше откладываете получение реальной обратной связи от пользователей. Сначала сделайте, чтоб работало, затем сделайте это правильно, а затем сделайте это быстрым.
Ранние релизы помогают проверить предположения, выяснить, что действительно нужно пользователям (в отличие от того, что вы думаете, что им нужно), и скорректировать курс. Выбрасывать код больно, но ещё больнее потратить полгода на создание неправильного продукта.
Опросы пользователей, прототипы и моки могут проверить идеи до написания кода. Иногда простой разговор показывает, что запланированная вами функция совсем не то, что нужно пользователям. Внедряйте механизмы обратной связи в процесс разработки, чтобы расти как инженер.
6. Проектируйте с учётом изменений, т.к. ПО никогда не бывает завершённым
Потребности пользователей и технологии меняются, платформы обновляются, и обнаруживаются уязвимости безопасности. Сделайте так, чтобы ПО было легко модифицировать, расширять и поддерживать. Создавайте с учётом расширяемости и ясности. Проводите рефакторинг непрерывно и постепенно, а не ждите «большой переработки». Сочетайте принцип YAGNI с продуманным проектированием, которое не загонит вас в тупик. Делайте изменения по возможности обратимыми, чтобы вы могли быстро откатить их, если возникнут проблемы. Помните о техническом долге и погашайте его постепенно, прежде чем он начнёт накапливаться.
7. Пишите код в первую очередь для людей
Код читается гораздо чаще, чем пишется. Вы можете потратить час на написание функции, но десятки разработчиков могут прочитать её сотни раз за годы. Пишите код, с которым другим разработчикам будет приятно работать.
Поддерживаемость означает чёткое именование, простую логику, хорошую структуру и соответствующие комментарии. Выбирайте ясность, а не хитрость, описательное именование, а не поясняющие комментарии. Избегайте хитрых трюков, которые экономят две строки, но требуют пяти минут для понимания. Это означает думать о разработчике, который будет отлаживать это в 2 часа ночи, когда прод лёг.
8. Сложность губит проекты — сохраняйте простоту
Умная абстракция, которой вы так гордитесь, сложный паттерн проектирования, который вы реализовали… У сложности есть цена. Каждый уровень абстракции, каждое косвенное обращение, каждый «гибкий» дизайн делают систему сложнее для отладки и анализа. Предпочитайте скучные, простые решения, которые легко понять. Создавайте системы, которые можно объяснить на доске. Абстракция должна скрывать сложность, а не создавать её. Соблюдайте принцип наименьшего удивления: код должен вести себя так, как того ожидают от него другие разработчики.
Вообще, оверинжиниринг — часть пути разработчика. Нужно пройти через это, чтобы начать ценить простоту. Но как только вы это сделаете, вы никогда не захотите вернуться назад.
9. Устраняйте причины, а не симптомы
Когда вы обнаруживаете ошибку, сопротивляйтесь желанию быстро её исправить. Уделите время, чтобы понять, почему это произошло. Временные решения приводят к хрупким системам с нагромождением исправлений.
Найдите причину. Исправьте её должным образом. Да, это займет больше времени и может потребовать рефакторинга. Но вы предотвратите связанные ошибки и построите более надёжную систему. Исправление класса ошибок в корне гораздо эффективнее, чем многократное устранение отдельных симптомов. См. также: «Я исправил ошибку. Что дальше?»
10. Поймите «почему», прежде чем двигаться дальше
Когда что-то не работает или ведёт себя иначе, чем вы ожидали, не просто меняйте что-то, пока не заработает. Это заманчиво, но опасно. Вы получаете код, который не понимаете, потенциальные скрытые ошибки и упущенные возможности для обучения.
Убедитесь, что вы понимаете, почему код вёл себя именно так. Какое предположение было неверным? Что вы неправильно поняли о фреймворке, библиотеке или языке? В чём была реальная причина неожиданного поведения? Без понимания причин вы не развиваете свои навыки и не создаёте надёжное ПО.
Уделите время исследованию. Читайте документацию. Задавайте вопросы. Проводите эксперименты для проверки своих гипотез. Полученное понимание поможет избежать подобных проблем в будущем и углубит ваши знания используемых инструментов.
11. Не влюбляйтесь в свой код
Ваш код — не отражение вашей ценности как разработчика. Это инструмент для решения проблемы, и, если есть лучший инструмент или лучший способ, вы должны быть готовы отказаться от своего.
Эмоциональная привязанность к коду вынуждает вас защищаться во время код-ревью, сопротивляться изменениям и не замечать лучших решений. Хорошие инженеры регулярно удаляют свой код, рефакторят свои проекты и признают, когда их подход был не лучшим.
Продолжение следует…
Источник: https://www.meziantou.net/essential-rules-of-software-engineering.htm
Основные Правила Разработки ПО. Начало
Я уже не раз приводил здесь подобные советы от опытных разработчиков. Gérald Barré (aka meziantou) также составил свой список.
1. Вам платят не за код, а за решение проблем
Написание кода — не цель, а средство её достижения. Ваша ценность как инженера в понимании бизнес-проблем и предоставлении решений, которые создают ценность для пользователей и организации.
Иногда лучшее решение - отсутствие кода. Возможно, изменение процесса сработает лучше, существующий инструмент можно настроить по-другому, а может проблему и не нужно решать. Прежде чем приступать к реализации, уделите время пониманию сути проблемы, проанализируйте ситуацию, исходя из желаемого результата, и рассмотрите все возможные подходы.
2. Нет «лучшего» решения, есть только компромиссы
Разработка ПО – это принятие решений на основе неполной информации. У каждого вашего выбора есть плюсы и минусы, и то, что подходит для одного проекта, может оказаться ужасным для другого.
- Микросервисы? Зависит от размера команды, потребностей в масштабируемости и операционной сложности, которая вам под силу.
- SQL/NoSQL? Зависит от структуры данных, шаблонов запросов и требований к согласованности.
- TDD? Зависит от контекста, опыта команды и ограничений проекта.
Опытный инженер не ищет «лучшее» решение. Он оценивает компромиссы, исходя из текущих ограничений: навыков команды, сроков, бюджета, потребностей в масштабируемости и требований к сопровождению. Задокументируйте, почему вы выбрали тот или иной вариант. В будущем вы (или коллеги) оценят понимание контекста, лежащего в основе принятых решений. Всегда знайте, что именно вы оптимизируете: производительность, удобство сопровождения, время выхода на рынок, скорость работы команды и т.п.
Когда вы ходите по кругу, не находя чёткого «хорошего решения», это обычно означает, что вы недостаточно определили ограничения для проблемы. Добавьте больше ограничений: бюджет, дедлайн, размер команды, допустимый уровень технического долга, ожидаемый масштаб.
3. Чем меньше кода, тем лучше
Больше кода - больше работы по поддержке, тестированию, отладке и поиску ошибок. Лучший код часто тот, который вы не написали. Каждая строка должна оправдывать своё существование.
Прежде чем добавлять новую функцию или абстракцию, спросите себя: действительно ли это необходимо? Могу ли я решить это с помощью существующего кода? Решаю ли я существующую проблему или ту, которая, как я думаю, может возникнуть в будущем? Немедленно удаляйте неиспользуемый код. Мёртвый код создает путаницу, увеличивает нагрузку на поддержку и создаёт ложное представление о том, что система на самом деле делает.
4. Каждая зависимость — это обуза
Зависимости могут быть заброшены, содержать уязвимости безопасности, незаметно ломаться при обновлениях или приводить к появлению транзитивных зависимостей, которые вы никогда не планировали включать, а также могут препятствовать обновлению других пакетов из-за несовместимости.
Регулярно проверяйте зависимости. Удаляйте неиспользуемые. Оценивайте состояние проектов, от которых вы зависите (последние коммиты, активные сопровождающие, история безопасности). Учитывайте общую стоимость владения, а не только первоначальное удобство.
5. Выпускайте рано, итерируйте часто
Идеальность — враг завершённости. Чем дольше вы оттягиваете выпуск, тем дольше откладываете получение реальной обратной связи от пользователей. Сначала сделайте, чтоб работало, затем сделайте это правильно, а затем сделайте это быстрым.
Ранние релизы помогают проверить предположения, выяснить, что действительно нужно пользователям (в отличие от того, что вы думаете, что им нужно), и скорректировать курс. Выбрасывать код больно, но ещё больнее потратить полгода на создание неправильного продукта.
Опросы пользователей, прототипы и моки могут проверить идеи до написания кода. Иногда простой разговор показывает, что запланированная вами функция совсем не то, что нужно пользователям. Внедряйте механизмы обратной связи в процесс разработки, чтобы расти как инженер.
6. Проектируйте с учётом изменений, т.к. ПО никогда не бывает завершённым
Потребности пользователей и технологии меняются, платформы обновляются, и обнаруживаются уязвимости безопасности. Сделайте так, чтобы ПО было легко модифицировать, расширять и поддерживать. Создавайте с учётом расширяемости и ясности. Проводите рефакторинг непрерывно и постепенно, а не ждите «большой переработки». Сочетайте принцип YAGNI с продуманным проектированием, которое не загонит вас в тупик. Делайте изменения по возможности обратимыми, чтобы вы могли быстро откатить их, если возникнут проблемы. Помните о техническом долге и погашайте его постепенно, прежде чем он начнёт накапливаться.
7. Пишите код в первую очередь для людей
Код читается гораздо чаще, чем пишется. Вы можете потратить час на написание функции, но десятки разработчиков могут прочитать её сотни раз за годы. Пишите код, с которым другим разработчикам будет приятно работать.
Поддерживаемость означает чёткое именование, простую логику, хорошую структуру и соответствующие комментарии. Выбирайте ясность, а не хитрость, описательное именование, а не поясняющие комментарии. Избегайте хитрых трюков, которые экономят две строки, но требуют пяти минут для понимания. Это означает думать о разработчике, который будет отлаживать это в 2 часа ночи, когда прод лёг.
8. Сложность губит проекты — сохраняйте простоту
Умная абстракция, которой вы так гордитесь, сложный паттерн проектирования, который вы реализовали… У сложности есть цена. Каждый уровень абстракции, каждое косвенное обращение, каждый «гибкий» дизайн делают систему сложнее для отладки и анализа. Предпочитайте скучные, простые решения, которые легко понять. Создавайте системы, которые можно объяснить на доске. Абстракция должна скрывать сложность, а не создавать её. Соблюдайте принцип наименьшего удивления: код должен вести себя так, как того ожидают от него другие разработчики.
Вообще, оверинжиниринг — часть пути разработчика. Нужно пройти через это, чтобы начать ценить простоту. Но как только вы это сделаете, вы никогда не захотите вернуться назад.
9. Устраняйте причины, а не симптомы
Когда вы обнаруживаете ошибку, сопротивляйтесь желанию быстро её исправить. Уделите время, чтобы понять, почему это произошло. Временные решения приводят к хрупким системам с нагромождением исправлений.
Найдите причину. Исправьте её должным образом. Да, это займет больше времени и может потребовать рефакторинга. Но вы предотвратите связанные ошибки и построите более надёжную систему. Исправление класса ошибок в корне гораздо эффективнее, чем многократное устранение отдельных симптомов. См. также: «Я исправил ошибку. Что дальше?»
10. Поймите «почему», прежде чем двигаться дальше
Когда что-то не работает или ведёт себя иначе, чем вы ожидали, не просто меняйте что-то, пока не заработает. Это заманчиво, но опасно. Вы получаете код, который не понимаете, потенциальные скрытые ошибки и упущенные возможности для обучения.
Убедитесь, что вы понимаете, почему код вёл себя именно так. Какое предположение было неверным? Что вы неправильно поняли о фреймворке, библиотеке или языке? В чём была реальная причина неожиданного поведения? Без понимания причин вы не развиваете свои навыки и не создаёте надёжное ПО.
Уделите время исследованию. Читайте документацию. Задавайте вопросы. Проводите эксперименты для проверки своих гипотез. Полученное понимание поможет избежать подобных проблем в будущем и углубит ваши знания используемых инструментов.
11. Не влюбляйтесь в свой код
Ваш код — не отражение вашей ценности как разработчика. Это инструмент для решения проблемы, и, если есть лучший инструмент или лучший способ, вы должны быть готовы отказаться от своего.
Эмоциональная привязанность к коду вынуждает вас защищаться во время код-ревью, сопротивляться изменениям и не замечать лучших решений. Хорошие инженеры регулярно удаляют свой код, рефакторят свои проекты и признают, когда их подход был не лучшим.
Продолжение следует…
Источник: https://www.meziantou.net/essential-rules-of-software-engineering.htm
👍9👎1
День 2761. #Карьера
Основные Правила Разработки ПО. Продолжение
Начало
12. Прежде чем судить о чужом коде, стремитесь понять его
Глядя на чужой код, вы думаете, что всё бы сделали не так. Прежде чем судить, попытайтесь понять контекст. Какие были ограничения, требования, кодовая база? Часто то, что кажется плохим решением, обретает смысл, когда вы понимаете всю историю целиком. Поймите бизнес-логику кода. Технические решения обретают больше смысла, когда вы понимаете бизнес-проблемы, которые он решает.
Но, если код действительно проблемный, критикуйте код, а не разработчика. Делайте код-ревью с эмпатией, предлагайте улучшения уважительно. Ваш код тоже когда-нибудь станет для кого-то «унаследованным беспорядком».
13. Документируйте «почему» вы принимали решения
Через полгода вы уже не вспомните, почему выбрали этот подход, а не тот. Ваши коллеги тоже. Со временем дизайн и принятые решения теряют контекст.
Документируйте важные решения, особенно когда приходится выбирать между разумными альтернативами. Используйте ADR, комментарии или проектную документацию. Объясните контекст, рассмотренные варианты и почему вы выбрали именно этот. Не обязательно формально, просто чтобы освежить память позже.
Хорошая документация — это фиксация «почему» были приняты неочевидные решения, рассмотренные компромиссы и ограничения, с которыми вы работали.
Документация имеет мультипликативный эффект: она поможет коллегам, если они застрянут, ускоряет адаптацию и предотвращает повторение ошибок. Час, потраченный на документирование, может сэкономить команде десятки часов.
14. Пишите содержательные сообщения коммитов
История коммитов - это история проекта. Хорошие сообщения коммитов помогают понять, что изменилось и почему, что значительно упрощает отладку, проверку кода и адаптацию новых сотрудников. Опишите не только, что вы исправили, но и почему. Это предоставит контекст для будущих разработчиков.
Обеспечьте чёткую коммуникацию также в пул-реквестах, тикетах и документации. Делайте пул-реквесты небольшими и управляемыми, чтобы их было легче проверять, и чтобы снизить вероятность появления ошибок. Чёткая коммуникация — отличительная черта поведения опытного специалиста, и она приносит пользу всем членам команды.
ИИ сейчас может писать сообщения коммитов, анализируя изменения в коде, но всегда проверяйте и редактируйте их, чтобы обеспечить точность и ясность.
15. Автоматизируйте всё, что можно
Установите стандарты кодирования на раннем этапе, автоматизируйте их соблюдение с помощью форматеров и линтеров и двигайтесь дальше. Будьте последовательны в стандартах.
Автоматизируйте тестирование, развёртывание, сканирование безопасности, обновление зависимостей и любые повторяющиеся задачи. CI/CD — не опция, а основа надёжной доставки.
Создавайте конвейеры с защитой: автоматический откат и всестороннее тестирование, чтобы уменьшить радиус поражения потенциальных проблем. Забудьте о «это работает на моей машине». Если это работает в конвейере, только тогда это работает везде.
16. Код-ревью улучшает не только качество
Это один из лучших способов обмена знаниями внутри команды и улучшения как кода, так и людей, которые его пишут. Джуны изучают шаблоны и практики. Сеньоры остаются в курсе того, что происходит в разных частях кодовой базы. Все узнают о новых областях, в которых они раньше не работали. Уменьшается разрозненность знаний, и команда становится более устойчивой.
17. Никогда не прекращайте учиться и подвергать сомнению предположения
Технологии развиваются быстро. Фреймворки, языки и инструменты, которые вы знаете сегодня, через 5 лет изменятся. Непрерывное обучение — это обязательное условие.
Но это не только погоня за новыми технологиями; стремитесь к пониманию. Изучите основы: алгоритмы, структуры данных, сети, безопасность и принципы проектирования. Освойте смежные навыки: коммуникацию, наставничество, управление проектами.
Поддержка работающих производственных систем предоставляет лучшие возможности для обучения. Только преодолевая трудности вы будете расти. Сталкивайтесь со сложными проблемами, устраняйте неполадки в проде и учитесь на ограничениях реального мира.
18. Обращайтесь за помощью, когда застряли
Застрять на несколько часов, потому что стесняетесь попросить о помощи, — пустая трата времени. Коллеги хотят помочь. Они сталкивались с похожими проблемами. У них разные точки зрения, они могут сразу заметить то, что вы упускаете. Просьба о помощи — это сильная сторона, а не слабость.
Ключ к успеху — задавать правильные вопросы. Покажите, что вы уже пробовали. Объясните своё текущее понимание, предоставьте контекст. Это поможет им помочь вам.
19. Оценки никогда не бывают абсолютно точными
Оценка — сложная задача. Всегда есть неизвестные факторы, неожиданные сложности и задачи, которые оказались простыми лишь на бумаге. Относитесь к оценкам как к обоснованным предположениям, а не обязательствам. Учитывайте неопределённость в своих оценках. Сравнивайте свои оценки с фактическими результатами, чтобы со временем улучшать их. Сообщайте как можно раньше, если обнаружите, что оценка неверна. Чётко сообщайте о компромиссах .
Вместо: «Это займёт 3 дня», говорите: «Думаю, что это займёт 3-5 дней, при условии отсутствия проблем с интеграцией API стороннего сервиса, с которым я раньше не работал. Я сообщу вам, как начну работу, если обнаружу сложности».
20. Вы не можете управлять тем, за чем не наблюдаете
Производственным системам необходима наблюдаемость. Иначе вы действуете вслепую, когда что-то идёт не так, а это обязательно произойдёт. Ведите логи, но не чрезмерно. Добавляйте логи, метрики, трассировки и оповещения, которые имеют значение.
Проектируйте системы с учётом возможности наблюдения с самого начала. Инструментируйте свой код для отслеживания ключевых метрик, производительности, ошибок и поведения пользователей. Убедитесь, что вы можете ответить на вопросы: «Здорова» ли система? Где узкие места? Каков пользовательский опыт? Когда начались сбои?
Совершенство в работе системы — это разница между устранением проблем за минуты, а не за часы, и между знанием о проблемах до того, как о них узнают пользователи, а не из обращений в службу поддержки. Надёжность важнее новых функций. Всегда.
Окончание следует…
Источник: https://www.meziantou.net/essential-rules-of-software-engineering.htm
Основные Правила Разработки ПО. Продолжение
Начало
12. Прежде чем судить о чужом коде, стремитесь понять его
Глядя на чужой код, вы думаете, что всё бы сделали не так. Прежде чем судить, попытайтесь понять контекст. Какие были ограничения, требования, кодовая база? Часто то, что кажется плохим решением, обретает смысл, когда вы понимаете всю историю целиком. Поймите бизнес-логику кода. Технические решения обретают больше смысла, когда вы понимаете бизнес-проблемы, которые он решает.
Но, если код действительно проблемный, критикуйте код, а не разработчика. Делайте код-ревью с эмпатией, предлагайте улучшения уважительно. Ваш код тоже когда-нибудь станет для кого-то «унаследованным беспорядком».
13. Документируйте «почему» вы принимали решения
Через полгода вы уже не вспомните, почему выбрали этот подход, а не тот. Ваши коллеги тоже. Со временем дизайн и принятые решения теряют контекст.
Документируйте важные решения, особенно когда приходится выбирать между разумными альтернативами. Используйте ADR, комментарии или проектную документацию. Объясните контекст, рассмотренные варианты и почему вы выбрали именно этот. Не обязательно формально, просто чтобы освежить память позже.
Хорошая документация — это фиксация «почему» были приняты неочевидные решения, рассмотренные компромиссы и ограничения, с которыми вы работали.
Документация имеет мультипликативный эффект: она поможет коллегам, если они застрянут, ускоряет адаптацию и предотвращает повторение ошибок. Час, потраченный на документирование, может сэкономить команде десятки часов.
14. Пишите содержательные сообщения коммитов
История коммитов - это история проекта. Хорошие сообщения коммитов помогают понять, что изменилось и почему, что значительно упрощает отладку, проверку кода и адаптацию новых сотрудников. Опишите не только, что вы исправили, но и почему. Это предоставит контекст для будущих разработчиков.
Обеспечьте чёткую коммуникацию также в пул-реквестах, тикетах и документации. Делайте пул-реквесты небольшими и управляемыми, чтобы их было легче проверять, и чтобы снизить вероятность появления ошибок. Чёткая коммуникация — отличительная черта поведения опытного специалиста, и она приносит пользу всем членам команды.
ИИ сейчас может писать сообщения коммитов, анализируя изменения в коде, но всегда проверяйте и редактируйте их, чтобы обеспечить точность и ясность.
15. Автоматизируйте всё, что можно
Установите стандарты кодирования на раннем этапе, автоматизируйте их соблюдение с помощью форматеров и линтеров и двигайтесь дальше. Будьте последовательны в стандартах.
Автоматизируйте тестирование, развёртывание, сканирование безопасности, обновление зависимостей и любые повторяющиеся задачи. CI/CD — не опция, а основа надёжной доставки.
Создавайте конвейеры с защитой: автоматический откат и всестороннее тестирование, чтобы уменьшить радиус поражения потенциальных проблем. Забудьте о «это работает на моей машине». Если это работает в конвейере, только тогда это работает везде.
16. Код-ревью улучшает не только качество
Это один из лучших способов обмена знаниями внутри команды и улучшения как кода, так и людей, которые его пишут. Джуны изучают шаблоны и практики. Сеньоры остаются в курсе того, что происходит в разных частях кодовой базы. Все узнают о новых областях, в которых они раньше не работали. Уменьшается разрозненность знаний, и команда становится более устойчивой.
17. Никогда не прекращайте учиться и подвергать сомнению предположения
Технологии развиваются быстро. Фреймворки, языки и инструменты, которые вы знаете сегодня, через 5 лет изменятся. Непрерывное обучение — это обязательное условие.
Но это не только погоня за новыми технологиями; стремитесь к пониманию. Изучите основы: алгоритмы, структуры данных, сети, безопасность и принципы проектирования. Освойте смежные навыки: коммуникацию, наставничество, управление проектами.
Поддержка работающих производственных систем предоставляет лучшие возможности для обучения. Только преодолевая трудности вы будете расти. Сталкивайтесь со сложными проблемами, устраняйте неполадки в проде и учитесь на ограничениях реального мира.
18. Обращайтесь за помощью, когда застряли
Застрять на несколько часов, потому что стесняетесь попросить о помощи, — пустая трата времени. Коллеги хотят помочь. Они сталкивались с похожими проблемами. У них разные точки зрения, они могут сразу заметить то, что вы упускаете. Просьба о помощи — это сильная сторона, а не слабость.
Ключ к успеху — задавать правильные вопросы. Покажите, что вы уже пробовали. Объясните своё текущее понимание, предоставьте контекст. Это поможет им помочь вам.
19. Оценки никогда не бывают абсолютно точными
Оценка — сложная задача. Всегда есть неизвестные факторы, неожиданные сложности и задачи, которые оказались простыми лишь на бумаге. Относитесь к оценкам как к обоснованным предположениям, а не обязательствам. Учитывайте неопределённость в своих оценках. Сравнивайте свои оценки с фактическими результатами, чтобы со временем улучшать их. Сообщайте как можно раньше, если обнаружите, что оценка неверна. Чётко сообщайте о компромиссах .
Вместо: «Это займёт 3 дня», говорите: «Думаю, что это займёт 3-5 дней, при условии отсутствия проблем с интеграцией API стороннего сервиса, с которым я раньше не работал. Я сообщу вам, как начну работу, если обнаружу сложности».
20. Вы не можете управлять тем, за чем не наблюдаете
Производственным системам необходима наблюдаемость. Иначе вы действуете вслепую, когда что-то идёт не так, а это обязательно произойдёт. Ведите логи, но не чрезмерно. Добавляйте логи, метрики, трассировки и оповещения, которые имеют значение.
Проектируйте системы с учётом возможности наблюдения с самого начала. Инструментируйте свой код для отслеживания ключевых метрик, производительности, ошибок и поведения пользователей. Убедитесь, что вы можете ответить на вопросы: «Здорова» ли система? Где узкие места? Каков пользовательский опыт? Когда начались сбои?
Совершенство в работе системы — это разница между устранением проблем за минуты, а не за часы, и между знанием о проблемах до того, как о них узнают пользователи, а не из обращений в службу поддержки. Надёжность важнее новых функций. Всегда.
Окончание следует…
Источник: https://www.meziantou.net/essential-rules-of-software-engineering.htm
👍4👎2
День 2762. #Карьера
Основные Правила Разработки ПО. Окончание
Начало
Продолжение
21. Никогда не доверяйте пользовательскому вводу
Пользователи допускают ошибки, а злоумышленники активно пытаются взломать вашу систему. Никогда не доверяйте пользовательскому вводу, будь то из форм, API, при загрузке файлов или в параметрах URL.
Проверяйте все входные данные: типы, форматы, диапазоны и допустимые значения. Очищайте данные, чтобы предотвратить атаки инъекцией. Используйте параметризованные запросы, кодирование и проверенные библиотеки безопасности, а не разрабатывайте собственные решения.
22. Ошибки должны приводить к громкому и немедленному сбою
Тихие сбои — кошмар отладки. Когда что-то идёт не так, сделайте это очевидным. Сообщайте об ошибках быстро, громко и предоставляйте чёткие сообщения об ошибках, которые помогут быстро выявить проблему.
Не игнорируйте исключения. Не возвращайте null при сбое. Не продолжайте работу, записав в лог. Когда не выполняется предусловие, данные невалидны, необходимый сервис недоступен, остановите выполнение и чётко сообщите о проблеме.
Хорошая обработка ошибок подразумевает быстрое выявление и устранение неполадок с чёткими сообщениями, предоставлением контекста о том, что пошло не так и почему, а также упрощением отслеживания источника ошибки. Это экономит часы отладки.
23. Сначала измерьте, потом оптимизируйте
Преждевременная оптимизация — корень всех зол. Разработчики часто тратят время на оптимизацию кода, который на самом деле не является узким местом, усложняя его без существенного повышения производительности.
Перед оптимизацией профилируйте приложение. Выявите реальные, а не предполагаемые узкие места. Вы можете обнаружить, что медленная часть вовсе не там, где вы думали. Оптимизируйте проблемные области и снова проведите измерения, чтобы проверить улучшение.
Не игнорируйте производительность полностью. Пишите достаточно эффективный код изначально, но не жертвуйте ясностью и удобством сопровождения ради микрооптимизаций. Знайте, что вы оптимизируете: задержку, пропускную способность, память или время разработки.
24. Меняйте по одной вещи за раз
При отладке или оптимизации сопротивляйтесь желанию изменять несколько вещей одновременно. Так вы не будете знать, какое именно изменение исправило ошибку или улучшило производительность. Внесите одно изменение, измерьте результат, а затем продолжайте. Такой дисциплинированный подход может показаться медленнее, но он экономит время, предоставляя чёткие причинно-следственные связи. Вы построите мысленную модель того, что действительно важно, и избежите разочарования от отмены целого ряда изменений, потому что одно из них сломало что-то другое.
Аналогично при рефакторинге изменяйте либо структуру, либо поведение, но не то и другое одновременно. При развертывании выпускайте по одной функции за раз, чтобы изолировать проблемы.
25. Создавайте API, которые легко использовать правильно и сложно неправильно
Хороший дизайн предотвращает ошибки до их возникновения. При проектировании функции, класса или интерфейса сервиса подумайте о том, как они будут использоваться и как их можно использовать неправильно.
Используйте типы для обеспечения ограничений (обязательность, допустимые диапазоны, разрешённые состояния). Сделайте недопустимые состояния непредставимыми. Используйте чёткие имена, указывающие на назначение и поведение. Предоставляйте хорошие значения по умолчанию. Сделайте распространённые случаи простыми, а сложные — возможными. Возвращайте содержательные ошибки, которые помогут разработчикам правильно использовать API.
Отдавайте предпочтение композиции перед наследованием. Композиция более гибкая и создаёт меньше проблем со связностью. Минимизируйте связь между компонентами и максимизируйте их согласованность. Каждый модуль должен иметь чёткое назначение.
26. Будьте хорошим человеком важнее технических навыков
Разработка — командный вид спорта. Техническое мастерство ничего не значит, если с вами трудно работать, вы неэффективно общаетесь или не поддерживаете своих коллег.
Будьте добры, терпеливы, делитесь заслугами, признавайте ошибки, помогайте другим расти, больше слушайте, уважайте разные точки зрения и стили работы. У каждого есть трудности, которых вы не видите. Ваше отношение и навыки сотрудничества важнее для вашей карьеры, чем любые технические навыки.
Поддерживайте коллег, когда у них возникают трудности. Отмечайте их успехи. Создавайте среду, где люди чувствуют себя комфортно, задавая вопросы, признавая, что чего-то не знают, или высказывая свои опасения. Это укрепляет доверие и побуждает людей сообщать о проблемах на ранних стадиях, а не скрывать их.
27. Думайте о последствиях, а не только о решениях
Большинство инженеров оптимизируют решения. Архитекторы оптимизируют последствия. Обе роли важны, но они решают разные классы проблем.
Сеньоры задаются вопросом: «Как построить это хорошо?» Архитекторы: «Что произойдёт, если мы ошибаемся?» Этот сдвиг в перспективе меняет то, что вы оптимизируете. Вместо того чтобы фокусироваться на локальной производительности или качестве реализации, минимизируйте возможный ущерб, оптимизируйте обратимость и долгосрочные эксплуатационные расходы.
Ваш прогресс как архитектора – это учитывание последствий необратимых решений, компромиссов в выборе инструментов, ограничений в выборе возможностей и побочных эффектов. Уменьшайте сложность не только внутри функции, но и в командах и организации в целом.
Архитектура — это не рисование диаграмм. Это выбор того, с какими последствиями вы готовы смириться, и принятие ответственности за этот выбор.
Итого
Это не строгие законы, а рекомендации, основанные на опыте, ошибках и извлечённых уроках. Возьмите то, что вам близко, адаптируйте к своему контексту и помните, что разработка ПО — это в равной степени код, люди и коммуникация.
Лучшие инженеры — не те, кто пишет самый умный код, а те, кто приносит пользу, эффективно сотрудничает, постоянно учится и делает лучше всех вокруг себя. Развивайте свои принципы, основываясь на собственном опыте. Каждый проект, каждая команда и каждое испытание учат вас чему-то новому.
Источник: https://www.meziantou.net/essential-rules-of-software-engineering.htm
Основные Правила Разработки ПО. Окончание
Начало
Продолжение
21. Никогда не доверяйте пользовательскому вводу
Пользователи допускают ошибки, а злоумышленники активно пытаются взломать вашу систему. Никогда не доверяйте пользовательскому вводу, будь то из форм, API, при загрузке файлов или в параметрах URL.
Проверяйте все входные данные: типы, форматы, диапазоны и допустимые значения. Очищайте данные, чтобы предотвратить атаки инъекцией. Используйте параметризованные запросы, кодирование и проверенные библиотеки безопасности, а не разрабатывайте собственные решения.
22. Ошибки должны приводить к громкому и немедленному сбою
Тихие сбои — кошмар отладки. Когда что-то идёт не так, сделайте это очевидным. Сообщайте об ошибках быстро, громко и предоставляйте чёткие сообщения об ошибках, которые помогут быстро выявить проблему.
Не игнорируйте исключения. Не возвращайте null при сбое. Не продолжайте работу, записав в лог. Когда не выполняется предусловие, данные невалидны, необходимый сервис недоступен, остановите выполнение и чётко сообщите о проблеме.
Хорошая обработка ошибок подразумевает быстрое выявление и устранение неполадок с чёткими сообщениями, предоставлением контекста о том, что пошло не так и почему, а также упрощением отслеживания источника ошибки. Это экономит часы отладки.
23. Сначала измерьте, потом оптимизируйте
Преждевременная оптимизация — корень всех зол. Разработчики часто тратят время на оптимизацию кода, который на самом деле не является узким местом, усложняя его без существенного повышения производительности.
Перед оптимизацией профилируйте приложение. Выявите реальные, а не предполагаемые узкие места. Вы можете обнаружить, что медленная часть вовсе не там, где вы думали. Оптимизируйте проблемные области и снова проведите измерения, чтобы проверить улучшение.
Не игнорируйте производительность полностью. Пишите достаточно эффективный код изначально, но не жертвуйте ясностью и удобством сопровождения ради микрооптимизаций. Знайте, что вы оптимизируете: задержку, пропускную способность, память или время разработки.
24. Меняйте по одной вещи за раз
При отладке или оптимизации сопротивляйтесь желанию изменять несколько вещей одновременно. Так вы не будете знать, какое именно изменение исправило ошибку или улучшило производительность. Внесите одно изменение, измерьте результат, а затем продолжайте. Такой дисциплинированный подход может показаться медленнее, но он экономит время, предоставляя чёткие причинно-следственные связи. Вы построите мысленную модель того, что действительно важно, и избежите разочарования от отмены целого ряда изменений, потому что одно из них сломало что-то другое.
Аналогично при рефакторинге изменяйте либо структуру, либо поведение, но не то и другое одновременно. При развертывании выпускайте по одной функции за раз, чтобы изолировать проблемы.
25. Создавайте API, которые легко использовать правильно и сложно неправильно
Хороший дизайн предотвращает ошибки до их возникновения. При проектировании функции, класса или интерфейса сервиса подумайте о том, как они будут использоваться и как их можно использовать неправильно.
Используйте типы для обеспечения ограничений (обязательность, допустимые диапазоны, разрешённые состояния). Сделайте недопустимые состояния непредставимыми. Используйте чёткие имена, указывающие на назначение и поведение. Предоставляйте хорошие значения по умолчанию. Сделайте распространённые случаи простыми, а сложные — возможными. Возвращайте содержательные ошибки, которые помогут разработчикам правильно использовать API.
Отдавайте предпочтение композиции перед наследованием. Композиция более гибкая и создаёт меньше проблем со связностью. Минимизируйте связь между компонентами и максимизируйте их согласованность. Каждый модуль должен иметь чёткое назначение.
26. Будьте хорошим человеком важнее технических навыков
Разработка — командный вид спорта. Техническое мастерство ничего не значит, если с вами трудно работать, вы неэффективно общаетесь или не поддерживаете своих коллег.
Будьте добры, терпеливы, делитесь заслугами, признавайте ошибки, помогайте другим расти, больше слушайте, уважайте разные точки зрения и стили работы. У каждого есть трудности, которых вы не видите. Ваше отношение и навыки сотрудничества важнее для вашей карьеры, чем любые технические навыки.
Поддерживайте коллег, когда у них возникают трудности. Отмечайте их успехи. Создавайте среду, где люди чувствуют себя комфортно, задавая вопросы, признавая, что чего-то не знают, или высказывая свои опасения. Это укрепляет доверие и побуждает людей сообщать о проблемах на ранних стадиях, а не скрывать их.
27. Думайте о последствиях, а не только о решениях
Большинство инженеров оптимизируют решения. Архитекторы оптимизируют последствия. Обе роли важны, но они решают разные классы проблем.
Сеньоры задаются вопросом: «Как построить это хорошо?» Архитекторы: «Что произойдёт, если мы ошибаемся?» Этот сдвиг в перспективе меняет то, что вы оптимизируете. Вместо того чтобы фокусироваться на локальной производительности или качестве реализации, минимизируйте возможный ущерб, оптимизируйте обратимость и долгосрочные эксплуатационные расходы.
Ваш прогресс как архитектора – это учитывание последствий необратимых решений, компромиссов в выборе инструментов, ограничений в выборе возможностей и побочных эффектов. Уменьшайте сложность не только внутри функции, но и в командах и организации в целом.
Архитектура — это не рисование диаграмм. Это выбор того, с какими последствиями вы готовы смириться, и принятие ответственности за этот выбор.
Итого
Это не строгие законы, а рекомендации, основанные на опыте, ошибках и извлечённых уроках. Возьмите то, что вам близко, адаптируйте к своему контексту и помните, что разработка ПО — это в равной степени код, люди и коммуникация.
Лучшие инженеры — не те, кто пишет самый умный код, а те, кто приносит пользу, эффективно сотрудничает, постоянно учится и делает лучше всех вокруг себя. Развивайте свои принципы, основываясь на собственном опыте. Каждый проект, каждая команда и каждое испытание учат вас чему-то новому.
Источник: https://www.meziantou.net/essential-rules-of-software-engineering.htm
👍5👎1
День 2763.
DotNext 2026
В этом году в очередной раз поеду на конференцию DotNext. На этот раз простым слушателем. Она пройдёт в Москве 25 и 26 сентября. В этом году её сдвинули с обычных четверга-пятницы на пятницу-субботу, так что отпроситься с работы стало проще 😉
Сильно изменилась и программа (расписание, кстати, тут). Раньше основной фокус докладов был на практических нюансах разработки. Всегда можно было найти в расписании доклады по DDD, EF, перекладыванию JSON-чиков, асинхронной коммуникации в приложении или особенностях БД (чаще всего Postgres). Некоторые даже упрекали конференцию в некотором застое, мол, доклады почти одинаковые из года в год. Так вот, в этот раз это совсем не так. AI ворвался на DotNext с ноги, даже создали отдельный топик «Practical AI», и он второй по популярности из 5 топиков программы. Я, например, для себя отметил «Когда код пишут не только люди: как меняется разработка в эпоху agentic development lifecycle» от Дмитрия Иванова и «Как закрывать весь SDLC с помощью агента» от Павла Федотовского.
Но это не значит, что ИИ захватил всё.
Больше всего докладов в топике «Архитектура». Некоторые будут без записи, поэтому, наверное, их стоит посетить лично, а остальные посмотреть потом. Из интересного, на мой взгляд, тут мастер-класс «Распилим монолит» от Алексея Лосева, «Модульность без микросервисов в ASP.NET из коробки» от Станислава Выщепана (да, темы схожие, но тут «у кого чего болит» - у меня на работе «big pile of mud» в каком-то смысле, который надо распиливать) и доклад о DomainEvents в DDD от Дениса Цветцих.
Топик «Internals» вобрал в себя доклады про «кишочки». Там, например, можно послушать Марка Шевченко про «Размеченные объединения в C# 15» или Юрия Малича про сравнение производительности и подводные камни Native AOT и JIT. Я скорей всего схожу на доклад Николая Савенко «Как выбрать инструмент для трейсов в .NET?»
Топик «Best practices» никуда не делся. Здесь меня заинтересовали доклады «Очень быстрые интеграционные тесты на EF Core + PostgreSQL» Сергея Булавского и про практику применения появившихся в C# 14 Extension Members от Виктора Дзицкого.
Ну и наконец, пара докладов есть в топике «Расширяем горизонты». Тут можно отвлечься от технических деталей и послушать о том, реально ли сделать свою IDE или почему в космосе пока нет дата-центров.
В общем, посмотреть и послушать много чего есть (я перечислил от силы четверть). И в этом году докладов стало больше. Если раньше я иногда терялся, на какой доклад пойти, когда их было 3 параллельно, то теперь их стало 4 в каждом слоте! А даже если не понравится совсем ничего из предложенного, можно посетить стенды партнёров конференции и поучаствовать в конкурсах, прикупить (или выиграть) мерча, а возможно и найти новое место работы.
Так что приезжайте тоже, скучно не будет. Ну и, конечно, если встретите меня, подходите, рад буду со всеми пообщаться в офлайне.
PS: если решили купить билет, напишите в личку 😉
DotNext 2026
В этом году в очередной раз поеду на конференцию DotNext. На этот раз простым слушателем. Она пройдёт в Москве 25 и 26 сентября. В этом году её сдвинули с обычных четверга-пятницы на пятницу-субботу, так что отпроситься с работы стало проще 😉
Сильно изменилась и программа (расписание, кстати, тут). Раньше основной фокус докладов был на практических нюансах разработки. Всегда можно было найти в расписании доклады по DDD, EF, перекладыванию JSON-чиков, асинхронной коммуникации в приложении или особенностях БД (чаще всего Postgres). Некоторые даже упрекали конференцию в некотором застое, мол, доклады почти одинаковые из года в год. Так вот, в этот раз это совсем не так. AI ворвался на DotNext с ноги, даже создали отдельный топик «Practical AI», и он второй по популярности из 5 топиков программы. Я, например, для себя отметил «Когда код пишут не только люди: как меняется разработка в эпоху agentic development lifecycle» от Дмитрия Иванова и «Как закрывать весь SDLC с помощью агента» от Павла Федотовского.
Но это не значит, что ИИ захватил всё.
Больше всего докладов в топике «Архитектура». Некоторые будут без записи, поэтому, наверное, их стоит посетить лично, а остальные посмотреть потом. Из интересного, на мой взгляд, тут мастер-класс «Распилим монолит» от Алексея Лосева, «Модульность без микросервисов в ASP.NET из коробки» от Станислава Выщепана (да, темы схожие, но тут «у кого чего болит» - у меня на работе «big pile of mud» в каком-то смысле, который надо распиливать) и доклад о DomainEvents в DDD от Дениса Цветцих.
Топик «Internals» вобрал в себя доклады про «кишочки». Там, например, можно послушать Марка Шевченко про «Размеченные объединения в C# 15» или Юрия Малича про сравнение производительности и подводные камни Native AOT и JIT. Я скорей всего схожу на доклад Николая Савенко «Как выбрать инструмент для трейсов в .NET?»
Топик «Best practices» никуда не делся. Здесь меня заинтересовали доклады «Очень быстрые интеграционные тесты на EF Core + PostgreSQL» Сергея Булавского и про практику применения появившихся в C# 14 Extension Members от Виктора Дзицкого.
Ну и наконец, пара докладов есть в топике «Расширяем горизонты». Тут можно отвлечься от технических деталей и послушать о том, реально ли сделать свою IDE или почему в космосе пока нет дата-центров.
В общем, посмотреть и послушать много чего есть (я перечислил от силы четверть). И в этом году докладов стало больше. Если раньше я иногда терялся, на какой доклад пойти, когда их было 3 параллельно, то теперь их стало 4 в каждом слоте! А даже если не понравится совсем ничего из предложенного, можно посетить стенды партнёров конференции и поучаствовать в конкурсах, прикупить (или выиграть) мерча, а возможно и найти новое место работы.
Так что приезжайте тоже, скучно не будет. Ну и, конечно, если встретите меня, подходите, рад буду со всеми пообщаться в офлайне.
PS: если решили купить билет, напишите в личку 😉
👍4
Посещаете ли вы профессиональные конференции (выберите наиболее подходящий вам ответ)?
Anonymous Poll
16%
да, интересно послушать доклады
1%
да, так развиваю сеть полезных контактов
11%
только если отправляет/оплачивает работодатель
3%
смотрю онлайн (по билету)
51%
нет, смотрю доклады, когда выкладывают в общий доступ
19%
нет, не интересно совсем
День 2764. #ЧтоНовенького #NET11
Вышли Превью 6 и 7 .NET 11
Посмотрим на самые интересные новинки в библиотеках.
1. Асинхронная валидация в DataAnnotations
System.ComponentModel.DataAnnotations теперь поддерживает асинхронную валидацию. Правило валидации, требующее операций ввода-вывода — поиск в базе данных, вызов удалённого API — теперь может выполняться без блокировки потока.
Способы выражения асинхронного правила:
- Наследовать от AsyncValidationAttribute и переопределить его метод IsValidAsync.
- Реализовать IAsyncValidatableObject в модели.
- Вызвать новые методы Validator.ValidateObjectAsync, TryValidateObjectAsync, ValidatePropertyAsync и ValidateValueAsync.
Microsoft.Extensions.Options получает соответствующую поддержку: конфигурация приложения может проверяться асинхронно, в том числе при запуске, с помощью нового IAsyncStartupValidator.
2. System.Text.Json сериализует типы-объединения
Сериализатор распознаёт объединение через новый тип контракта JsonTypeInfoKind.Union, читает и записывает активный вариант и поддерживает как сериализатор на основе рефлексии, так и генератор исходного кода. Новые API JsonUnionAttribute, JsonUnionCaseInfo и классификаторы типов (JsonTypeClassifier и JsonSerializerOptions.TypeClassifiers) позволяют настраивать способ обнаружения и именования вариантов.
3. Настройка трассировки действий с помощью правил
Новый API настройки трассировки в Microsoft.Extensions.Diagnostics позволяет включать и выключать трассировку действий с помощью правил вместо ручной настройки экземпляров ActivityListener. Вызовите AddTracing и укажите, какие источники действий, операции и слушатели следует включить или отключить. Правила также могут управляться из конфигурации, поэтому трассировку можно настраивать без повторного развёртывания:
Также добавлена ActivitySourceFactory, а у класса ActivitySource убран модификатор sealed, что предоставляет фабричное создание и обновляемых слушателей.
4. Запуск приостановленных процессов и их поиск по идентификатору
System.Diagnostics.Process добавляет более тонкий контроль над запуском и поиском процессов:
- ProcessStartInfo.StartSuspended запускает процесс в приостановленном состоянии в Windows. Используйте его в паре с SafeProcessHandle.Resume, чтобы он продолжил работу после завершения какой-либо настройки, например подключения отладчика или настройки объектов заданий.
- Process.TryGetProcessById возвращает false вместо исключения, если процесса с заданным идентификатором не существует.
- SafeProcessHandle.Open и TryOpen открывают дескриптор существующего процесса по идентификатору.
5. Десятичные типы с плавающей запятой по IEEE 754
System.Numerics получает три десятичных типа с плавающей запятой IEEE 754-2019 — Decimal32, Decimal64 и Decimal128 — с точностью до 7, 16 и 34 знаков после запятой соответственно. В отличие от System.Decimal, они используют кодировку обмена двоичными целыми и десятичными числами IEEE (BID), и поддерживают специальные значения IEEE, такие как бесконечность и NaN. Они поддерживают обобщённые математические операции через IDecimalFloatingPointIeee754<TSelf>, поэтому обобщённые числовые алгоритмы могут использовать их наряду с существующими типами с плавающей запятой.
Типы включают в себя API для парсинга, преобразований, арифметических и сравнительных операторов, округления, умножения-сложения, квантования, а также двоичного и десятичного кодирования. Базовые арифметические операции проверяются с точностью до бита по тестовым векторам библиотеки Intel Decimal Floating-Point Math Library. Функции более высокого уровня, такие как трансцендентные числа, не являются точными в .NET 11, но планируется сделать их точными в будущей версии.
6. Частичный парсинг чисел
INumberBase<TSelf>.TryParsePartial добавляет универсальные перегрузки для парсинга чисел, которые сообщают, сколько входных данных было обработано. Это позволяет парсерам для таких форматов, как CSV, анализировать одно числовое поле и продолжать обработку оставшихся входных данных без их копирования или обработки разделителя как ошибки парсинга:
7. Сжатие HTTP-запросов
System.Net.Http получает три обёртки над HttpContent, которые сжимают тела запросов в сети — GZipCompressedContent, BrotliCompressedContent и ZstandardCompressedContent в пространстве имен System.Net.Http. Каждая обёртка устанавливает соответствующий заголовок Content-Encoding, очищает Content-Length и передаёт сжатые байты потоком по мере сериализации запроса. Для каждого алгоритма предоставляются два конструктора: один принимает CompressionLevel для быстрой настройки скорости/размера, а другой принимает полный тип параметров алгоритма (ZLibCompressionOptions, BrotliCompressionOptions или ZstandardCompressionOptions) для более точной настройки. Сжатие запросов является необязательным: используйте его только в том случае, если вы знаете, что сервер поддерживает выбранный кодировщик содержимого, поскольку HttpClient не согласовывает сжатие содержимого запроса автоматически.
8. Поддержка паролей для ZIP-архивов
System.IO.Compression теперь читает и записывает защищённые паролем ZIP-файлы. ZipArchiveEntry получает перегрузки Open и OpenAsync, принимающие пароль типа ReadOnlySpan<char>, ZipArchive.CreateEntry получает перегрузки, принимающие пароль и ZipEncryptionMethod (ZipCrypto, Aes128, Aes192, Aes256), а новое свойство ZipArchiveEntry.EncryptionMethod отображает алгоритм, используемый существующей записью.
9. Фабрики HybridCache с поддержкой параметров
Microsoft.Extensions.Caching.Hybrid.HybridCache добавляет перегрузки GetOrCreateAsync, чей фабричный метод обратного вызова получает изменяемый HybridCacheEntryContext в дополнение к состоянию и токену отмены. Теперь фабрика может настраивать параметры Expiration, LocalCacheExpiration, Flags и новое свойство LocalSize (HybridCacheEntryOptions.LocalSize, используемое для MemoryCache.Size) в зависимости от только что полученного значения — например, предоставляя более длительный срок жизни (TTL) для большого вычисленного результата.
Источники:
- https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/preview6/libraries.md
- https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/preview7/libraries.md
Вышли Превью 6 и 7 .NET 11
Посмотрим на самые интересные новинки в библиотеках.
1. Асинхронная валидация в DataAnnotations
System.ComponentModel.DataAnnotations теперь поддерживает асинхронную валидацию. Правило валидации, требующее операций ввода-вывода — поиск в базе данных, вызов удалённого API — теперь может выполняться без блокировки потока.
Способы выражения асинхронного правила:
- Наследовать от AsyncValidationAttribute и переопределить его метод IsValidAsync.
- Реализовать IAsyncValidatableObject в модели.
- Вызвать новые методы Validator.ValidateObjectAsync, TryValidateObjectAsync, ValidatePropertyAsync и ValidateValueAsync.
Microsoft.Extensions.Options получает соответствующую поддержку: конфигурация приложения может проверяться асинхронно, в том числе при запуске, с помощью нового IAsyncStartupValidator.
2. System.Text.Json сериализует типы-объединения
Сериализатор распознаёт объединение через новый тип контракта JsonTypeInfoKind.Union, читает и записывает активный вариант и поддерживает как сериализатор на основе рефлексии, так и генератор исходного кода. Новые API JsonUnionAttribute, JsonUnionCaseInfo и классификаторы типов (JsonTypeClassifier и JsonSerializerOptions.TypeClassifiers) позволяют настраивать способ обнаружения и именования вариантов.
3. Настройка трассировки действий с помощью правил
Новый API настройки трассировки в Microsoft.Extensions.Diagnostics позволяет включать и выключать трассировку действий с помощью правил вместо ручной настройки экземпляров ActivityListener. Вызовите AddTracing и укажите, какие источники действий, операции и слушатели следует включить или отключить. Правила также могут управляться из конфигурации, поэтому трассировку можно настраивать без повторного развёртывания:
builder.Services.AddTracing(trc =>
{
trc.EnableTracing(sourceName: "MyCompany.Orders");
trc.DisableTracing(sourceName: "MyCompany.Orders", operationName: "HealthCheck");
});
Также добавлена ActivitySourceFactory, а у класса ActivitySource убран модификатор sealed, что предоставляет фабричное создание и обновляемых слушателей.
4. Запуск приостановленных процессов и их поиск по идентификатору
System.Diagnostics.Process добавляет более тонкий контроль над запуском и поиском процессов:
- ProcessStartInfo.StartSuspended запускает процесс в приостановленном состоянии в Windows. Используйте его в паре с SafeProcessHandle.Resume, чтобы он продолжил работу после завершения какой-либо настройки, например подключения отладчика или настройки объектов заданий.
- Process.TryGetProcessById возвращает false вместо исключения, если процесса с заданным идентификатором не существует.
- SafeProcessHandle.Open и TryOpen открывают дескриптор существующего процесса по идентификатору.
5. Десятичные типы с плавающей запятой по IEEE 754
System.Numerics получает три десятичных типа с плавающей запятой IEEE 754-2019 — Decimal32, Decimal64 и Decimal128 — с точностью до 7, 16 и 34 знаков после запятой соответственно. В отличие от System.Decimal, они используют кодировку обмена двоичными целыми и десятичными числами IEEE (BID), и поддерживают специальные значения IEEE, такие как бесконечность и NaN. Они поддерживают обобщённые математические операции через IDecimalFloatingPointIeee754<TSelf>, поэтому обобщённые числовые алгоритмы могут использовать их наряду с существующими типами с плавающей запятой.
Типы включают в себя API для парсинга, преобразований, арифметических и сравнительных операторов, округления, умножения-сложения, квантования, а также двоичного и десятичного кодирования. Базовые арифметические операции проверяются с точностью до бита по тестовым векторам библиотеки Intel Decimal Floating-Point Math Library. Функции более высокого уровня, такие как трансцендентные числа, не являются точными в .NET 11, но планируется сделать их точными в будущей версии.
6. Частичный парсинг чисел
INumberBase<TSelf>.TryParsePartial добавляет универсальные перегрузки для парсинга чисел, которые сообщают, сколько входных данных было обработано. Это позволяет парсерам для таких форматов, как CSV, анализировать одно числовое поле и продолжать обработку оставшихся входных данных без их копирования или обработки разделителя как ошибки парсинга:
using System;
using System.Globalization;
ReadOnlySpan<char> input = "123; 456";
if (int.TryParsePartial(
input,
NumberStyles.Integer,
CultureInfo.InvariantCulture,
out int value,
out int charsConsumed))
{
Console.WriteLine(value); // 123
input = input[charsConsumed..];
Console.WriteLine(input); // ; 456
}
7. Сжатие HTTP-запросов
System.Net.Http получает три обёртки над HttpContent, которые сжимают тела запросов в сети — GZipCompressedContent, BrotliCompressedContent и ZstandardCompressedContent в пространстве имен System.Net.Http. Каждая обёртка устанавливает соответствующий заголовок Content-Encoding, очищает Content-Length и передаёт сжатые байты потоком по мере сериализации запроса. Для каждого алгоритма предоставляются два конструктора: один принимает CompressionLevel для быстрой настройки скорости/размера, а другой принимает полный тип параметров алгоритма (ZLibCompressionOptions, BrotliCompressionOptions или ZstandardCompressionOptions) для более точной настройки. Сжатие запросов является необязательным: используйте его только в том случае, если вы знаете, что сервер поддерживает выбранный кодировщик содержимого, поскольку HttpClient не согласовывает сжатие содержимого запроса автоматически.
8. Поддержка паролей для ZIP-архивов
System.IO.Compression теперь читает и записывает защищённые паролем ZIP-файлы. ZipArchiveEntry получает перегрузки Open и OpenAsync, принимающие пароль типа ReadOnlySpan<char>, ZipArchive.CreateEntry получает перегрузки, принимающие пароль и ZipEncryptionMethod (ZipCrypto, Aes128, Aes192, Aes256), а новое свойство ZipArchiveEntry.EncryptionMethod отображает алгоритм, используемый существующей записью.
9. Фабрики HybridCache с поддержкой параметров
Microsoft.Extensions.Caching.Hybrid.HybridCache добавляет перегрузки GetOrCreateAsync, чей фабричный метод обратного вызова получает изменяемый HybridCacheEntryContext в дополнение к состоянию и токену отмены. Теперь фабрика может настраивать параметры Expiration, LocalCacheExpiration, Flags и новое свойство LocalSize (HybridCacheEntryOptions.LocalSize, используемое для MemoryCache.Size) в зависимости от только что полученного значения — например, предоставляя более длительный срок жизни (TTL) для большого вычисленного результата.
Источники:
- https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/preview6/libraries.md
- https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/preview7/libraries.md
👍3