День 2347. #BestPractices
Пишем Хорошую Документацию. Продолжение
Начало
4. Сосредоточьтесь на вариантах использования и используйте интеллектуальные инструменты для API
Разработчики не просто хотят знать, что делает конечная точка API; они хотят знать, как использовать её для решения своей проблемы. Создавайте документацию вокруг общих задач и рабочих процессов.
Специализированные фреймворки для API значительно улучшают процесс создания документации. Инструменты, разработанные специально для жизненного цикла API, могут оптимизировать как разработку, так и документирование, обеспечивая согласованность и интерактивность. Например, Apidog интегрирует проектирование API, отладку, тестирование непосредственно с созданием документации.
5. Поддерживайте точность и актуальность
Устаревшая документация подрывает доверие быстрее, чем что-либо ещё. Разработчики полагаются на правильность документов. Если они сталкиваются с несоответствиями, они полностью перестают доверять документации.
- Версионирование: чётко версионируйте документацию вместе с выпусками ПО/API. Позвольте пользователям легко переключаться между версиями.
- Документация-как-Код: относитесь к документации как к коду. Храните её в системе контроля версий (например, Git) вместе с исходным кодом, который она описывает. Это упрощает отслеживание изменений, просмотр обновлений и синхронизацию документов с выпусками кода. Интегрируйте обновления документации в свой конвейер CI/CD.
- Циклы обратной связи: упростите разработчикам возможность сообщать об ошибках или предлагать улучшения (например, кнопка «Предложить изменения», ссылающаяся на GitHub Issues или специальную форму обратной связи). Незамедлительно реагируйте на эти отзывы.
- Регулярные обзоры: запланируйте периодические обзоры документации для проверки точности, ясности и полноты.
6. Визуальная привлекательность и последовательность
Хотя суть является ключевой, представление тоже имеет значение. Красивые документы приятны и легко читаются.
- Чистый дизайн: используйте достаточно свободного пространства, читаемые шрифты и четкую визуальную иерархию. Избегайте загромождённых шаблонов.
- Единообразное форматирование: применяйте единый стиль для блоков кода, заметок, предупреждений, заголовков, ссылок и т. д. По возможности используйте руководство по стилю.
- Подсветка синтаксиса: необходима для примеров кода. Используйте чёткое и правильное выделение для соответствующих языков программирования.
- Наглядные пособия: используйте диаграммы (блок-схемы, диаграммы последовательности, диаграммы архитектуры), снимки экрана или короткие видеоролики, где они могут прояснить сложные концепции более эффективно, чем текст. Убедитесь, что визуальные материалы понятны, маркированы и актуальны.
7. Улучшите интерактивность и поисковую доступность
Выйдите за рамки статического текста:
- Интерактивные элементы: помимо пользовательского интерфейса API (например, Swagger), рассмотрите использование встроенных редакторов кода (например, CodeSandbox) или интерактивных руководств (например, Jupiter Notebooks).
- Расширенный поиск: для больших наборов документации разрешите пользователям фильтровать результаты поиска по категории, версии или разделу API.
- Виджеты "Была ли эта страница полезна?": собирайте быстрые отзывы об эффективности страницы.
Окончание следует…
Источник: https://dev.to/therealmrmumba/beyond-code-how-to-create-beautiful-documentation-that-developers-actually-love-best-practices-hc4
Пишем Хорошую Документацию. Продолжение
Начало
4. Сосредоточьтесь на вариантах использования и используйте интеллектуальные инструменты для API
Разработчики не просто хотят знать, что делает конечная точка API; они хотят знать, как использовать её для решения своей проблемы. Создавайте документацию вокруг общих задач и рабочих процессов.
Специализированные фреймворки для API значительно улучшают процесс создания документации. Инструменты, разработанные специально для жизненного цикла API, могут оптимизировать как разработку, так и документирование, обеспечивая согласованность и интерактивность. Например, Apidog интегрирует проектирование API, отладку, тестирование непосредственно с созданием документации.
5. Поддерживайте точность и актуальность
Устаревшая документация подрывает доверие быстрее, чем что-либо ещё. Разработчики полагаются на правильность документов. Если они сталкиваются с несоответствиями, они полностью перестают доверять документации.
- Версионирование: чётко версионируйте документацию вместе с выпусками ПО/API. Позвольте пользователям легко переключаться между версиями.
- Документация-как-Код: относитесь к документации как к коду. Храните её в системе контроля версий (например, Git) вместе с исходным кодом, который она описывает. Это упрощает отслеживание изменений, просмотр обновлений и синхронизацию документов с выпусками кода. Интегрируйте обновления документации в свой конвейер CI/CD.
- Циклы обратной связи: упростите разработчикам возможность сообщать об ошибках или предлагать улучшения (например, кнопка «Предложить изменения», ссылающаяся на GitHub Issues или специальную форму обратной связи). Незамедлительно реагируйте на эти отзывы.
- Регулярные обзоры: запланируйте периодические обзоры документации для проверки точности, ясности и полноты.
6. Визуальная привлекательность и последовательность
Хотя суть является ключевой, представление тоже имеет значение. Красивые документы приятны и легко читаются.
- Чистый дизайн: используйте достаточно свободного пространства, читаемые шрифты и четкую визуальную иерархию. Избегайте загромождённых шаблонов.
- Единообразное форматирование: применяйте единый стиль для блоков кода, заметок, предупреждений, заголовков, ссылок и т. д. По возможности используйте руководство по стилю.
- Подсветка синтаксиса: необходима для примеров кода. Используйте чёткое и правильное выделение для соответствующих языков программирования.
- Наглядные пособия: используйте диаграммы (блок-схемы, диаграммы последовательности, диаграммы архитектуры), снимки экрана или короткие видеоролики, где они могут прояснить сложные концепции более эффективно, чем текст. Убедитесь, что визуальные материалы понятны, маркированы и актуальны.
7. Улучшите интерактивность и поисковую доступность
Выйдите за рамки статического текста:
- Интерактивные элементы: помимо пользовательского интерфейса API (например, Swagger), рассмотрите использование встроенных редакторов кода (например, CodeSandbox) или интерактивных руководств (например, Jupiter Notebooks).
- Расширенный поиск: для больших наборов документации разрешите пользователям фильтровать результаты поиска по категории, версии или разделу API.
- Виджеты "Была ли эта страница полезна?": собирайте быстрые отзывы об эффективности страницы.
Окончание следует…
Источник: https://dev.to/therealmrmumba/beyond-code-how-to-create-beautiful-documentation-that-developers-actually-love-best-practices-hc4
👍6
День 2348. #BestPractices
Пишем Хорошую Документацию. Окончание
Начало
Продолжение
8. Используйте современные инструменты (вне зависимости от API)
Существует множество инструментов, которые помогут вам эффективно и результативно создавать документацию:
- Генераторы статических сайтов (SSG): такие инструменты, как MkDocs, Docusaurus, Hugo, Jekyll и Sphinx, являются популярным выбором. Они берут простые файлы разметки (например, Markdown) и создают профессионально выглядящие веб-сайты документации, с возможностью поиска. Они часто поставляются с темами, плагинами для поиска, поддержкой управления версиями и хорошо согласуются с подходом «Документация-как-Код».
- Платформы документации: такие сервисы, как Read the Docs, GitBook или Confluence, предлагают решения со встроенными функциями для совместной работы, управления версиями и презентации.
Выбирайте инструменты, которые соответствуют вашему рабочему процессу, размеру команды и техническим требованиям.
9. Используйте ИИ (осторожно)
Искусственный интеллект проникает в документацию. Генератор документации ИИ может стать мощным помощником, но крайне важно понимать его сильные и слабые стороны.
Потенциальные преимущества
- Генерация шаблонов: ИИ может быстро генерировать первоначальные черновики описаний функций/методов на основе комментариев или сигнатур кода (например, блоков
- Саммари: ИИ может обобщать длинные технические объяснения или сложные разделы кода.
- Проверки согласованности: ИИ может помочь выявить несоответствия в терминологии или стиле в больших наборах документации.
- Перевод: сервисы перевода на другие языки совершенствуются, хотя человеческая проверка по-прежнему необходима для технической точности.
Важные предостережения
- Точность не гарантируется: модели ИИ могут галлюцинировать или неправильно интерпретировать контекст кода. Всегда привлекайте эксперта-человека для проверки технической корректности контента, созданного ИИ.
- Отсутствие контекста: ИИ может не понимать более широкий вариант использования, потребности целевой аудитории или причины использования фрагментов кода.
- Общее повествование: текст, сгенерированный ИИ, иногда может быть скучным или не иметь конкретных идей, которые мог бы предоставить эксперт-человек.
Используйте генератор документации ИИ как инструмент для дополнения человеческих усилий, а не для их замены. Он может ускорить составление черновиков и выявить области для улучшения, но критическое мышление, техническая проверка и создание действительно полезных объяснений остаются человеческими задачами.
10. Развивайте культуру документирования
Отличная документация обычно не является продуктом одного человека, работающего в изоляции. Для этого требуются командные усилия и культурный сдвиг:
- Интеграция в рабочий процесс: сделайте документацию частью определения «готово» для новых функций или изменений API. Выделяйте на неё время во время спринтов.
- Поощряйте вклад: сделайте так, чтобы всем разработчикам (а не только преданным писателям) было легко вносить исправления и улучшения. Снизьте барьер для входа (например, простое редактирование Markdown через Git).
- Признавайте и вознаграждайте: признавайте и цените усилия, которые вложены в создание и поддержание высококачественной документации.
- Показывайте пример: если руководители групп и старшие разработчики отдают приоритет документации, другие, скорее всего, последуют их примеру.
Итого
Создание документации, которая понравится разработчикам, — непростая задача, но это чрезвычайно ценная инвестиция. Красивые документы — точные, понятные, хорошо структурированные, простые в навигации и визуально приятные — напрямую влияют на производительность разработчиков, снижают нагрузку на поддержку, улучшают адаптацию и улучшают общее восприятие вашего ПО или платформы.
Источник: https://dev.to/therealmrmumba/beyond-code-how-to-create-beautiful-documentation-that-developers-actually-love-best-practices-hc4
Пишем Хорошую Документацию. Окончание
Начало
Продолжение
8. Используйте современные инструменты (вне зависимости от API)
Существует множество инструментов, которые помогут вам эффективно и результативно создавать документацию:
- Генераторы статических сайтов (SSG): такие инструменты, как MkDocs, Docusaurus, Hugo, Jekyll и Sphinx, являются популярным выбором. Они берут простые файлы разметки (например, Markdown) и создают профессионально выглядящие веб-сайты документации, с возможностью поиска. Они часто поставляются с темами, плагинами для поиска, поддержкой управления версиями и хорошо согласуются с подходом «Документация-как-Код».
- Платформы документации: такие сервисы, как Read the Docs, GitBook или Confluence, предлагают решения со встроенными функциями для совместной работы, управления версиями и презентации.
Выбирайте инструменты, которые соответствуют вашему рабочему процессу, размеру команды и техническим требованиям.
9. Используйте ИИ (осторожно)
Искусственный интеллект проникает в документацию. Генератор документации ИИ может стать мощным помощником, но крайне важно понимать его сильные и слабые стороны.
Потенциальные преимущества
- Генерация шаблонов: ИИ может быстро генерировать первоначальные черновики описаний функций/методов на основе комментариев или сигнатур кода (например, блоков
///summary
).- Саммари: ИИ может обобщать длинные технические объяснения или сложные разделы кода.
- Проверки согласованности: ИИ может помочь выявить несоответствия в терминологии или стиле в больших наборах документации.
- Перевод: сервисы перевода на другие языки совершенствуются, хотя человеческая проверка по-прежнему необходима для технической точности.
Важные предостережения
- Точность не гарантируется: модели ИИ могут галлюцинировать или неправильно интерпретировать контекст кода. Всегда привлекайте эксперта-человека для проверки технической корректности контента, созданного ИИ.
- Отсутствие контекста: ИИ может не понимать более широкий вариант использования, потребности целевой аудитории или причины использования фрагментов кода.
- Общее повествование: текст, сгенерированный ИИ, иногда может быть скучным или не иметь конкретных идей, которые мог бы предоставить эксперт-человек.
Используйте генератор документации ИИ как инструмент для дополнения человеческих усилий, а не для их замены. Он может ускорить составление черновиков и выявить области для улучшения, но критическое мышление, техническая проверка и создание действительно полезных объяснений остаются человеческими задачами.
10. Развивайте культуру документирования
Отличная документация обычно не является продуктом одного человека, работающего в изоляции. Для этого требуются командные усилия и культурный сдвиг:
- Интеграция в рабочий процесс: сделайте документацию частью определения «готово» для новых функций или изменений API. Выделяйте на неё время во время спринтов.
- Поощряйте вклад: сделайте так, чтобы всем разработчикам (а не только преданным писателям) было легко вносить исправления и улучшения. Снизьте барьер для входа (например, простое редактирование Markdown через Git).
- Признавайте и вознаграждайте: признавайте и цените усилия, которые вложены в создание и поддержание высококачественной документации.
- Показывайте пример: если руководители групп и старшие разработчики отдают приоритет документации, другие, скорее всего, последуют их примеру.
Итого
Создание документации, которая понравится разработчикам, — непростая задача, но это чрезвычайно ценная инвестиция. Красивые документы — точные, понятные, хорошо структурированные, простые в навигации и визуально приятные — напрямую влияют на производительность разработчиков, снижают нагрузку на поддержку, улучшают адаптацию и улучшают общее восприятие вашего ПО или платформы.
Источник: https://dev.to/therealmrmumba/beyond-code-how-to-create-beautiful-documentation-that-developers-actually-love-best-practices-hc4
👍3
День 2349. #SystemDesign101 #Шпаргалка
Коды Состояний HTTP
1xx: Информационные
100 Continue (продолжайте);
101 Switching Protocols (переключение протоколов);
102 Processing (идёт обработка);
103 Early Hints (предварительный ответ).
2xx: Успех
200 OK (хорошо);
201 Created (создано);
202 Accepted (принято);
204 No Content (нет содержимого);
205 Reset Content (сбросить содержимое);
206 Partial Content (частичное содержимое);
207 Multi-Status (многостатусный);
208 Already Reported (уже сообщалось).
3xx: Перенаправление
300 Multiple Choices (несколько вариантов);
301 Moved Permanently (перемещено навсегда);
302 Found (найдено);
303 See Other (смотреть другое);
304 Not Modified (не изменялось);
305 Use Proxy (использовать прокси);
307 Temporary Redirect (временное перенаправление);
308 Permanent Redirect (постоянное перенаправление).
4xx: Ошибка клиента
400 Bad Request (некорректный запрос);
401 Unauthorized (не авторизован);
402 Payment Required (необходима оплата);
403 Forbidden (запрещено);
404 Not Found (не найдено);
405 Method Not Allowed (метод не поддерживается);
406 Not Acceptable (неприемлемо);
407 Proxy Authentication Required (необходима аутентификация прокси);
408 Request Timeout (истекло время ожидания);
409 Conflict (конфликт);
410 Gone (удалён);
411 Length Required (необходима длина);
412 Precondition Failed (условие ложно);
413 Payload Too Large (полезная нагрузка слишком велика);
414 URI Too Long (URI слишком длинный);
415 Unsupported Media Type (неподдерживаемый тип данных);
416 Range Not Satisfiable (диапазон не достижим);
417 Expectation Failed (ожидание не оправдалось);
422 Unprocessable Entity (необрабатываемый экземпляр);
423 Locked (заблокировано);
424 Failed Dependency (невыполненная зависимость);
425 Too Early (слишком рано);
426 Upgrade Required (необходимо обновление);
428 Precondition Required (необходимо предусловие);
429 Too Many Requests (слишком много запросов);
431 Request Header Fields Too Large (поля заголовка запроса слишком большие);
449 Retry With (повторить с);
499 Client Closed Request (клиент закрыл соединение).
5xx: Ошибка сервера
500 Internal Server Error (внутренняя ошибка сервера);
501 Not Implemented (не реализовано);
502 Bad Gateway (ошибочный шлюз);
503 Service Unavailable (сервис недоступен);
504 Gateway Timeout (шлюз не отвечает);
505 HTTP Version Not Supported (версия HTTP не поддерживается);
506 Variant Also Negotiates (вариант тоже проводит согласование);
507 Insufficient Storage (переполнение хранилища);
508 Loop Detected (обнаружено бесконечное перенаправление);
509 Bandwidth Limit Exceeded (исчерпана пропускная ширина канала);
510 Not Extended (отсутствует расширение);
511 Network Authentication Required (требуется сетевая аутентификация);
520 Unknown Error (неизвестная ошибка);
521 Web Server Is Down (веб-сервер не работает);
522 Connection Timed Out (соединение не отвечает);
523 Origin Is Unreachable (источник недоступен);
524 A Timeout Occurred (время ожидания истекло);
525 SSL Handshake Failed (SSL-рукопожатие не удалось);
526 Invalid SSL Certificate (недействительный сертификат SSL).
Коды состояний, которые не должны были существовать
Источники:
- https://github.com/ByteByteGoHq/system-design-101
- https://ru.wikipedia.org/wiki/Список_кодов_состояния_HTTP
Коды Состояний HTTP
1xx: Информационные
100 Continue (продолжайте);
101 Switching Protocols (переключение протоколов);
102 Processing (идёт обработка);
103 Early Hints (предварительный ответ).
2xx: Успех
200 OK (хорошо);
201 Created (создано);
202 Accepted (принято);
204 No Content (нет содержимого);
205 Reset Content (сбросить содержимое);
206 Partial Content (частичное содержимое);
207 Multi-Status (многостатусный);
208 Already Reported (уже сообщалось).
3xx: Перенаправление
300 Multiple Choices (несколько вариантов);
301 Moved Permanently (перемещено навсегда);
302 Found (найдено);
303 See Other (смотреть другое);
304 Not Modified (не изменялось);
305 Use Proxy (использовать прокси);
307 Temporary Redirect (временное перенаправление);
308 Permanent Redirect (постоянное перенаправление).
4xx: Ошибка клиента
400 Bad Request (некорректный запрос);
401 Unauthorized (не авторизован);
402 Payment Required (необходима оплата);
403 Forbidden (запрещено);
404 Not Found (не найдено);
405 Method Not Allowed (метод не поддерживается);
406 Not Acceptable (неприемлемо);
407 Proxy Authentication Required (необходима аутентификация прокси);
408 Request Timeout (истекло время ожидания);
409 Conflict (конфликт);
410 Gone (удалён);
411 Length Required (необходима длина);
412 Precondition Failed (условие ложно);
413 Payload Too Large (полезная нагрузка слишком велика);
414 URI Too Long (URI слишком длинный);
415 Unsupported Media Type (неподдерживаемый тип данных);
416 Range Not Satisfiable (диапазон не достижим);
417 Expectation Failed (ожидание не оправдалось);
422 Unprocessable Entity (необрабатываемый экземпляр);
423 Locked (заблокировано);
424 Failed Dependency (невыполненная зависимость);
425 Too Early (слишком рано);
426 Upgrade Required (необходимо обновление);
428 Precondition Required (необходимо предусловие);
429 Too Many Requests (слишком много запросов);
431 Request Header Fields Too Large (поля заголовка запроса слишком большие);
449 Retry With (повторить с);
499 Client Closed Request (клиент закрыл соединение).
5xx: Ошибка сервера
500 Internal Server Error (внутренняя ошибка сервера);
501 Not Implemented (не реализовано);
502 Bad Gateway (ошибочный шлюз);
503 Service Unavailable (сервис недоступен);
504 Gateway Timeout (шлюз не отвечает);
505 HTTP Version Not Supported (версия HTTP не поддерживается);
506 Variant Also Negotiates (вариант тоже проводит согласование);
507 Insufficient Storage (переполнение хранилища);
508 Loop Detected (обнаружено бесконечное перенаправление);
509 Bandwidth Limit Exceeded (исчерпана пропускная ширина канала);
510 Not Extended (отсутствует расширение);
511 Network Authentication Required (требуется сетевая аутентификация);
520 Unknown Error (неизвестная ошибка);
521 Web Server Is Down (веб-сервер не работает);
522 Connection Timed Out (соединение не отвечает);
523 Origin Is Unreachable (источник недоступен);
524 A Timeout Occurred (время ожидания истекло);
525 SSL Handshake Failed (SSL-рукопожатие не удалось);
526 Invalid SSL Certificate (недействительный сертификат SSL).
Коды состояний, которые не должны были существовать
218 This Is Fine
- Отсылка к мему, означает, что ошибка не должна перехватываться сервером и отправляться клиенту.418 I’m A Teapot
– Введен в 1998 году как первоапрельская шутка (4/1/8). Означает, что чайник не может сварить кофе.420 Enhance Your Calm
– Нестандартный ответ, предлагающий клиенту «остыть». Сейчас заменён кодом 429.451 Unavailable For Legal Reasons
– Доступ к ресурсу закрыт по юридическим причинам, например, по требованию органов государственной власти или по требованию правообладателя в случае нарушения авторских прав. Является отсылкой к роману Рэя Брэдбери «451 градус по Фаренгейту».530 Site Frozen
– Сайт работает, но заморожен провайдером (например, за неоплату).Источники:
- https://github.com/ByteByteGoHq/system-design-101
- https://ru.wikipedia.org/wiki/Список_кодов_состояния_HTTP
👍19
День 2350. #ЗаметкиНаПолях
Разбираем Генераторы Исходного Кода. Начало
Генераторы исходного кода, представленные в C# 9, стали мощным инструментом для метапрограммирования в .NET, позволяя разработчикам генерировать дополнительный исходный код. Они запускаются во время компиляции для проверки кода и генерации дополнительных файлов кода C#. Затем эти файлы компилируются вместе с остальным кодом, что позволяет динамически создавать код на основе существующей кодовой базы. Генератор исходного кода можно использовать для различных целей, таких как скафолдинг, проверка и повышение читаемости и удобства обслуживания кода.
В C# 12 генераторы исходного кода получили дальнейшее развитие, позволяя реализовывать более сложные сценарии и повышая производительность за счёт сокращения шаблонного кода и улучшения проверок во время компиляции.
1. Инкрементные генераторы
Регенерируют код только при необходимости, что значительно повышает производительность. Кэшируют результаты предыдущих запусков и повторно генерируют код только при изменении базовых данных.
2. Анализ зависимостей исходного кода
Позволяет компилятору точнее определять, какие части вашего кода зависят от сгенерированного кода. Это приводит к более эффективным сборкам и меньшему количеству ненужных перекомпиляций.
3. Улучшенная диагностика
Позволяет лучше сообщать об ошибках и предупреждениях непосредственно в сгенерированном коде. Разработчики получают чёткую обратную связь в процессе разработки, что упрощает отладку и поддержку сгенерированного кода.
4. Улучшения API Roslyn
Больше возможностей для анализа и изменения синтаксического дерева, позволяющих реализовывать более сложные сценарии генерации кода.
Простой генератор кода
1. Настройка проекта
Создадим проект библиотеки и добавим нужные NuGet-пакеты
2. Генератор кода
Понадобится класс, реализующий IIncrementalGenerator:
Этот простой генератор добавляет класс HelloWorld с методом SayHello в ваш проект. Сгенерированный код будет скомпилирован с остальной частью проекта, что позволит вызывать HelloWorldGenerated.HelloWorld.SayHello() из вашего кода.
3. Интеграция с проектом
Создадим консольный проект:
Добавим ссылку на проект генератора из другого проекта:
Заметьте два нестандартных атрибута:
-
-
Теперь сгенерированный класс HelloWorld доступен в консольном проекте.
4. Сборка и запуск
Соберём проект и запустим его. Мы увидим в консоли вывод из сгенерированного метода HelloWorld.SayHello():
Окончание следует…
Источник: https://thecodeman.net/posts/source-generators-deep-dive
Разбираем Генераторы Исходного Кода. Начало
Генераторы исходного кода, представленные в C# 9, стали мощным инструментом для метапрограммирования в .NET, позволяя разработчикам генерировать дополнительный исходный код. Они запускаются во время компиляции для проверки кода и генерации дополнительных файлов кода C#. Затем эти файлы компилируются вместе с остальным кодом, что позволяет динамически создавать код на основе существующей кодовой базы. Генератор исходного кода можно использовать для различных целей, таких как скафолдинг, проверка и повышение читаемости и удобства обслуживания кода.
В C# 12 генераторы исходного кода получили дальнейшее развитие, позволяя реализовывать более сложные сценарии и повышая производительность за счёт сокращения шаблонного кода и улучшения проверок во время компиляции.
1. Инкрементные генераторы
Регенерируют код только при необходимости, что значительно повышает производительность. Кэшируют результаты предыдущих запусков и повторно генерируют код только при изменении базовых данных.
2. Анализ зависимостей исходного кода
Позволяет компилятору точнее определять, какие части вашего кода зависят от сгенерированного кода. Это приводит к более эффективным сборкам и меньшему количеству ненужных перекомпиляций.
3. Улучшенная диагностика
Позволяет лучше сообщать об ошибках и предупреждениях непосредственно в сгенерированном коде. Разработчики получают чёткую обратную связь в процессе разработки, что упрощает отладку и поддержку сгенерированного кода.
4. Улучшения API Roslyn
Больше возможностей для анализа и изменения синтаксического дерева, позволяющих реализовывать более сложные сценарии генерации кода.
Простой генератор кода
1. Настройка проекта
Создадим проект библиотеки и добавим нужные NuGet-пакеты
dotnet new classlib -n MySourceGenerator
cd MySourceGenerator
dotnet add package Microsoft.CodeAnalysis.CSharp
2. Генератор кода
Понадобится класс, реализующий IIncrementalGenerator:
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
[Generator]
public class HelloWorldGenerator : IIncrementalGenerator
{
public void Initialize(
IncrementalGeneratorInitializationContext context)
{
var src =
"""
using System;
namespace HelloGenerated;
public static class HelloWorld
{
public static void SayHello()
=> Console.WriteLine("Hello from the generated code!");
}
""";
context.RegisterPostInitializationOutput(
ctx => ctx.AddSource(
"HelloWorldGenerated",
SourceText.From(src, Encoding.UTF8)));
}
}
Этот простой генератор добавляет класс HelloWorld с методом SayHello в ваш проект. Сгенерированный код будет скомпилирован с остальной частью проекта, что позволит вызывать HelloWorldGenerated.HelloWorld.SayHello() из вашего кода.
3. Интеграция с проектом
Создадим консольный проект:
dotnet new console -n UseSourceGenerator
Добавим ссылку на проект генератора из другого проекта:
<ProjectReference Include="..\MySourceGenerator\MySourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
Заметьте два нестандартных атрибута:
-
OutputItemType="Analyzer"
- позволяет целевому проекту просматривать указанный проект в качестве анализатора;-
ReferenceOutputAssembly="false"
- гарантирует, что целевой проект не будет ссылаться на DLL генератора кода во время компиляции.Теперь сгенерированный класс HelloWorld доступен в консольном проекте.
4. Сборка и запуск
Соберём проект и запустим его. Мы увидим в консоли вывод из сгенерированного метода HelloWorld.SayHello():
// Program.cs
HelloGenerated.HelloWorld.SayHello();
Окончание следует…
Источник: https://thecodeman.net/posts/source-generators-deep-dive
👍19
День 2351. #ЗаметкиНаПолях
Разбираем Генераторы Исходного Кода. Окончание
Начало
Чтение кода из файла
Если у вас есть код C# в файле, и вы хотите использовать этот код в для генерации дополнительного или изменения существующего кода, вы можете прочитать содержимое файла и использовать его как часть процесса генерации исходного кода.
Предположим, у есть файл MyClass.cs со следующим содержимым:
В проекте генератора кода читаем содержимое этого файла и генерируем на его основе дополнительный код:
В клиентский проект нужно добавить файл MyClass.cs и ссылку на проект генератора кода, как показано в предыдущем посте.
Рекомендации по использованию кодогенерации
1. Производительность
Хотя генераторы исходного кода могут сэкономить время во время разработки, они также могут вносить дополнительные накладные расходы во время компиляции. Используйте инкрементальные генераторы, чтобы минимизировать это влияние и избежать генерации ненужного кода.
2. Поддерживаемость
Сгенерированный код должен быть понятным и поддерживаемым. Убедитесь, что сгенерированный код соответствует тем же стандартам кодирования, что и остальная часть проекта. Рассмотрите возможность предоставления документации или комментариев в сгенерированном коде для облегчения будущего обслуживания.
3. Отладка и диагностика
Используйте расширенные возможности диагностики в C#12, чтобы предоставлять понятные сообщения об ошибках и предупреждения в генераторах исходного кода. Это поможет другим разработчикам понять и исправить проблемы, возникающие из-за сгенерированного кода.
4. Управление исходным кодом
Будьте осторожны при проверке сгенерированного кода в системе управления версиями. В большинстве случаев лучше повторно сгенерировать код во время процесса сборки, а не сохранять его в репозитории.
5. Версионирование
При обновлении генераторов кода убедитесь, что сгенерированный код остаётся совместимым с предыдущими версиями. Рассмотрите возможность предоставления путей миграции, если сгенерированный код значительно изменится.
Источник: https://thecodeman.net/posts/source-generators-deep-dive
Разбираем Генераторы Исходного Кода. Окончание
Начало
Чтение кода из файла
Если у вас есть код C# в файле, и вы хотите использовать этот код в для генерации дополнительного или изменения существующего кода, вы можете прочитать содержимое файла и использовать его как часть процесса генерации исходного кода.
Предположим, у есть файл MyClass.cs со следующим содержимым:
// MyClass.cs
namespace MyNamespace;
public class MyClass
{
public void MyMethod()
{
// реализация метода
}
}
В проекте генератора кода читаем содержимое этого файла и генерируем на его основе дополнительный код:
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
[Generator]
public class FileBasedGenerator
: IIncrementalGenerator
{
public void Initialize(
IncrementalGeneratorInitializationContext context)
{
var srcFile = context
.AdditionalTextsProvider
.Where(f => f.Path.EndsWith("MyClass.cs"));
var content = srcFile
.Select((f, ct) => f.GetText(ct)?.ToString());
// используем код из файла
// для генерации нового кода
}
}
В клиентский проект нужно добавить файл MyClass.cs и ссылку на проект генератора кода, как показано в предыдущем посте.
Рекомендации по использованию кодогенерации
1. Производительность
Хотя генераторы исходного кода могут сэкономить время во время разработки, они также могут вносить дополнительные накладные расходы во время компиляции. Используйте инкрементальные генераторы, чтобы минимизировать это влияние и избежать генерации ненужного кода.
2. Поддерживаемость
Сгенерированный код должен быть понятным и поддерживаемым. Убедитесь, что сгенерированный код соответствует тем же стандартам кодирования, что и остальная часть проекта. Рассмотрите возможность предоставления документации или комментариев в сгенерированном коде для облегчения будущего обслуживания.
3. Отладка и диагностика
Используйте расширенные возможности диагностики в C#12, чтобы предоставлять понятные сообщения об ошибках и предупреждения в генераторах исходного кода. Это поможет другим разработчикам понять и исправить проблемы, возникающие из-за сгенерированного кода.
4. Управление исходным кодом
Будьте осторожны при проверке сгенерированного кода в системе управления версиями. В большинстве случаев лучше повторно сгенерировать код во время процесса сборки, а не сохранять его в репозитории.
5. Версионирование
При обновлении генераторов кода убедитесь, что сгенерированный код остаётся совместимым с предыдущими версиями. Рассмотрите возможность предоставления путей миграции, если сгенерированный код значительно изменится.
Источник: https://thecodeman.net/posts/source-generators-deep-dive
👍9
День 2352. #TipsAndTricks
Используем Roslyn Для Улучшения Кода. Начало
Следующий пример проверит все публичные типы решения на предмет, можно ли сделать их internal.
Создадим консольное приложение, и добавим следующие ссылки в .csproj:
Код перебирает типы в решении, проверяет, есть ли ссылки на тип за пределами проекта. Если нет, тип может быть internal:
Источник: https://www.meziantou.net/how-to-find-public-symbols-that-can-be-internal-using-roslyn.htm
Используем Roslyn Для Улучшения Кода. Начало
Следующий пример проверит все публичные типы решения на предмет, можно ли сделать их internal.
Создадим консольное приложение, и добавим следующие ссылки в .csproj:
<PackageReference Include="Microsoft.Build.Locator" Version="1.9.1" />
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="17.14.8" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.14.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.14.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.14.0" />
Код перебирает типы в решении, проверяет, есть ли ссылки на тип за пределами проекта. Если нет, тип может быть internal:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.MSBuild;
var path = @"Sample.sln";
Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults();
var ws = MSBuildWorkspace.Create();
var sln = await
ws.OpenSolutionAsync(path);
// Перебираем проекты в решении
foreach (var proj in sln.Projects)
{
if (!proj.SupportsCompilation)
continue;
var comp = await proj.GetCompilationAsync();
if (comp is null)
continue;
// Проверяем, может ли тип быть internal
foreach (var symb in GetTypes(comp.Assembly))
{
// Вычисляем видимость
var visibility = GetVisibility(symb);
if (visibility is not Visibility.Public)
continue;
var canBeInternal = true;
// Проверяем внешние ссылки
var refs = await SymbolFinder
.FindReferencesAsync(symb, sln);
foreach (var rf in refs)
{
foreach (var loc in rf.Locations)
{
if (loc.Document.Project != proj)
canBeInternal = false;
}
}
if (canBeInternal)
Console.WriteLine(
$"{symb.ToDisplayString()} может быть internal");
}
}
static IEnumerable<ITypeSymbol> GetTypes(IAssemblySymbol assembly)
{
var result = new List<ITypeSymbol>();
foreach (var module in assembly.Modules)
DoNS(result, module.GlobalNamespace);
return result;
static void DoNS(List<ITypeSymbol> result, INamespaceSymbol ns)
{
foreach (var type in ns.GetTypeMembers())
DoType(result, type);
foreach (var nestedNs in ns.GetNamespaceMembers())
DoNS(result, nestedNs);
}
static void DoType(List<ITypeSymbol> result, ITypeSymbol s)
{
result.Add(s);
foreach (var type in s.GetTypeMembers())
DoType(result, type);
}
}
static Visibility GetVisibility(ISymbol s)
{
var vis = Visibility.Public;
switch (s.Kind)
{
case SymbolKind.Alias:
return Visibility.Private;
case SymbolKind.Parameter:
return GetVisibility(s.ContainingSymbol);
case SymbolKind.TypeParameter:
return Visibility.Private;
}
while (s is not null &&
s.Kind != SymbolKind.Namespace)
{
switch (s.DeclaredAccessibility)
{
case Accessibility.NotApplicable:
case Accessibility.Private:
return Visibility.Private;
case Accessibility.Internal:
case Accessibility.ProtectedAndInternal:
vis = Visibility.Internal;
break;
}
s = s.ContainingSymbol;
}
return vis;
}
enum Visibility
{
Public,
Internal,
Private,
}
Источник: https://www.meziantou.net/how-to-find-public-symbols-that-can-be-internal-using-roslyn.htm
👍11
День 2353. #TipsAndTricks
Используем Roslyn Для Улучшения Кода. Окончание
Начало
Вчера мы рассмотрели, как использовать Roslyn для поиска всех типов, которые могут быть обозначены внутренними, вместо публичных. Продолжим эту серию, и сегодня посмотрим, как найти все типы, которые могут быть отмечены как запечатанные (sealed). Обозначение типа как запечатанного может повысить производительность и безопасность, предотвращая дальнейшее наследование. Обратите внимание, что удаление модификатора sealed не является критическим изменением, поэтому вы можете спокойно помечать типы как запечатанные, не беспокоясь о проблемах совместимости, если позже решите удалить модификатор.
Создадим консольное приложение и добавим необходимые NuGet-пакеты в файл .csproj:
Используем следующий код для анализа решения и поиска всех типов, которые можно запечатать. Код переберёт все типы в решении и попросит Roslyn найти производные классы для каждого. Если их нет, тип можно запечатать:
Источник: https://www.meziantou.net/how-to-find-all-types-that-can-be-sealed-using-roslyn.htm
Используем Roslyn Для Улучшения Кода. Окончание
Начало
Вчера мы рассмотрели, как использовать Roslyn для поиска всех типов, которые могут быть обозначены внутренними, вместо публичных. Продолжим эту серию, и сегодня посмотрим, как найти все типы, которые могут быть отмечены как запечатанные (sealed). Обозначение типа как запечатанного может повысить производительность и безопасность, предотвращая дальнейшее наследование. Обратите внимание, что удаление модификатора sealed не является критическим изменением, поэтому вы можете спокойно помечать типы как запечатанные, не беспокоясь о проблемах совместимости, если позже решите удалить модификатор.
Создадим консольное приложение и добавим необходимые NuGet-пакеты в файл .csproj:
<PackageReference Include="Microsoft.Build.Locator" Version="1.9.1" />
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="17.14.8" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.14.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.14.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.14.0" />
Используем следующий код для анализа решения и поиска всех типов, которые можно запечатать. Код переберёт все типы в решении и попросит Roslyn найти производные классы для каждого. Если их нет, тип можно запечатать:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.MSBuild;
var path = @"Sample.sln";
Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults();
var ws = MSBuildWorkspace.Create();
var sln = await ws.OpenSolutionAsync(path);
foreach (var proj in sln.Projects)
{
if (!proj.SupportsCompilation)
continue;
var comp = await proj.GetCompilationAsync();
if (comp is null)
continue;
foreach (var symb in GetTypes(comp.Assembly))
{
if (symb is not INamedTypeSymbol namedType)
continue;
if (namedType.TypeKind is not TypeKind.Class)
continue;
if (namedType.IsSealed)
continue;
var derivedClasses = await
SymbolFinder.FindDerivedClassesAsync(namedType, sln);
if (!derivedClasses.Any())
Console.WriteLine(
$"{symb.ToDisplayString()} может быть sealed");
}
}
static IEnumerable<ITypeSymbol>
GetTypes(IAssemblySymbol assembly)
{
var result = new List<ITypeSymbol>();
foreach (var module in assembly.Modules)
DoNS(result, module.GlobalNamespace);
return result;
static void DoNS(
List<ITypeSymbol> result,
INamespaceSymbol ns)
{
foreach (var t in ns.GetTypeMembers())
DoType(result, t);
foreach (var ns in ns.GetNamespaceMembers())
DoNS(result, ns);
}
static void DoType(
List<ITypeSymbol> result,
ITypeSymbol symb)
{
result.Add(symb);
foreach (var type in symb.GetTypeMembers())
DoType(result, type);
}
}
Источник: https://www.meziantou.net/how-to-find-all-types-that-can-be-sealed-using-roslyn.htm
👍8
День 2354. #ЧтоНовенького
ToListAsync в Entity Framework Снова Быстрый
Если вы извлекаете очень большую строку NVARCHAR(MAX) через await ToListAsync(), Entity Framework будет делать что-то непонятное, что приводит к медленному запросу и большому объёму выделения памяти. На самом деле, виновник не совсем Entity Framework. Основную работу выполняет базовый Microsoft.Data.SqlClient. Проблема в том, что он неправильно использует SqlDataReader.
Замечание: это касается в основном пакета/реализации для SqlServer, поскольку он использует пакет Microsoft.Data.SqlClient.
Вот вырезка из бенчмарка от @Wraith2:
Microsoft.Data.SqlClient 6.1.0
@Wraith2 исправил проблему серией пул-реквестов, на протяжении более 5 лет! Это невероятно. Конечно, он не работал над исправлениями непрерывно 5 лет, но всё равно это очень впечатляет. Последние заметки о выпуске можно прочитать здесь. Обратите внимание на материалы "Added packet multiplexing support to improve large data read performance" (Добавлена поддержка мультиплексирования пакетов для повышения производительности чтения больших объёмов данных).
А пул-реквест, исправляющий проблему, находится здесь.
Результаты такие:
Разрыв в выделении памяти в 20% всё ещё сохраняется, но время уже сопоставимо. Wraith2 и команда проделали потрясающую работу. Вы можете протестировать превью версию пакета, даже используя EF, поскольку прямые зависимости переопределяют транзитивные. Либо подождите следующего релиза EF, включающего новую версию Microsoft.Data.SqlClient 6.1.0, который должен уже скоро выйти.
Источник: https://steven-giesel.com/blogPost/dd7ab934-c311-45e0-9a04-9a28c624b957/tolistasync-is-fast-again-in-entity-framework-kind-of
ToListAsync в Entity Framework Снова Быстрый
Если вы извлекаете очень большую строку NVARCHAR(MAX) через await ToListAsync(), Entity Framework будет делать что-то непонятное, что приводит к медленному запросу и большому объёму выделения памяти. На самом деле, виновник не совсем Entity Framework. Основную работу выполняет базовый Microsoft.Data.SqlClient. Проблема в том, что он неправильно использует SqlDataReader.
Замечание: это касается в основном пакета/реализации для SqlServer, поскольку он использует пакет Microsoft.Data.SqlClient.
Вот вырезка из бенчмарка от @Wraith2:
| Method | Mean | Allocated |
|------- |----------:|----------:|
| Async | 757.51 ms | 101.49 MB |
| Sync | 39.40 ms | 80.14 MB |
Microsoft.Data.SqlClient 6.1.0
@Wraith2 исправил проблему серией пул-реквестов, на протяжении более 5 лет! Это невероятно. Конечно, он не работал над исправлениями непрерывно 5 лет, но всё равно это очень впечатляет. Последние заметки о выпуске можно прочитать здесь. Обратите внимание на материалы "Added packet multiplexing support to improve large data read performance" (Добавлена поддержка мультиплексирования пакетов для повышения производительности чтения больших объёмов данных).
А пул-реквест, исправляющий проблему, находится здесь.
Результаты такие:
| Async | 49.45 ms | 101.51 MB |
| Sync | 40.09 ms | 80.14 MB |
Разрыв в выделении памяти в 20% всё ещё сохраняется, но время уже сопоставимо. Wraith2 и команда проделали потрясающую работу. Вы можете протестировать превью версию пакета, даже используя EF, поскольку прямые зависимости переопределяют транзитивные. Либо подождите следующего релиза EF, включающего новую версию Microsoft.Data.SqlClient 6.1.0, который должен уже скоро выйти.
Источник: https://steven-giesel.com/blogPost/dd7ab934-c311-45e0-9a04-9a28c624b957/tolistasync-is-fast-again-in-entity-framework-kind-of
👍20
День 2355. #УрокиРазработки
Уроки 50 Лет Разработки ПО
Урок 57. Адаптируйте готовые шаблоны документов. Начало
Простые списки функций, запрашиваемых клиентами – хорошее начало для упорядочивания спецификации. Однако, есть шаблон спецификации требований к ПО (Software Requirements Specification, SRS), описанный в уже устаревшем стандарте IEEE 830. Шаблон содержит множество разделов, которые помогут систематизировать разнообразную информацию о требованиях. Однако, это не догма. Его можно и нужно менять, чтобы он лучше подходил для разрабатываемых вами систем.
Шаблоны документов предлагают несколько преимуществ:
- определяют согласованные способы организации проектной информации, что облегчает людям, работающим с этими документами, поиск нужной информации.
- могут выявить потенциальные пробелы в знаниях автора документа о проекте, напомнить об информации, которую, возможно, следует добавить.
Предположим, вы решили использовать шаблон, приведённый выше, для структурирования информации о требованиях к новой системе. Не заполняйте его сверху вниз. Пишите определённые разделы по мере накопления соответствующей информации. Возможно, через какое-то время вы заметите, что раздел 2.5 «Допущения и зависимости» не заполнен. Это побудит задаться вопросом, есть ли какая-то недостающая информация о предположениях и зависимостях, которую нужно выяснить. Возможно, нужно побеседовать с определёнными заинтересованными сторонами. Может быть, никто ещё не указал на какие-либо вероятные предположения или зависимости и следует их определить. Некоторые допущения или зависимости могли быть записаны где-то еще; тогда, возможно, их следует переместить в этот раздел или добавить ссылки на них. Или, может быть, действительно нет никаких известных предположений или зависимостей. Пустой раздел напоминает, что вам ещё есть над чем поработать.
Кроме того, нужно подумать, что делать, если определённый раздел неактуален для вашего проекта. Один из вариантов — просто удалить его из документа с требованиями по завершении работы. Но отсутствие раздела может вызвать у читателя вопрос: «Я не увидел ничего о допущениях и зависимостях. Есть ли такие? Спрошу-ка я у кого-нибудь». Конечно, можно сохранить заголовок раздела и оставить сам раздел пустым, но это заставит читателя задаться вопросом, завершён ли документ. Лучше оставить заголовок и добавить пояснение: «Для этого проекта не было выявлено никаких допущений или зависимостей». Явное сообщение вызывает меньше путаницы, чем неявное.
Окончание следует…
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 7.
Уроки 50 Лет Разработки ПО
Урок 57. Адаптируйте готовые шаблоны документов. Начало
Простые списки функций, запрашиваемых клиентами – хорошее начало для упорядочивания спецификации. Однако, есть шаблон спецификации требований к ПО (Software Requirements Specification, SRS), описанный в уже устаревшем стандарте IEEE 830. Шаблон содержит множество разделов, которые помогут систематизировать разнообразную информацию о требованиях. Однако, это не догма. Его можно и нужно менять, чтобы он лучше подходил для разрабатываемых вами систем.
РАСШИРЕННЫЙ ШАБЛОН СПЕЦИФИКАЦИИ ТРЕБОВАНИЙ К ПО
1. Введение.
1.1. Цель.
1.2. Условные обозначения.
1.3. Сфера применения проекта.
1.4. Ссылки.
2. Общее описание.
2.1. Перспектива продукта.
2.2. Классы и характеристики пользователей.
2.3. Операционная среда.
2.4. Ограничения и реализация.
2.5. Допущения и зависимости.
3. Системные функции.
3.x. Системная функция X.
3.x.1. Описание.
3.x.2. Функциональные требования.
4. Требования к данным.
4.1. Логическая модель данных.
4.2. Словарь данных.
4.3. Отчеты.
4.4. Целостность, хранение и удаление данных.
5. Требования к внешнему интерфейсу.
5.1. Пользовательские интерфейсы.
5.2. Программные интерфейсы.
5.3. Аппаратные интерфейсы.
5.4. Коммуникационные интерфейсы.
6. Атрибуты качества.
6.1. Удобство использования.
6.2. Производительность.
6.3. Безопасность.
6.4. Защищенность.
6.x. [другие].
7. Требования к интернационализации и локализации.
8. Другие требования.
Приложение A. Глоссарий.
Приложение Б. Модели анализа.
Шаблоны документов предлагают несколько преимуществ:
- определяют согласованные способы организации проектной информации, что облегчает людям, работающим с этими документами, поиск нужной информации.
- могут выявить потенциальные пробелы в знаниях автора документа о проекте, напомнить об информации, которую, возможно, следует добавить.
Предположим, вы решили использовать шаблон, приведённый выше, для структурирования информации о требованиях к новой системе. Не заполняйте его сверху вниз. Пишите определённые разделы по мере накопления соответствующей информации. Возможно, через какое-то время вы заметите, что раздел 2.5 «Допущения и зависимости» не заполнен. Это побудит задаться вопросом, есть ли какая-то недостающая информация о предположениях и зависимостях, которую нужно выяснить. Возможно, нужно побеседовать с определёнными заинтересованными сторонами. Может быть, никто ещё не указал на какие-либо вероятные предположения или зависимости и следует их определить. Некоторые допущения или зависимости могли быть записаны где-то еще; тогда, возможно, их следует переместить в этот раздел или добавить ссылки на них. Или, может быть, действительно нет никаких известных предположений или зависимостей. Пустой раздел напоминает, что вам ещё есть над чем поработать.
Кроме того, нужно подумать, что делать, если определённый раздел неактуален для вашего проекта. Один из вариантов — просто удалить его из документа с требованиями по завершении работы. Но отсутствие раздела может вызвать у читателя вопрос: «Я не увидел ничего о допущениях и зависимостях. Есть ли такие? Спрошу-ка я у кого-нибудь». Конечно, можно сохранить заголовок раздела и оставить сам раздел пустым, но это заставит читателя задаться вопросом, завершён ли документ. Лучше оставить заголовок и добавить пояснение: «Для этого проекта не было выявлено никаких допущений или зависимостей». Явное сообщение вызывает меньше путаницы, чем неявное.
Окончание следует…
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 7.
👍4
День 2356. #УрокиРазработки
Уроки 50 Лет Разработки ПО
Урок 57. Адаптируйте готовые шаблоны документов. Окончание
Начало
Создание подходящего шаблона с чистого листа идёт медленно и бессистемно. Лучше начать с универсального шаблона, а затем адаптировать его к размеру, характеру и потребностям каждого проекта. Многие технические стандарты описывают шаблоны документов. К организациям, выпускающим технические стандарты по разработке ПО, относятся:
- Институт инженеров по электротехнике и электронике (Institute of Electrical and Electronics Engineers, IEEE);
- Международная организация по стандартизации (International Organization for Standardization, ISO);
- Международная электротехническая комиссия (International Electrotechnical Commission, IEC)
Например, международный стандарт ISO/IEC/IEEE 29148 содержит предлагаемые шаблоны для описания ПО, заинтересованных сторон и спецификаций системных требований (ISO/IEC/IEEE 2018). В сети вы найдёте множество шаблонов различных документов.
Поскольку такие общие шаблоны предназначены для широкого круга проектов, они могут вам не подойти. Но они дадут много идей об информации, которую следует добавить, и способах её организации. Концепция использования готовых шаблонов подразумевает возможность адаптировать эти шаблоны к вашей ситуации:
- Удалите разделы, которые вам не нужны.
- Добавьте разделы, которых нет в шаблоне, но которые помогут вашему проекту.
- Упростите или объедините разделы шаблона, если это не вызовет путаницы.
- Измените терминологию в соответствии с вашим проектом или культурой.
- Реорганизуйте содержимое шаблона, чтобы оно лучше соответствовало потребностям вашей аудитории.
- Разделите или объедините шаблоны связанных документов, если это целесообразно.
Если ваша организация работает над проектами нескольких классов или размеров, то создайте наборы шаблонов, подходящие для каждого класса.
Компании добиваются успеха не потому, что пишут отличные спецификации или планы, а потому, что создают высококачественные информационные системы или коммерческие приложения. Хорошо составленные ключевые документы могут способствовать этому успеху. Некоторые люди не используют шаблоны, опасаясь, что они наложат на проект излишние ограничения. Они могут быть обеспокоены тем, что команда сосредоточится на заполнении шаблона, а не на создании продукта. Если у вас нет договорных требований, то вы не обязаны заполнять каждый раздел шаблона. И, конечно же, не обязаны заполнять шаблон до начала разработки. Но, даже если ваша организация не использует документы для хранения информации, при разработке проекта всё равно нужно записывать и сохранять определённые знания в какой-либо форме. Вы можете использовать контрольные списки вместо шаблонов, чтобы не упустить из виду что-то важное. Контрольный список также помогает оценить, насколько полным является набор информации, однако не помогает систематизировать её согласованным образом.
Многие организации хранят требования и другие сведения о проекте в определённом инструменте. Такие инструменты позволяют определять шаблоны для хранимых объектов данных. При необходимости пользователи могут создавать документы в виде отчётов на основе содержимого базы данных инструмента. Всем, кто использует такой инструмент, важно понимать, что он является основным хранилищем текущей информации. Сгенерированный документ — это лишь моментальный снимок содержимого базы данных, который завтра может устареть.
Шаблоны, контрольные списки и формы ценны, т.к. избавляют вас от необходимости заново изобретать способы работы над каждым проектом. Продуманные шаблоны напоминают вам и вашим коллегам о том, как наиболее эффективно внести свой вклад в проект.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 7.
Уроки 50 Лет Разработки ПО
Урок 57. Адаптируйте готовые шаблоны документов. Окончание
Начало
Создание подходящего шаблона с чистого листа идёт медленно и бессистемно. Лучше начать с универсального шаблона, а затем адаптировать его к размеру, характеру и потребностям каждого проекта. Многие технические стандарты описывают шаблоны документов. К организациям, выпускающим технические стандарты по разработке ПО, относятся:
- Институт инженеров по электротехнике и электронике (Institute of Electrical and Electronics Engineers, IEEE);
- Международная организация по стандартизации (International Organization for Standardization, ISO);
- Международная электротехническая комиссия (International Electrotechnical Commission, IEC)
Например, международный стандарт ISO/IEC/IEEE 29148 содержит предлагаемые шаблоны для описания ПО, заинтересованных сторон и спецификаций системных требований (ISO/IEC/IEEE 2018). В сети вы найдёте множество шаблонов различных документов.
Поскольку такие общие шаблоны предназначены для широкого круга проектов, они могут вам не подойти. Но они дадут много идей об информации, которую следует добавить, и способах её организации. Концепция использования готовых шаблонов подразумевает возможность адаптировать эти шаблоны к вашей ситуации:
- Удалите разделы, которые вам не нужны.
- Добавьте разделы, которых нет в шаблоне, но которые помогут вашему проекту.
- Упростите или объедините разделы шаблона, если это не вызовет путаницы.
- Измените терминологию в соответствии с вашим проектом или культурой.
- Реорганизуйте содержимое шаблона, чтобы оно лучше соответствовало потребностям вашей аудитории.
- Разделите или объедините шаблоны связанных документов, если это целесообразно.
Если ваша организация работает над проектами нескольких классов или размеров, то создайте наборы шаблонов, подходящие для каждого класса.
Компании добиваются успеха не потому, что пишут отличные спецификации или планы, а потому, что создают высококачественные информационные системы или коммерческие приложения. Хорошо составленные ключевые документы могут способствовать этому успеху. Некоторые люди не используют шаблоны, опасаясь, что они наложат на проект излишние ограничения. Они могут быть обеспокоены тем, что команда сосредоточится на заполнении шаблона, а не на создании продукта. Если у вас нет договорных требований, то вы не обязаны заполнять каждый раздел шаблона. И, конечно же, не обязаны заполнять шаблон до начала разработки. Но, даже если ваша организация не использует документы для хранения информации, при разработке проекта всё равно нужно записывать и сохранять определённые знания в какой-либо форме. Вы можете использовать контрольные списки вместо шаблонов, чтобы не упустить из виду что-то важное. Контрольный список также помогает оценить, насколько полным является набор информации, однако не помогает систематизировать её согласованным образом.
Многие организации хранят требования и другие сведения о проекте в определённом инструменте. Такие инструменты позволяют определять шаблоны для хранимых объектов данных. При необходимости пользователи могут создавать документы в виде отчётов на основе содержимого базы данных инструмента. Всем, кто использует такой инструмент, важно понимать, что он является основным хранилищем текущей информации. Сгенерированный документ — это лишь моментальный снимок содержимого базы данных, который завтра может устареть.
Шаблоны, контрольные списки и формы ценны, т.к. избавляют вас от необходимости заново изобретать способы работы над каждым проектом. Продуманные шаблоны напоминают вам и вашим коллегам о том, как наиболее эффективно внести свой вклад в проект.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 7.
День 2357. #ЧтоНовенького #NET10
Разбираем Возможности dotnet run app.cs. Начало
В этой серии подробно разберём новую функцию, появившуюся в .NET 10, для сборки и запуска одного C#-файла без необходимости предварительного создания проекта .csproj. Похоже, у неё пока нет окончательного названия. В некоторых источниках её называют однофайловым приложением (file-based app), иногда - «runfile».
Что это?
В .NET 10 добавлена возможность сохранить код в один файл .cs и запустить его, выполнив команду
См. представление новой функции здесь.
Доступные функции
Пока возможности относительно ограничены, но есть несколько точек расширения. Следующий пример демонстрирует простое хост-приложение Aspire без операций, реализованное в виде однофайлового приложения. Он ничего не делает, а просто демонстрирует все функции, доступные в .NET 10 превью 5:
Этот скрипт демонстрирует все новые директивы, доступные в режиме запуска однофайлового приложения.
1. Создание исполняемого файла с помощью шебанг
#! (шебанг) и представляет директиву для систем *nix, позволяющую запускать файл напрямую. См. подробнее.
2. Добавление ссылок на SDK
Возможно с помощью директивы #:sdk. Также, как видите, можно указать версию. Заметьте, что в превью 5 она указывается через пробел, но, возможно, синтаксис поменяется для соответствия синтаксису добавления NuGet-пакетов.
3. Добавление ссылок на NuGet-пакеты
Возможно с помощью директивы #:package. Версия указывается после @. Можно использовать подстановочный знак (*) для версий:
Подстановочный знак обычно выберет наивысшую версию пакета.
4. Обновление свойств MSBuild
#:property используется для определения свойств MSBuild для приложения. Можно добавить любые свойства, которые обычно определяются в <PropertyGroup> файла .csproj. Здесь синтаксис, скорее всего тоже изменится. Вместо пробела нужно будет использовать =.
5. Ссылки на проекты (скоро)
В .NET 10 превью 6 должна появиться возможность ссылаться на проекты с помощью директивы #:project
Возможность ссылаться на каталог проекта вместо полного пути к файлу .csproj — отличный способ уменьшить дублирование.
Продолжение следует…
Источник: https://andrewlock.net/exploring-dotnet-10-preview-features-1-exploring-the-dotnet-run-app.cs/
Разбираем Возможности dotnet run app.cs. Начало
В этой серии подробно разберём новую функцию, появившуюся в .NET 10, для сборки и запуска одного C#-файла без необходимости предварительного создания проекта .csproj. Похоже, у неё пока нет окончательного названия. В некоторых источниках её называют однофайловым приложением (file-based app), иногда - «runfile».
Что это?
В .NET 10 добавлена возможность сохранить код в один файл .cs и запустить его, выполнив команду
dotnet run app.cs
См. представление новой функции здесь.
Доступные функции
Пока возможности относительно ограничены, но есть несколько точек расширения. Следующий пример демонстрирует простое хост-приложение Aspire без операций, реализованное в виде однофайлового приложения. Он ничего не делает, а просто демонстрирует все функции, доступные в .NET 10 превью 5:
#!/usr/bin/dotnet run
#:sdk Microsoft.NET.Sdk
#:sdk Aspire.AppHost.Sdk 9.3.0
#:package Aspire.Hosting.AppHost@9.3.0
#:property UserSecretsId 2eec9746-c21a-4933-90af-c22431f35459
using Microsoft.Extensions.Configuration;
var builder = DistributedApplication.CreateBuilder(args);
builder.Configuration.AddInMemoryCollection(new Dictionary<string, string?>
{
{ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL", "https://localhost:21049" },
{ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL", "https://localhost:22001" },
{ "ASPNETCORE_URLS", "https://localhost:17246" },
});
builder.Build().Run();
Этот скрипт демонстрирует все новые директивы, доступные в режиме запуска однофайлового приложения.
1. Создание исполняемого файла с помощью шебанг
#! (шебанг) и представляет директиву для систем *nix, позволяющую запускать файл напрямую. См. подробнее.
2. Добавление ссылок на SDK
Возможно с помощью директивы #:sdk. Также, как видите, можно указать версию. Заметьте, что в превью 5 она указывается через пробел, но, возможно, синтаксис поменяется для соответствия синтаксису добавления NuGet-пакетов.
3. Добавление ссылок на NuGet-пакеты
Возможно с помощью директивы #:package. Версия указывается после @. Можно использовать подстановочный знак (*) для версий:
#:package Aspire.Hosting.AppHost@*
#:package Aspire.Hosting.AppHost@9.*
#:package Aspire.Hosting.AppHost@9.3.*
Подстановочный знак обычно выберет наивысшую версию пакета.
4. Обновление свойств MSBuild
#:property используется для определения свойств MSBuild для приложения. Можно добавить любые свойства, которые обычно определяются в <PropertyGroup> файла .csproj. Здесь синтаксис, скорее всего тоже изменится. Вместо пробела нужно будет использовать =.
5. Ссылки на проекты (скоро)
В .NET 10 превью 6 должна появиться возможность ссылаться на проекты с помощью директивы #:project
#:project ../src/MyProject
#:project ../src/MyProject/MyProject.csproj
Возможность ссылаться на каталог проекта вместо полного пути к файлу .csproj — отличный способ уменьшить дублирование.
Продолжение следует…
Источник: https://andrewlock.net/exploring-dotnet-10-preview-features-1-exploring-the-dotnet-run-app.cs/
👍7
День 2358. #ЧтоНовенького #NET10
Разбираем Возможности dotnet run app.cs. Продолжение
Начало
Зачем?
Прежде всего, команда .NET ясно дала понять, что это делается для того, чтобы сделать обучение новичков .NET максимально удобным. Во многих других языках, будь то Node.js или Python, например, есть однофайловый интерфейс, а теперь он есть и в .NET. Новичок теперь может начать просто с файла .cs, и постепенно вводить новые концепции.
Постепенно вы дойдёте до точки, когда будет иметь смысл создать проект, например, для объединения нескольких CS-файлов. Тогда можно просто преобразовать отдельный файл в проект, выполнив:
Выполнение этой команды на примере из предыдущего поста создаст файл проекта, который выглядит следующим образом:
Все добавленные директивы включены в проект, а также добавлены другие значения по умолчанию. Это очень плавный переход от файловых приложений к файлам проектов.
Кроме того, есть несколько сценариев, где избавление от необходимости в отдельном проекте (и соответствующем каталоге) имеет смысл. Например:
1. Сервисные скрипты. Раньше вы, вероятно, использовали бы bash или PowerShell, но теперь, если хотите, можете легко использовать C#.
2. Примеры. Многие библиотеки или фреймворки предлагают несколько примеров приложений для демонстрации функций, каждому из которых требуется отдельная папка и файл проекта. Теперь у вас может быть одна папка, где каждый CS-файл будет примером приложения.
Дополнительные функции
Также существуют различные файлы, которые однофайловое приложение будет неявно использовать, если они доступны. К ним относятся:
- global.json
- NuGet.config
- Directory.Build.props
- Directory.Build.targets
- Directory.Packages.props
- Directory.Build.rsp
- MSBuild.rsp
Пожалуй, самый полезный из этих файлов — Directory.Build.props, который, по сути, позволяет «улучшить» ваше однофайловое приложение, добавив всё, что вы обычно помещаете в файл .csproj. Это особенно полезно, если у вас, например, есть несколько однофайловых приложений в каталоге, и вы хотите задать свойство или добавить пакет для всех, не обновляя каждое из них.
Это всё немного абстрактно, но вы можете увидеть различные примеры подобных вещей в репозитории «runfile Playground» Дэмиана Эдвардса, посвящённом этой функции!
Окончание следует…
Источник: https://andrewlock.net/exploring-dotnet-10-preview-features-1-exploring-the-dotnet-run-app.cs/
Разбираем Возможности dotnet run app.cs. Продолжение
Начало
Зачем?
Прежде всего, команда .NET ясно дала понять, что это делается для того, чтобы сделать обучение новичков .NET максимально удобным. Во многих других языках, будь то Node.js или Python, например, есть однофайловый интерфейс, а теперь он есть и в .NET. Новичок теперь может начать просто с файла .cs, и постепенно вводить новые концепции.
Постепенно вы дойдёте до точки, когда будет иметь смысл создать проект, например, для объединения нескольких CS-файлов. Тогда можно просто преобразовать отдельный файл в проект, выполнив:
dotnet project convert app.cs
Выполнение этой команды на примере из предыдущего поста создаст файл проекта, который выглядит следующим образом:
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Aspire.AppHost.Sdk" Version="9.3.0" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<UserSecretsId>2eec9746-c21a-4933-90af-c22431f35459</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.3." />
</ItemGroup>
</Project>
Все добавленные директивы включены в проект, а также добавлены другие значения по умолчанию. Это очень плавный переход от файловых приложений к файлам проектов.
Кроме того, есть несколько сценариев, где избавление от необходимости в отдельном проекте (и соответствующем каталоге) имеет смысл. Например:
1. Сервисные скрипты. Раньше вы, вероятно, использовали бы bash или PowerShell, но теперь, если хотите, можете легко использовать C#.
2. Примеры. Многие библиотеки или фреймворки предлагают несколько примеров приложений для демонстрации функций, каждому из которых требуется отдельная папка и файл проекта. Теперь у вас может быть одна папка, где каждый CS-файл будет примером приложения.
Дополнительные функции
Также существуют различные файлы, которые однофайловое приложение будет неявно использовать, если они доступны. К ним относятся:
- global.json
- NuGet.config
- Directory.Build.props
- Directory.Build.targets
- Directory.Packages.props
- Directory.Build.rsp
- MSBuild.rsp
Пожалуй, самый полезный из этих файлов — Directory.Build.props, который, по сути, позволяет «улучшить» ваше однофайловое приложение, добавив всё, что вы обычно помещаете в файл .csproj. Это особенно полезно, если у вас, например, есть несколько однофайловых приложений в каталоге, и вы хотите задать свойство или добавить пакет для всех, не обновляя каждое из них.
Это всё немного абстрактно, но вы можете увидеть различные примеры подобных вещей в репозитории «runfile Playground» Дэмиана Эдвардса, посвящённом этой функции!
Окончание следует…
Источник: https://andrewlock.net/exploring-dotnet-10-preview-features-1-exploring-the-dotnet-run-app.cs/
👍5
👩💻 DotNet Developer Blog – практические кейсы и реальные решения!
В Telegram-канале DotNet Developer Blog публикуются реальные примеры и опыт .NET-разработки:
✅ Как писать надежные и отказоустойчивые приложения на .NET
✅ Тестирование, в т.ч. с помощью Copilot: ускоряем написание тестов и повышаем качество кода
✅ Рефакторинг и переезд с легаси – разбор реальных кейсов
✅ Лучшие практики и реальные примеры из продакшена
💡 Без воды – только практические решения и полезные инсайты!
Подписывайся 👉 @dotnetdevblog
Действительно полезные посты каждую неделю
#реклама
В Telegram-канале DotNet Developer Blog публикуются реальные примеры и опыт .NET-разработки:
✅ Как писать надежные и отказоустойчивые приложения на .NET
✅ Тестирование, в т.ч. с помощью Copilot: ускоряем написание тестов и повышаем качество кода
✅ Рефакторинг и переезд с легаси – разбор реальных кейсов
✅ Лучшие практики и реальные примеры из продакшена
💡 Без воды – только практические решения и полезные инсайты!
Подписывайся 👉 @dotnetdevblog
Действительно полезные посты каждую неделю
#реклама
👍11👎2
День 2359. #ЧтоНовенького #NET10
Разбираем Возможности dotnet run app.cs. Окончание
Начало
Продолжение
Наконец, посмотрим, что ещё готовится для однофайловых приложений.
Нет гарантии, что описанные ниже функции попадут в финальную версию, но есть большие шансы на это, учитывая, что первые несколько функций уже объединены в ветку следующего превью.
1. Публикация однофайловых приложений
Одна из функций, которая должна появиться в превью 6, — это возможность публиковать однофайловые приложения с помощью:
При этом по умолчанию приложения будут публиковаться как приложения NativeAOT! Вы можете отключить это, добавив #:property PublishAot false, но, скорее всего, всё будет работать без проблем во многих сценариях, для которых предназначены однофайловые приложения.
2. Запуск через dotnet app.cs
Поддержка запуска однофайловых приложений без использования команды run, т.е. вы можете использовать
вместо
Одно из главных преимуществ этого подхода заключается в том, что он делает поддержку шебангов в Linux более надёжной. Например, если вы хотите использовать /usr/bin/env для поиска исполняемого файла dotnet, вместо того, чтобы предполагать, что он находится в /usr/bin/dotnet, раньше нужно было сделать что-то вроде этого:
К сожалению, из-за того, что здесь приходится предоставлять несколько аргументов ("dotnet run"), это может не работать в некоторых оболочках. Однако с новой поддержкой dotnet app.cs вы можете использовать более простой и широко поддерживаемый вариант:
3. Запуск C# напрямую из стандартного ввода
Недавно была добавлена поддержка конвейеризации C#-кода напрямую в dotnet run, что позволяет выполнять такие действия:
Это перенаправляет приложение Hello World прямо из консоли в dotnet run и запускает его. Классический случай, когда «так делать ни в коем случае нельзя, но люди постоянно это делают», — скачиваем код из сети через curl и запускаем его напрямую - теперь возможен:
Чего не будет?
1. Одна из важных функций, которая не появится в .NET 10, — это поддержка нескольких файлов. Изначально планировалось включить её, причём такие вещи, как «вложенные» файлы и подкаталоги, которые неявно включались в компиляцию. Вместо этого эта работа была перенесена на .NET 11, чтобы сосредоточиться на максимальном улучшении взаимодействия с одним файлом.
Вы можете косвенно получить поддержку нескольких файлов, используя Directory.Build.props и Directory.Build.targets, а также добавляя ссылки на файлы «вручную».
2. Поддержка отдельных файлов не появится в Visual Studio. Поддержка от Microsoft будет реализована только в Visual Studio Code (и, разумеется, в CLI).
3. На данном этапе поддержка отдельных файлов будет реализована только для файлов .cs, а не для файлов .vb или .fs. Команда разработчиков не исключает полностью эту возможность, но маловероятно, что Microsoft сами добавят такую поддержку.
Источник: https://andrewlock.net/exploring-dotnet-10-preview-features-1-exploring-the-dotnet-run-app.cs/
Разбираем Возможности dotnet run app.cs. Окончание
Начало
Продолжение
Наконец, посмотрим, что ещё готовится для однофайловых приложений.
Нет гарантии, что описанные ниже функции попадут в финальную версию, но есть большие шансы на это, учитывая, что первые несколько функций уже объединены в ветку следующего превью.
1. Публикация однофайловых приложений
Одна из функций, которая должна появиться в превью 6, — это возможность публиковать однофайловые приложения с помощью:
dotnet publish app.cs
При этом по умолчанию приложения будут публиковаться как приложения NativeAOT! Вы можете отключить это, добавив #:property PublishAot false, но, скорее всего, всё будет работать без проблем во многих сценариях, для которых предназначены однофайловые приложения.
2. Запуск через dotnet app.cs
Поддержка запуска однофайловых приложений без использования команды run, т.е. вы можете использовать
dotnet app.cs
вместо
dotnet run app.cs
Одно из главных преимуществ этого подхода заключается в том, что он делает поддержку шебангов в Linux более надёжной. Например, если вы хотите использовать /usr/bin/env для поиска исполняемого файла dotnet, вместо того, чтобы предполагать, что он находится в /usr/bin/dotnet, раньше нужно было сделать что-то вроде этого:
#!/usr/bin/env dotnet run
К сожалению, из-за того, что здесь приходится предоставлять несколько аргументов ("dotnet run"), это может не работать в некоторых оболочках. Однако с новой поддержкой dotnet app.cs вы можете использовать более простой и широко поддерживаемый вариант:
#!/usr/bin/env dotnet
3. Запуск C# напрямую из стандартного ввода
Недавно была добавлена поддержка конвейеризации C#-кода напрямую в dotnet run, что позволяет выполнять такие действия:
> 'Console.WriteLine("Hello, World!");' | dotnet run -
Hello, World!
Это перенаправляет приложение Hello World прямо из консоли в dotnet run и запускает его. Классический случай, когда «так делать ни в коем случае нельзя, но люди постоянно это делают», — скачиваем код из сети через curl и запускаем его напрямую - теперь возможен:
> curl -S http://totally-safe-not-scary-at-all.com/ | dotnet run -
All your bases are belong to us!
Чего не будет?
1. Одна из важных функций, которая не появится в .NET 10, — это поддержка нескольких файлов. Изначально планировалось включить её, причём такие вещи, как «вложенные» файлы и подкаталоги, которые неявно включались в компиляцию. Вместо этого эта работа была перенесена на .NET 11, чтобы сосредоточиться на максимальном улучшении взаимодействия с одним файлом.
Вы можете косвенно получить поддержку нескольких файлов, используя Directory.Build.props и Directory.Build.targets, а также добавляя ссылки на файлы «вручную».
2. Поддержка отдельных файлов не появится в Visual Studio. Поддержка от Microsoft будет реализована только в Visual Studio Code (и, разумеется, в CLI).
3. На данном этапе поддержка отдельных файлов будет реализована только для файлов .cs, а не для файлов .vb или .fs. Команда разработчиков не исключает полностью эту возможность, но маловероятно, что Microsoft сами добавят такую поддержку.
Источник: https://andrewlock.net/exploring-dotnet-10-preview-features-1-exploring-the-dotnet-run-app.cs/
👍6
День 2360. #ЗаметкиНаПолях
Улучшаем Обработку Ошибок в Минимальных API с ProblemDetails
Будем честны: обработка ошибок — обычно последнее, о чём мы думаем при разработке API. Но она должна быть одной из первых.
Представьте, фронтенд вызывает API и получает в ответ следующее: "Object reference not set to an instance of an object." Вряд ли это сообщение ясно и полезно. Для сравнения:
Это полезно и понятно. Именно это нам и даёт ProblemDetails.
Что это?
Стандартный способ возврата сообщений об ошибках в API, определённый в RFC 7807. Вместо случайного текста или несогласованного JSON вы возвращаете структурированные ошибки, например:
В ASP.NET есть встроенная поддержка ProblemDetails, и она прекрасно работает и в минимальных API. Создадим пример минимального API, который
получает продукт по ID и возвращает ошибки, используя ProblemDetails.
Теперь запрос несуществующего продукта вернёт стандартный ответ ProblemDetails.
Дополнительные поля
Вы можете расширять ProblemDetails дополнительными данными:
Затем возвращайте его через Results.Problem(…) и передавайте дополнительные метаданные.
Преимущества
- Чистые ответы об ошибках;
- Легкость для понимания фронтендерами;
- Стандарт (RFC 7807);
- Встроено в .NET.
Глобальную обработку ошибок, начиная с .NET 8, можно настроить с помощью IExceptionHandler, который также будет выдавать ProblemDetails.
Источник: https://thecodeman.net/posts/better-error-handling-with-problemdetails
Улучшаем Обработку Ошибок в Минимальных API с ProblemDetails
Будем честны: обработка ошибок — обычно последнее, о чём мы думаем при разработке API. Но она должна быть одной из первых.
Представьте, фронтенд вызывает API и получает в ответ следующее: "Object reference not set to an instance of an object." Вряд ли это сообщение ясно и полезно. Для сравнения:
{
"title": "Что-то пошло не так.",
"status": 500,
"detail": "Пожалуйста, свяжитесь с поддержкой.",
"instance": "/products/0"
}
Это полезно и понятно. Именно это нам и даёт ProblemDetails.
Что это?
Стандартный способ возврата сообщений об ошибках в API, определённый в RFC 7807. Вместо случайного текста или несогласованного JSON вы возвращаете структурированные ошибки, например:
{
"title": "Product not found",
"status": 404,
"detail": "No product with ID 42.",
"instance": "/products/42"
}
В ASP.NET есть встроенная поддержка ProblemDetails, и она прекрасно работает и в минимальных API. Создадим пример минимального API, который
получает продукт по ID и возвращает ошибки, используя ProblemDetails.
public record Product(int Id, string Name);
…
// получаем продукт
app.MapGet("/products/{id:int}", (int id, HttpContext http) =>
{
var prod = context.Products
.FirstOrDefault(p => p.Id == id);
if (prod is null)
{
var notFound = new ProblemDetails
{
Title = "Продукт не найден",
Status = StatusCodes.Status404NotFound,
Detail = $"Продукт с ID={id} не найден.",
Instance = http.Request.Path
};
return Results.Problem(
title: notFound.Title,
detail: notFound.Detail,
statusCode: notFound.Status,
instance: notFound.Instance
);
}
return Results.Ok(prod);
});
app.Run();
Теперь запрос несуществующего продукта вернёт стандартный ответ ProblemDetails.
Дополнительные поля
Вы можете расширять ProblemDetails дополнительными данными:
public class CustomProblemDetails : ProblemDetails
{
public string ErrorCode { get; set; } = default!;
}
Затем возвращайте его через Results.Problem(…) и передавайте дополнительные метаданные.
Преимущества
- Чистые ответы об ошибках;
- Легкость для понимания фронтендерами;
- Стандарт (RFC 7807);
- Встроено в .NET.
Глобальную обработку ошибок, начиная с .NET 8, можно настроить с помощью IExceptionHandler, который также будет выдавать ProblemDetails.
Источник: https://thecodeman.net/posts/better-error-handling-with-problemdetails
👍12
День 2361. #Testing #BestPractices
Лучшие Практики Интеграционного Тестирования с Testcontainers. Начало
Интеграционные тесты с Testcontainers — мощный инструмент, но их поддержка может быстро превратиться в кошмар. Сегодня рассмотрим шаблоны, которые делают тесты Testcontainers надёжными, быстрыми и простыми в поддержке.
Традиционные интеграционные тесты часто используют общие тестовые БД или БД в памяти, которые не соответствуют поведению в рабочей среде. Вам либо приходится сталкиваться с загрязнением тестов между запусками, либо жертвовать реализмом ради скорости.
Testcontainers решает эту проблему, разворачивая настоящие Docker-контейнеры для ваших зависимостей. Тесты выполняются с использованием реальных PostgreSQL, Redis или любого другого сервиса, используемого в рабочей среде. После завершения тестов контейнеры уничтожаются, каждый раз позволяя вам начинать с чистого листа.
Всё происходит через API Docker. Testcontainers управляет всем жизненным циклом: извлечение образов, запуск контейнеров, ожидание готовности и очистка. Тестовому коду нужно лишь знать, как подключиться.
1. Подготовка
Убедитесь, что у вас есть необходимые пакеты:
Пакеты TestContainers существуют для множества сервисов.
2. Создание
Вот так можно создать контейнеры для PostgreSql и Redis:
3. Использование
Чтобы запускать и останавливать контейнеры в тестах, нужно реализовать IAsyncLifetime в вашей WebApplicationFactory:
Это гарантирует готовность контейнеров до запуска тестов и их очистку после них. Т.е. отсутствие остаточного состояния Docker или состояний гонки.
Совет: закрепите версии образов (например, postgres:17), чтобы избежать сюрпризов от изменений версий зависимостей.
4. Передача конфигурации в приложение
Testcontainers назначает динамические порты. Не пишите жёсткие строки подключения в коде. Вместо этого внедряйте значения через WebApplicationFactory.ConfigureWebHost:
Здесь используется метод UseSetting для динамической передачи строк подключения. Это также позволяет избежать состояний гонки или конфликтов с другими тестами, которые могут выполняться параллельно. Это гарантирует, что тесты всегда будут подключаться к правильным портам, независимо от того, что назначает Docker.
Нет необходимости удалять сервисы из коллекции сервисов или настраивать их вручную. Просто задайте строки подключения, и ваше приложение будет использовать их автоматически.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/testcontainers-best-practices-dotnet-integration-testing
Лучшие Практики Интеграционного Тестирования с Testcontainers. Начало
Интеграционные тесты с Testcontainers — мощный инструмент, но их поддержка может быстро превратиться в кошмар. Сегодня рассмотрим шаблоны, которые делают тесты Testcontainers надёжными, быстрыми и простыми в поддержке.
Традиционные интеграционные тесты часто используют общие тестовые БД или БД в памяти, которые не соответствуют поведению в рабочей среде. Вам либо приходится сталкиваться с загрязнением тестов между запусками, либо жертвовать реализмом ради скорости.
Testcontainers решает эту проблему, разворачивая настоящие Docker-контейнеры для ваших зависимостей. Тесты выполняются с использованием реальных PostgreSQL, Redis или любого другого сервиса, используемого в рабочей среде. После завершения тестов контейнеры уничтожаются, каждый раз позволяя вам начинать с чистого листа.
Всё происходит через API Docker. Testcontainers управляет всем жизненным циклом: извлечение образов, запуск контейнеров, ожидание готовности и очистка. Тестовому коду нужно лишь знать, как подключиться.
1. Подготовка
Убедитесь, что у вас есть необходимые пакеты:
Install-Package Microsoft.AspNetCore.Mvc.Testing
Install-Package Testcontainers.PostgreSql
Install-Package Testcontainers.Redis
Пакеты TestContainers существуют для множества сервисов.
2. Создание
Вот так можно создать контейнеры для PostgreSql и Redis:
var _pg = new PostgreSqlBuilder()
.WithImage("postgres:17")
.WithDatabase("mydb")
.WithUsername("postgres")
.WithPassword("postgres")
.Build();
var _redis = new RedisBuilder()
.WithImage("redis:latest")
.Build();
3. Использование
Чтобы запускать и останавливать контейнеры в тестах, нужно реализовать IAsyncLifetime в вашей WebApplicationFactory:
public sealed class IntegrationWebAppFactory :
WebApplicationFactory<Program>, IAsyncLifetime
{
public async Task InitializeAsync()
{
await _pg.StartAsync();
await _redis.StartAsync();
// Старт других зависимостей
}
public async Task DisposeAsync()
{
await _pg.StopAsync();
await _redis.StopAsync();
// Остановка других зависимостей
}
}
Это гарантирует готовность контейнеров до запуска тестов и их очистку после них. Т.е. отсутствие остаточного состояния Docker или состояний гонки.
Совет: закрепите версии образов (например, postgres:17), чтобы избежать сюрпризов от изменений версий зависимостей.
4. Передача конфигурации в приложение
Testcontainers назначает динамические порты. Не пишите жёсткие строки подключения в коде. Вместо этого внедряйте значения через WebApplicationFactory.ConfigureWebHost:
protected override void
ConfigureWebHost(IWebHostBuilder bldr)
{
bldr.UseSetting("ConnectionStrings:Database",
_pg.GetConnectionString());
bldr.UseSetting("ConnectionStrings:Redis",
_redis.GetConnectionString());
}
Здесь используется метод UseSetting для динамической передачи строк подключения. Это также позволяет избежать состояний гонки или конфликтов с другими тестами, которые могут выполняться параллельно. Это гарантирует, что тесты всегда будут подключаться к правильным портам, независимо от того, что назначает Docker.
Нет необходимости удалять сервисы из коллекции сервисов или настраивать их вручную. Просто задайте строки подключения, и ваше приложение будет использовать их автоматически.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/testcontainers-best-practices-dotnet-integration-testing
👍11
День 2362. #Testing #BestPractices
Лучшие Практики Интеграционного Тестирования с Testcontainers. Окончание
Начало
5. Совместное использование настроек с фикстурами xUnit
Фикстура — это общий контекст для тестов, позволяющий настроить дорогостоящие ресурсы, такие как БД или брокеры сообщений, один раз и использовать их повторно в нескольких тестах. Выбор между фикстурами классов и коллекций влияет как на производительность тестов, так и на изоляцию.
Фикстура класса — один контейнер на каждый тестовый класс.
Используйте, когда тесты изменяют глобальное состояние или когда отладка тестовых взаимодействий становится затруднительной. Применяйте, когда требуется полная изоляция между тестовыми классами (это медленнее, но безопаснее).
Фикстура коллекции — один контейнер, общий для нескольких тестовых классов.
Используйте, когда тесты не изменяют общее состояние или когда вы можете надёжно выполнить очистку между тестами. Т.е. когда тестовые классы не мешают друг другу (это быстрее, но требует дисциплины).
При использовании фикстур коллекций необходимо позаботиться об очистке любого состояния, которое может сохраняться между тестами. Это может включать сброс БД, очистку кэшей или удаление тестовых данных. Если этого не сделать, тесты могут влиять друг на друга, что приведёт к нестабильным результатам.
6. Вспомогательные методы для аутентификации и очистки
Фикстура может предоставлять вспомогательные методы для упрощения написания тестов:
Эти методы могут выполнять настройку аутентификации и очистку БД, чтобы не повторять шаблонный код в каждом тесте.
7. Написание поддерживаемых интеграционных тестов
При правильной настройке инфраструктуры ваши тесты должны быть сосредоточены на бизнес-логике. Сложность контейнеров должна быть скрыта за грамотно спроектированными базовыми классами и вспомогательными методами. В тестах вы не должны заботиться о правильной имитаций Postgres или Redis, а должны тестировать реальное поведение.
Итого
Testcontainers преобразовывает интеграционное тестирование, давая вам уверенность, которую приносит тестирование с реальными зависимостями. Больше не нужно беспокоиться о том, соответствует ли поведение вашей базы в памяти поведению производственной базы, или работать с общими тестовыми средами, которые выходят из строя при запуске тестов кем-то другим.
Начните с простого: выберите один интеграционный тест, который в настоящее время использует моки или БД в памяти, и преобразуйте его для использования Testcontainers. Вы сразу заметите разницу в уверенности, когда тест пройдёт успешно. Затем постепенно расширяйте его, чтобы охватить критически важные бизнес-процессы.
Источник: https://www.milanjovanovic.tech/blog/testcontainers-best-practices-dotnet-integration-testing
Лучшие Практики Интеграционного Тестирования с Testcontainers. Окончание
Начало
5. Совместное использование настроек с фикстурами xUnit
Фикстура — это общий контекст для тестов, позволяющий настроить дорогостоящие ресурсы, такие как БД или брокеры сообщений, один раз и использовать их повторно в нескольких тестах. Выбор между фикстурами классов и коллекций влияет как на производительность тестов, так и на изоляцию.
Фикстура класса — один контейнер на каждый тестовый класс.
Используйте, когда тесты изменяют глобальное состояние или когда отладка тестовых взаимодействий становится затруднительной. Применяйте, когда требуется полная изоляция между тестовыми классами (это медленнее, но безопаснее).
public class AddItemToCartTests :
IClassFixture<IntegrationWebAppFactory>
{
private IntegrationWebAppFactory _factory;
public AddItemToCartTests(
IntegrationWebAppFactory factory)
{
_factory = factory;
}
[Fact]
public async Task ShouldFail_WhenNotEnoughQuantity()
{ … }
}
Фикстура коллекции — один контейнер, общий для нескольких тестовых классов.
Используйте, когда тесты не изменяют общее состояние или когда вы можете надёжно выполнить очистку между тестами. Т.е. когда тестовые классы не мешают друг другу (это быстрее, но требует дисциплины).
[CollectionDefinition(nameof(IntegrationCollection))]
public sealed class IntegrationCollection :
ICollectionFixture<IntegrationWebAppFactory>
{
}
// Применение
[Collection(nameof(IntegrationCollection))]
public class AddItemToCartTests :
IntegrationTestFixture
{
public AddItemToCartTests(
IntegrationWebAppFactory factory)
: base(factory) { }
[Fact]
public async Task Should_Fail_WhenNotEnoughQuantity()
{ … }
}
При использовании фикстур коллекций необходимо позаботиться об очистке любого состояния, которое может сохраняться между тестами. Это может включать сброс БД, очистку кэшей или удаление тестовых данных. Если этого не сделать, тесты могут влиять друг на друга, что приведёт к нестабильным результатам.
6. Вспомогательные методы для аутентификации и очистки
Фикстура может предоставлять вспомогательные методы для упрощения написания тестов:
public async Task<HttpClient>
CreateAuthenticatedClientAsync() { … }
protected async Task CleanupDBAsync() { … }
Эти методы могут выполнять настройку аутентификации и очистку БД, чтобы не повторять шаблонный код в каждом тесте.
7. Написание поддерживаемых интеграционных тестов
При правильной настройке инфраструктуры ваши тесты должны быть сосредоточены на бизнес-логике. Сложность контейнеров должна быть скрыта за грамотно спроектированными базовыми классами и вспомогательными методами. В тестах вы не должны заботиться о правильной имитаций Postgres или Redis, а должны тестировать реальное поведение.
Итого
Testcontainers преобразовывает интеграционное тестирование, давая вам уверенность, которую приносит тестирование с реальными зависимостями. Больше не нужно беспокоиться о том, соответствует ли поведение вашей базы в памяти поведению производственной базы, или работать с общими тестовыми средами, которые выходят из строя при запуске тестов кем-то другим.
Начните с простого: выберите один интеграционный тест, который в настоящее время использует моки или БД в памяти, и преобразуйте его для использования Testcontainers. Вы сразу заметите разницу в уверенности, когда тест пройдёт успешно. Затем постепенно расширяйте его, чтобы охватить критически важные бизнес-процессы.
Источник: https://www.milanjovanovic.tech/blog/testcontainers-best-practices-dotnet-integration-testing
👍3
День 2363. #УрокиРазработки
Уроки 50 Лет Разработки ПО
Урок 58. Если не тратить время на учёбу и совершенствование, то не стоит ждать, что следующий проект будет реализован лучше предыдущего
Процесс размышления о событии, имеющий целью пережить следующее событие, называется ретроспективой («обзором результатов разработки» или «постмортемом» - даже если проект был реализован). Все команды разработчиков ПО должны проводить ретроспективы в конце цикла разработки (выпуска или итерации), по завершении проекта и при возникновении неожиданного или разрушительного события.
Ретроспектива позволяет учиться и совершенствоваться. Команда определяет, что получилось хорошо, а что нет, и благодаря этому получает возможность применить полученный опыт в будущей работе.
Ретроспектива помогает ответить на четыре вопроса:
1. Что получилось хорошо и что мы хотели бы повторить?
2. Что получилось не так хорошо и где в следующий раз следует поступить иначе?
3. Что нас удивило и может быть опасным в будущем?
4. Есть ли что-то, чего мы ещё не понимаем и должны исследовать?
Ретроспектива должна проводиться с привлечением всех участников и не должна использоваться для обвинения кого-то. Важно объективно и беспристрастно исследовать прошлый опыт. Каждый участник ретроспективы должен помнить первый закон Керта: «Какие бы факты ни вскрылись в ходе ретроспективы, мы должны понимать и искренне верить, что каждый старался делать свою работу максимально хорошо, с учётом имеющейся информации, навыков и способностей, доступных ресурсов и ситуации, сложившейся на тот момент.»
Время ретроспективы зависит от объёма проекта, качества выполнения работы и количества нового материала, который еще предстоит узнать. Agile-команде может потребоваться 30-60 минут на обсуждение итогов двухнедельного спринта. В больших проектах можно выделять до нескольких дней. Чем больше потенциальных рычагов для улучшения будущих результатов, тем целесообразнее тратить время на подведение итогов.
Ретроспектива — структурированная и ограниченная по времени последовательность действий: планирование, начало мероприятия, сбор информации, определение приоритетов проблем, анализ проблем и принятие решения о том, что делать с информацией. Члены команды делятся своим опытом работы над проектом: что произошло, когда и чем закончилось. Рекомендуется спрашивать мнение всех участников проекта, поскольку точка зрения каждого уникальна. Важно узнать эмоциональный фон участников во время выполнения проекта. Это позволяет найти идеи, повышающие чувство удовлетворенности работой.
Участники ретроспективы сообщают любые данные, собранные командой, в виде типичных метрик:
- размер — количество и объём требований, пользовательских историй и других элементов;
- трудозатраты — запланированные и фактические;
- время — запланированная и фактическая календарная продолжительность;
- качество — количество дефектов и их виды, производительность системы и другие качественные характеристики.
Результаты ретроспективы обязательно должны учитываться в текущей деятельности по совершенствованию процессов. Люди, проводящие ретроспективы, должны уделить время изучению способов решения прошлых проблем. В это время они не смогут заниматься решением задач проекта, поэтому усилия по совершенствованию должны быть добавлены в графики проекта. Если проектная команда проводит ретроспективу, но руководство не предоставляет ресурсы для решения выявленных проблем, то такая ретроспектива бесполезна.
Поэтому добавляйте в график время для обучения и экспериментов, чтобы люди могли учиться эффективно применять новые практики, инструменты и методы. Проведение ретроспектив без внесения каких-либо изменений в процессы — пустая трата времени, отбивающая у участников охоту участвовать в них. Высший признак успеха ретроспективы — наличие устойчивых изменений. Это говорит о том, что время, потраченное командой на размышления о прошлых событиях, приносит долговременную пользу.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 7.
Уроки 50 Лет Разработки ПО
Урок 58. Если не тратить время на учёбу и совершенствование, то не стоит ждать, что следующий проект будет реализован лучше предыдущего
Процесс размышления о событии, имеющий целью пережить следующее событие, называется ретроспективой («обзором результатов разработки» или «постмортемом» - даже если проект был реализован). Все команды разработчиков ПО должны проводить ретроспективы в конце цикла разработки (выпуска или итерации), по завершении проекта и при возникновении неожиданного или разрушительного события.
Ретроспектива позволяет учиться и совершенствоваться. Команда определяет, что получилось хорошо, а что нет, и благодаря этому получает возможность применить полученный опыт в будущей работе.
Ретроспектива помогает ответить на четыре вопроса:
1. Что получилось хорошо и что мы хотели бы повторить?
2. Что получилось не так хорошо и где в следующий раз следует поступить иначе?
3. Что нас удивило и может быть опасным в будущем?
4. Есть ли что-то, чего мы ещё не понимаем и должны исследовать?
Ретроспектива должна проводиться с привлечением всех участников и не должна использоваться для обвинения кого-то. Важно объективно и беспристрастно исследовать прошлый опыт. Каждый участник ретроспективы должен помнить первый закон Керта: «Какие бы факты ни вскрылись в ходе ретроспективы, мы должны понимать и искренне верить, что каждый старался делать свою работу максимально хорошо, с учётом имеющейся информации, навыков и способностей, доступных ресурсов и ситуации, сложившейся на тот момент.»
Время ретроспективы зависит от объёма проекта, качества выполнения работы и количества нового материала, который еще предстоит узнать. Agile-команде может потребоваться 30-60 минут на обсуждение итогов двухнедельного спринта. В больших проектах можно выделять до нескольких дней. Чем больше потенциальных рычагов для улучшения будущих результатов, тем целесообразнее тратить время на подведение итогов.
Ретроспектива — структурированная и ограниченная по времени последовательность действий: планирование, начало мероприятия, сбор информации, определение приоритетов проблем, анализ проблем и принятие решения о том, что делать с информацией. Члены команды делятся своим опытом работы над проектом: что произошло, когда и чем закончилось. Рекомендуется спрашивать мнение всех участников проекта, поскольку точка зрения каждого уникальна. Важно узнать эмоциональный фон участников во время выполнения проекта. Это позволяет найти идеи, повышающие чувство удовлетворенности работой.
Участники ретроспективы сообщают любые данные, собранные командой, в виде типичных метрик:
- размер — количество и объём требований, пользовательских историй и других элементов;
- трудозатраты — запланированные и фактические;
- время — запланированная и фактическая календарная продолжительность;
- качество — количество дефектов и их виды, производительность системы и другие качественные характеристики.
Результаты ретроспективы обязательно должны учитываться в текущей деятельности по совершенствованию процессов. Люди, проводящие ретроспективы, должны уделить время изучению способов решения прошлых проблем. В это время они не смогут заниматься решением задач проекта, поэтому усилия по совершенствованию должны быть добавлены в графики проекта. Если проектная команда проводит ретроспективу, но руководство не предоставляет ресурсы для решения выявленных проблем, то такая ретроспектива бесполезна.
Поэтому добавляйте в график время для обучения и экспериментов, чтобы люди могли учиться эффективно применять новые практики, инструменты и методы. Проведение ретроспектив без внесения каких-либо изменений в процессы — пустая трата времени, отбивающая у участников охоту участвовать в них. Высший признак успеха ретроспективы — наличие устойчивых изменений. Это говорит о том, что время, потраченное командой на размышления о прошлых событиях, приносит долговременную пользу.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 7.
👍5
День 2364. #ЗаметкиНаПолях
5 Ошибок, из-за Которых Код Становится Неподдерживаемым. Начало
Вот 5 самых распространённых ошибок в проектировании ПО, из-за которых работа с кодом становится кошмаром. Все эти ошибки со временем, по мере роста объёма кода, делают его неподдерживаемым.
1. Недопустимое состояние и проблемы с согласованностью данных
Ошибка связана с тем, что данные оказываются в недопустимом состоянии и сталкиваются с проблемами согласованности. Обычно это происходит из-за отсутствия надлежащего контроля над изменяющимся состоянием.
Представьте, что в приложении есть две разные системы — скажем, биллинг и управление доставкой — и биллинг меняет состояние доставки. Этого не должно быть. Нужна одна система, которая будет контролировать изменение своего состояния. Все изменения состояния всегда должны быть допустимыми, и данные всегда должны быть в допустимом состоянии.
В монолите применим тот же принцип. Вам необходимо определить владельцев данных, которые управляют своим состоянием. Недопустимо, чтобы любая часть монолита могла менять любое состояние в любой точке системы.
Если вы когда-либо сталкивались с вопросом: «Как мы оказались в таком состоянии? Почему данные вообще выглядят именно так? Как это произошло?», и не имели об этом ни малейшего понятия, то причина в отсутствии владения данными. Возможно, кто-то вручную подключился к БД, или другой сервис или приложение интегрировалось и изменило состояние.
Решение — явно определить владельцев данных. Определите API, с которым взаимодействуют другие части системы — это контракт. Когда вы хотите изменить состояние части системы, вы вызываете её API, и она управляет изменением своего состояния. Т.е. за изменение состояния отвечает одно конкретное место.
Ещё лучше определить команды и запросы:
- Команды изменяют состояние.
- Запросы только получают данные, относящиеся к определённой части системы.
Каждое взаимодействие проходит через единое место, которое владеет командами и запросами.
2. Кодовая база неявная
Обычно кодовая база основана на CRUD-операциях над сущностями. Если взглянуть на вашу кодовую базу, можно ли точно сказать, что она делает и каковы её возможности? Обычно нет, поскольку рабочие процессы, управляемые конечными пользователями, не описаны явно.
Это легко представить на примере событий домена. Предположим, у вас есть груз, и одним из необходимых действий является получение груза водителем. Когда водитель это делает, вы часто назначаете коносамент (bill of lading - BOL). Если вы используете только CRUD, вы можете обновить отгрузку с помощью BOL. Тогда возникает событие «изменение отгрузки».
Но почему отгрузка изменилась? Вы не знаете. Кто-то ввёл BOL впервые? Или добавил его повторно? Или же это был самовывоз?
В этом заключается существенная разница между явным и неявным описанием. Вместо стандартного события «изменение отгрузки» нужно явное событие, например, «отгрузка подготовлена к самовывозу», которое включает идентификатор отгрузки, местоположение, дату, время и коносамент. Это гораздо понятнее и точно показывает, что произошло.
Когда ваш API — это просто «изменение отгрузки», вы не знаете, что на самом деле пытается сделать пользователь. Вам остаётся только пытаться понять, что означает изменение данных и что вы хотите сделать после этого. Но часто дело не только в том, что данные изменились, но и в том, почему они изменились.
Явное описание значительно упрощает навигацию по кодовой базе. И вот связь с первой ошибкой: если у вас есть явные команды, они отвечают за владение и обеспечение корректности состояния.
Окончание следует…
Источник: https://codeopinion.com/5-mistakes-that-make-your-code-unmaintainable/
5 Ошибок, из-за Которых Код Становится Неподдерживаемым. Начало
Вот 5 самых распространённых ошибок в проектировании ПО, из-за которых работа с кодом становится кошмаром. Все эти ошибки со временем, по мере роста объёма кода, делают его неподдерживаемым.
1. Недопустимое состояние и проблемы с согласованностью данных
Ошибка связана с тем, что данные оказываются в недопустимом состоянии и сталкиваются с проблемами согласованности. Обычно это происходит из-за отсутствия надлежащего контроля над изменяющимся состоянием.
Представьте, что в приложении есть две разные системы — скажем, биллинг и управление доставкой — и биллинг меняет состояние доставки. Этого не должно быть. Нужна одна система, которая будет контролировать изменение своего состояния. Все изменения состояния всегда должны быть допустимыми, и данные всегда должны быть в допустимом состоянии.
В монолите применим тот же принцип. Вам необходимо определить владельцев данных, которые управляют своим состоянием. Недопустимо, чтобы любая часть монолита могла менять любое состояние в любой точке системы.
Если вы когда-либо сталкивались с вопросом: «Как мы оказались в таком состоянии? Почему данные вообще выглядят именно так? Как это произошло?», и не имели об этом ни малейшего понятия, то причина в отсутствии владения данными. Возможно, кто-то вручную подключился к БД, или другой сервис или приложение интегрировалось и изменило состояние.
Решение — явно определить владельцев данных. Определите API, с которым взаимодействуют другие части системы — это контракт. Когда вы хотите изменить состояние части системы, вы вызываете её API, и она управляет изменением своего состояния. Т.е. за изменение состояния отвечает одно конкретное место.
Ещё лучше определить команды и запросы:
- Команды изменяют состояние.
- Запросы только получают данные, относящиеся к определённой части системы.
Каждое взаимодействие проходит через единое место, которое владеет командами и запросами.
2. Кодовая база неявная
Обычно кодовая база основана на CRUD-операциях над сущностями. Если взглянуть на вашу кодовую базу, можно ли точно сказать, что она делает и каковы её возможности? Обычно нет, поскольку рабочие процессы, управляемые конечными пользователями, не описаны явно.
Это легко представить на примере событий домена. Предположим, у вас есть груз, и одним из необходимых действий является получение груза водителем. Когда водитель это делает, вы часто назначаете коносамент (bill of lading - BOL). Если вы используете только CRUD, вы можете обновить отгрузку с помощью BOL. Тогда возникает событие «изменение отгрузки».
Но почему отгрузка изменилась? Вы не знаете. Кто-то ввёл BOL впервые? Или добавил его повторно? Или же это был самовывоз?
В этом заключается существенная разница между явным и неявным описанием. Вместо стандартного события «изменение отгрузки» нужно явное событие, например, «отгрузка подготовлена к самовывозу», которое включает идентификатор отгрузки, местоположение, дату, время и коносамент. Это гораздо понятнее и точно показывает, что произошло.
Когда ваш API — это просто «изменение отгрузки», вы не знаете, что на самом деле пытается сделать пользователь. Вам остаётся только пытаться понять, что означает изменение данных и что вы хотите сделать после этого. Но часто дело не только в том, что данные изменились, но и в том, почему они изменились.
Явное описание значительно упрощает навигацию по кодовой базе. И вот связь с первой ошибкой: если у вас есть явные команды, они отвечают за владение и обеспечение корректности состояния.
Окончание следует…
Источник: https://codeopinion.com/5-mistakes-that-make-your-code-unmaintainable/
👍13👎1
День 2365. #ЗаметкиНаПолях
5 Mistakes That Make Your Code Unmaintainable
Начало
3. Добавление косвенности без осознания
Вы добавляете косвенность, заботясь о соблюдении принципа единой ответственности, и думаете, что поступаете хорошо, но на самом деле это не так. Косвенность возникает, когда вызывающий объект вызывает целевой объект, а вы добавляете что-то промежуточное, разделяя ответственность. Типичный пример — доступ к данным.
То, что кажется простым, например, контроллер, вызывающий уровень доступа к данным, часто реализовано гораздо сложнее. Контроллер вызывает сервис, вызывающий другой сервис, использующий метод расширения, который вызывает ORM, обращающийся к БД. Такая косвенная связь часто невидима, но затрудняет отслеживание запросов. Ошибка заключается в неосознавании добавляемой косвенности и того, представляет ли она реальную ценность.
Если ваш вызывающий код тесно связан с абстракцией, созданной только для того, чтобы скрыть зависимость от сторонних компонентов, это может быть оправдано, если есть несколько реализаций или вы хотите упростить API. Но если абстракция используется только в одном месте и не служит никакой реальной цели, она бесполезна. Бесполезные абстракции затрудняют навигацию по коду и его поддержку. Иногда лучше управлять связностью напрямую, чем создавать ненужные уровни.
Главное — помнить о добавляемой косвенности и о том, действительно ли она добавляет ценность. Слабая связность полезна. Но бесполезная косвенность и абстракции, которые не несут никакой ценности, а создаются лишь для «чистоты кода», вредны.
4. Игра в «Что, если»
Продумывание всех возможных будущих сценариев и попытка написать код, учитывающий их заранее. Это часто нарушает принцип YAGNI (You Ain't Gonna It — «Вам это не понадобится»). Вы можете подумать: «А что, если кто-то попросит похожую, но немного другую функцию?» Но если никто её ещё не запросил, как узнать, ценна ли она?
Вы можете писать слишком общий код или встраивать абстракции для гипотетических будущих изменений, например, замены технологий, которые могут никогда не произойти. Это приводит к появлению внутренних фреймворков или общего кода, который никто не понимает и не нуждается в нём, потому что существует только один реальный вариант использования.
Проблема с вопросом «что, если?» заключается в стоимости владения. Это не только первоначальные затраты на разработку, но и необходимость постоянного обслуживания и работы с кодом, который никто не использует, что добавляет сложности.
5. Неправильное управление рабочими процессами
Представьте себе простой рабочий процесс, например, размещение заказа. Вы проходите стадии оформления заказа, подтверждения, обработки оплаты и затем отправляете email с подтверждением. Кажется, просто.
Проблема, если рассматриваете это как один длинный процедурный процесс, а не как рабочий процесс с изолированными шагами. Что будет, если возникнет ошибка при списании средств с карты? Как ваш код справится с этой ситуацией? Нужно ли отменить заказ? Отправить email? Вывести ошибку пользователю?
Если ваш код слишком процедурный, у вас, вероятно, будет запутанный клубок условий и обработки исключений, который сложно отслеживать и поддерживать. Рабочие процессы следует рассматривать как небольшие блоки, которые выполняются изолированно и переходят от одного шага к другому. Такое управление значительно упрощает обработку ошибок, повторных попыток и ветвление.
Использование таких инструментов, как обмен сообщениями, очереди и т.п., поможет вам справиться с этой сложностью. Главное — распознавать рабочие процессы и использовать подходящие инструменты, а не создавать сложный процедурный код с обилием ветвлений.
Итого
Каждая из этих ошибок затрудняет поддержку и развитие кода с течением времени. Определение владения данными, чёткое указание команд и запросов, тщательное управление косвенными зависимостями, избегание преждевременного обобщения и правильная обработка рабочих процессов сделают вашу систему намного более управляемой.
Источник: https://codeopinion.com/5-mistakes-that-make-your-code-unmaintainable/
5 Mistakes That Make Your Code Unmaintainable
Начало
3. Добавление косвенности без осознания
Вы добавляете косвенность, заботясь о соблюдении принципа единой ответственности, и думаете, что поступаете хорошо, но на самом деле это не так. Косвенность возникает, когда вызывающий объект вызывает целевой объект, а вы добавляете что-то промежуточное, разделяя ответственность. Типичный пример — доступ к данным.
То, что кажется простым, например, контроллер, вызывающий уровень доступа к данным, часто реализовано гораздо сложнее. Контроллер вызывает сервис, вызывающий другой сервис, использующий метод расширения, который вызывает ORM, обращающийся к БД. Такая косвенная связь часто невидима, но затрудняет отслеживание запросов. Ошибка заключается в неосознавании добавляемой косвенности и того, представляет ли она реальную ценность.
Если ваш вызывающий код тесно связан с абстракцией, созданной только для того, чтобы скрыть зависимость от сторонних компонентов, это может быть оправдано, если есть несколько реализаций или вы хотите упростить API. Но если абстракция используется только в одном месте и не служит никакой реальной цели, она бесполезна. Бесполезные абстракции затрудняют навигацию по коду и его поддержку. Иногда лучше управлять связностью напрямую, чем создавать ненужные уровни.
Главное — помнить о добавляемой косвенности и о том, действительно ли она добавляет ценность. Слабая связность полезна. Но бесполезная косвенность и абстракции, которые не несут никакой ценности, а создаются лишь для «чистоты кода», вредны.
4. Игра в «Что, если»
Продумывание всех возможных будущих сценариев и попытка написать код, учитывающий их заранее. Это часто нарушает принцип YAGNI (You Ain't Gonna It — «Вам это не понадобится»). Вы можете подумать: «А что, если кто-то попросит похожую, но немного другую функцию?» Но если никто её ещё не запросил, как узнать, ценна ли она?
Вы можете писать слишком общий код или встраивать абстракции для гипотетических будущих изменений, например, замены технологий, которые могут никогда не произойти. Это приводит к появлению внутренних фреймворков или общего кода, который никто не понимает и не нуждается в нём, потому что существует только один реальный вариант использования.
Проблема с вопросом «что, если?» заключается в стоимости владения. Это не только первоначальные затраты на разработку, но и необходимость постоянного обслуживания и работы с кодом, который никто не использует, что добавляет сложности.
5. Неправильное управление рабочими процессами
Представьте себе простой рабочий процесс, например, размещение заказа. Вы проходите стадии оформления заказа, подтверждения, обработки оплаты и затем отправляете email с подтверждением. Кажется, просто.
Проблема, если рассматриваете это как один длинный процедурный процесс, а не как рабочий процесс с изолированными шагами. Что будет, если возникнет ошибка при списании средств с карты? Как ваш код справится с этой ситуацией? Нужно ли отменить заказ? Отправить email? Вывести ошибку пользователю?
Если ваш код слишком процедурный, у вас, вероятно, будет запутанный клубок условий и обработки исключений, который сложно отслеживать и поддерживать. Рабочие процессы следует рассматривать как небольшие блоки, которые выполняются изолированно и переходят от одного шага к другому. Такое управление значительно упрощает обработку ошибок, повторных попыток и ветвление.
Использование таких инструментов, как обмен сообщениями, очереди и т.п., поможет вам справиться с этой сложностью. Главное — распознавать рабочие процессы и использовать подходящие инструменты, а не создавать сложный процедурный код с обилием ветвлений.
Итого
Каждая из этих ошибок затрудняет поддержку и развитие кода с течением времени. Определение владения данными, чёткое указание команд и запросов, тщательное управление косвенными зависимостями, избегание преждевременного обобщения и правильная обработка рабочих процессов сделают вашу систему намного более управляемой.
Источник: https://codeopinion.com/5-mistakes-that-make-your-code-unmaintainable/
👍10