AI

Habr AI

Ни зарплат, ни отпусков, ни больничных. Зато есть User Story и Merge Request: как AI‑агенты работают у меня в Jira

Открыть оригинал ↗ Перевод заголовка: Ни зарплат, ни отпусков, ни больничных. Зато есть User Story и Merge Request: как AI-агенты работают у меня в Jira

В компании IT-команда использует Jira как основной трекер, где теперь в поле Assignee назначаются не люди, а AI-агенты — фронтенд-разработчики, бэкенд-разработчики и аналитики. Агенты выполняют ограниченные задачи (oneshot), такие как добавление кнопки или подготовка требований, не заменяя людей в ревью и принятии решений. Работа идёт через серверный harness, который интегрирован с Jira, Confluence и GitLab, обеспечивая автоматическое создание MR, проверки кода и документации. Важно, что агенты не могут изменять код напрямую, если это не предусмотрено, и в сложных случаях (например, при постановке бизнес-требований) задают вопросы, которые затем решаются человеком.

  1. Агенты получают задачи через поле Assignee в Jira, как обычные коллеги, и выполняют их в изолированном окружении с доступом к проектной документации и репозиториям.
  2. Для каждого агента настроены роли, модели, инструкции и доступы, что позволяет единообразно применять правила по всем задачам.
  3. Результаты включают MR, документацию и логи, но ревью остаётся за человеком — агенты не заменяют команду, а лишь ускоряют выполнение определённых этапов.
  4. Система поддерживает автозапуск, возврат к заданию и отслеживание расхода токенов, но не хранит внутренние рассуждения модели.
  5. Важно, что агенты не могут работать с репозиториями в режиме редактирования — их результаты (например, User Story) требуют ручного подтверждения и публикации.

Когда пригодится

Материал полезен для команд, которые уже используют Jira и хотят внедрить AI-агентов для автоматизации небольших, повторяющихся задач — например, добавление элементов интерфейса, подготовка требований или формирование User Story. Особенно актуален в средних компаниях, где есть структура и инструкции, но не хватает времени на ручную подготовку задач.

Вопросы для размышления

  1. Если агенты начинают генерировать 30 MR в день вместо одного, как изменится стратегия ревью — будет ли оно распределено между разработчиками, усилено автоматическими проверками, или задачи будут пересмотрены с более жёсткими критериями приёмки?
  2. Если агенты уже выполняют задачи, которые раньше делали джун-разработчики, как это повлияет на обучение команды — кто будет отвечать за ревью AI-кода и как это может быть интегрировано в систему развития кадров?

Java / Spring

HowToDoInJava

TypeScript 7 Native Go Compiler: tsc, tsgo and Migration

Открыть оригинал ↗ Перевод заголовка: TypeScript 7: нативный компилятор на Go, tsc, tsgo и миграция

TypeScript 7 представляет собой стабильную версию, в которой компилятор и сервис языка перенесены с JavaScript на Go. Команда tsc теперь работает как нативный бинарный файл, что позволяет сократить время компиляции в 4–12 раз по сравнению с версией 6, особенно на больших проектах. Важно понимать, что переход возможен только в тех случаях, когда в сборке используется только tsc, а не инструменты, которые импортируют TypeScript как библиотеку. В таких случаях компилятор остаётся на версии 6, так как в TypeScript 7 отсутствует программируемый API (например, createProgram). При миграции необходимо убрать устаревшие настройки в tsconfig.json (например, target: es5, moduleResolution: node10, baseUrl), и проверить, что выходные файлы (.js, .d.ts) идентичны в обоих версиях.

  1. Миграция возможна только при отсутствии инструментов, импортирующих TypeScript как библиотеку (например, ts-loader, typescript-eslint).
  2. Устаревшие настройки в tsconfig.json (например, baseUrl, target: es5) должны быть удалены до перехода.
  3. Время компиляции в TypeScript 7 на 4–12 раз быстрее, но эффективность зависит от архитектуры системы и количества ядер.
  4. Команда tsgo устарела и больше не рекомендуется — она была только предварительной версией, прекратившейся 7 июля 2026 года.
  5. Программируемый API (например, createProgram) отсутствует в версии 7 и появится в 7.1, что ограничивает использование в инструментах, зависящих от него.

Когда пригодится

Материал полезен для команд, которые используют tsc в CI и имеют небольшой или средний размер проекта, где время компиляции критично. Особенно актуален для монорепозиториев с длинными CI-процессами и для проектов, где выходные файлы должны быть идентичны между версиями.

Вопросы для размышления

  1. Какой компромисс между производительностью и стабильностью возникает при использовании tsc в среде с несколькими инструментами, зависящими от старого API, если они не могут обновиться до версии 7.1?
  2. В каких условиях (по размеру проекта, количеству ядер, настройкам tsconfig.json) скорость компиляции в TypeScript 7 может достигать 10x, и как это влияет на выбор времени миграции?
  3. Какие риски связаны с тем, чтобы в проекте одновременно использовать typescript@7.0.2 и @typescript/typescript6, если в нём есть инструменты, требующие старый API?

MCP Tool Poisoning: A Local Demo and Defenses in Python

Открыть оригинал ↗ Перевод заголовка: Угроза заражения инструментов MCP: локальный демо и защита на Python

Атака MCP tool poisoning заключается в том, что сервер скрывает инструкции в метаданных инструмента, и модель интерпретирует их как команды от пользователя. Поскольку описание инструмента передаётся в контекст модели как обычный текст, разработчик, составляющий описание, может непосредственно влиять на поведение модели. Пользователь видит лишь имя инструмента в диалоге, а не полный текст, что делает угрозу незаметной. Важно, что при установке серверов из реестров или репозиториев или при разработке собственных серверов, разработчики должны внедрять защиту.

Четыре практические меры эффективны: хеширование описаний инструментов (pinning), отображение полного описания перед одобрением, сканирование текста на наличие инструкций и запуск серверов с минимальными правами доступа. Хеширование — наиболее простое и автоматизированное решение: при первом одобрении вычисляется SHA-256 от всей структуры инструмента, а при последующих проверках — сравнивается с хешем. Сканер идентифицирует скрытые теги, приказы и упоминания иностранных инструментов. Проверка на наличие инструкций в описании и результатах вызова инструмента позволяет обнаружить атаки на разных этапах.

Когда пригодится

Материал полезен при разработке или внедрении AI-агентов, использующих внешние инструменты, особенно в средах, где используются сторонние серверы или интегрируются с внешними API. Критически важен при работе с открытыми или публичными репозиториями инструментов, где описание может быть изменено после одобрения. Особенно актуален для команд, которые не контролируют весь цикл развертывания инструментов.

Вопросы для размышления

  1. Какова сложность реализации хеширования инструментов в условиях динамического обновления описаний, если серверы могут обновляться без предварительного уведомления и без контроля версий?
  2. В каких случаях сканер поиска инструкций может пропустить атаку, если атакующий использует синонимичные формулировки или переформулированные инструкции, не включающие явные теги?
  3. Какова надёжность защиты при использовании агентов с низкой пропускной способностью, если каждый вызов инструмента требует проверки результата, но при этом серверы запускаются в одном процессе без изоляции?

Free-Threaded Python (No-GIL): Writing Thread-Safe Code

Открыть оригинал ↗ Перевод заголовка: Свободная потоковая Python (без GIL): написание потокобезопасного кода

В Python 3.14 появился опциональный режим выполнения без Global Interpreter Lock (GIL), позволяющий нескольким потокам одновременно использовать несколько ядер процессора при выполнении CPU-нагруженных задач. В отличие от стандартной версии, где GIL блокирует выполнение кода только для одного потока за раз, в режиме без GIL потоки могут работать параллельно, что значительно улучшает производительность при обработке вычислительных задач. Однако это не делает операции, такие как counter += 1, атомичными — они всё ещё подвержены конкуренции, и для предотвращения потерь данных требуется использование блокировок или очередей. Ключевые инструменты для потокобезопасности включают threading.Lock, threading.RLock, queue.Queue и ThreadPoolExecutor, каждый из которых подходит в зависимости от модели взаимодействия между потоками.

  1. Режим без GIL — это отдельная сборка CPython (например, python3.14t), а не включение в стандартную версию.
  2. Без GIL потоки могут работать параллельно на нескольких ядрах, что даёт значительное ускорение для CPU-нагруженных задач.
  3. Операции, включающие чтение и запись в общие переменные (например, counter += 1), остаются неатомичными и требуют блокировок даже в режиме без GIL.
  4. Потокобезопасность достигается через блокировки, очереди или использование ThreadPoolExecutor, который избегает прямого доступа к общим данным.
  5. Важно проверять состояние GIL во время запуска (через sys._is_gil_enabled()), так как он может быть включён вручную через переменную среды или опцию -X gil.

Когда пригодится

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

Вопросы для размышления

  1. Какой компромисс между производительностью и стабильностью возникает при использовании threading.Lock в цикле с большим числом итераций, и как это влияет на масштабируемость при увеличении количества потоков?
  2. При каких условиях ThreadPoolExecutor может быть предпочтительнее, чем queue.Queue, и как это влияет на выбор архитектуры при работе с большими объёмами данных?
  3. Какова вероятность возникновения race condition в коде, который прошёл тесты на стандартной версии Python, при переходе на free-threaded build, и как это может быть оценено без реальных нагрузок?

Python JIT Compiler: Enable, Benchmark and When to Use

Открыть оригинал ↗ Перевод заголовка: Python JIT-компилятор: включение, бенчмаркинг и когда его использовать

Новый JIT-компилятор в CPython (с 3.13) работает на уровне машинного кода, транслируя «горячие» блоки байт-кода в машинные инструкции во время выполнения. Он использует метод copy-and-patch и впервые включён в официальную версию Python, но остаётся экспериментальным и не рекомендуется для использования в production. Официальные бенчмарки показывают средний прирост скорости от 4 до 12%, но это среднее значение по разным программам и платформам — реальные результаты на обычных машинах, особенно в виртуальных средах, могут быть неуловимыми или даже отрицательными.

  1. Скорость улучшения минимальна для большинства реальных приложений — JIT не ускоряет код, который не содержит типоустойчивых числовых циклов.
  2. Для точного измерения нужно запускать тест несколько раз и смотреть на медиану, а не на среднее значение, чтобы избежать шума от фоновых процессов.
  3. JIT работает только на определённых типах кода — чистые арифметические циклы без вызовов функций и создания объектов.
  4. Он не ускоряет код, работающий в Numpy, pandas или других библиотеках, где медленные участки уже находятся в C-коде.
  5. В production использовать JIT нельзя — он нестабилен, может быть удалён без предупреждения, и инструменты профилирования не работают корректно.

Когда пригодится

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

Вопросы для размышления

  1. Почему виртуальная машина с двумя ядрами не даёт стабильного результата, и как это влияет на доверие к измерениям JIT-ускорения?
  2. Какой из компиляторов (PyPy, Numba, Cython) лучше подходит для ускорения численных вычислений в реальном приложении, и в каких случаях JIT CPython может быть альтернативой?
  3. Если JIT не ускоряет код, который вызывает функции или создаёт объекты, как это влияет на выбор между использованием CPython и PyPy для проектов с большими циклами?

Python Lazy Imports: The lazy Keyword and Alternatives

Открыть оригинал ↗ Перевод заголовка: Python: ленивые импорты, ключевое слово lazy и альтернативы

Ленивые импорты в Python откладывают загрузку модуля до первого его использования, в отличие от обычных (жадных) импортов, которые загружают модуль сразу при выполнении инструкции import. В Python 3.15, начиная с релиза 9 октября 2026 года, реализовано ключевое слово lazy в рамках PEP 810, которое позволяет откладывать загрузку модулей до первого обращения к имени. При этом lazy import json сразу привязывает имя к прокси-объекту, а сам модуль загружается только при первом вызове, например, json.dumps(). Такой подход особенно полезен в командах-инструментах, где не все подкоманды используют все зависимости — например, команда list может не требовать модулей для работы с XML или архивами.

Практические тезисы:
— Ленивые импорты сокращают время запуска программы, если в конкретном запуске не используются определённые модули.
— Ключевое слово lazy работает только на уровне модуля (вне функций, классов и блоков try), и не поддерживается внутри функций или блоков.
— При первом использовании имени, прокси-объект разрешается и модуль загружается, а ошибка при несуществующем модуле выдаётся на этом этапе.
— Для старых версий Python (до 3.15) можно использовать импорты внутри функций или importlib.util.LazyLoader, хотя это требует дополнительной настройки.
— Важно учитывать, что ленивые импорты могут вызывать побочные эффекты (например, регистрацию модулей при первом обращении), и такие случаи требуют явного управления.

Когда пригодится

Материал полезен в тех случаях, когда запуск приложения должен быть максимально быстрым, а в конкретной сессии используются только часть модулей — например, в CLI-инструментах, где подкоманды имеют разные зависимости. Особенно эффективно при наличии большого количества импортов, которые не используются в большинстве случаев.

Вопросы для размышления

  1. Какой компромисс между производительностью и надёжностью возникает при использовании ленивых импортов в приложении, где отсутствие модуля может привести к критической ошибке на уровне выполнения?
  2. В каких условиях ленивый импорт может привести к непредсказуемому поведению в многопоточной среде, и как это может быть обходится при архитектуре, где модули регистрируются при первом обращении?
  3. При каких условиях использование lazy в сочетании с __getattr__ или LazyLoader становится предпочтительнее, чем встроенный lazy в Python 3.15, с точки зрения масштабируемости и поддержки на разных версиях Python?

Context Bloat in AI Agents: Causes, Cost and How to Fix It

Открыть оригинал ↗ Перевод заголовка: Бloat контекста в ИИ-агентах: причины, стоимость и способы устранения

Контекст-блоат — это рост объёма токенов в запросе модели, который не нужен для текущего шага, например, из-за вставки полных файлов или ненужных результатов инструментов. Важно это в код-агентах и других приложениях, где каждый шаг агента повторяет весь контекст, включая инструкции, описания инструментов и историю диалога. Такой рост увеличивает стоимость запроса, замедляет ответ и может снижать точность ответа. В примере из статьи файлы и результаты инструментов составляют 95,9% от общего объёма запроса, что делает их главной целью для оптимизации.

  1. Удаление избыточных данных — сокращение результатов инструментов, например, ограничение количества строк или сохранение только неудачных тестов.
  2. Замена вставки полных файлов на извлечение кусков (chunks) по запросу, что снижает объём вручную добавляемого контента.
  3. Компактное хранение истории диалога — замена старых сообщений на один обобщённый фрагмент.
  4. Загрузка инструментов по запросу (on-demand), вместо включения всех описаний в начало запроса.
  5. Использование подагента с собственным контекстом для выполнения отдельных задач.
  6. Кэширование стабильного префикса (системных инструкций), что уменьшает повторяющийся объём запроса.

Когда пригодится

Материал полезен при разработке ИИ-агентов, работающих с большими объёмами кода или историей диалога, где важно минимизировать стоимость запросов и сохранить точность ответов.

Вопросы для размышления

  1. Какой компромисс между сокращением контекста и потерей важной детали может возникнуть при использовании trim-инструментов для результатов тестов, если модель должна идентифицировать конкретную строку в логе?
  2. В каких условиях (по масштабу, нагрузке, типу задачи) может быть оправдано использование подагента вместо кэширования префикса, если оба подхода снижают объём контекста, но вводят разные риски?
  3. Как изменится сложность и надёжность агента при переходе от вставки полных файлов к извлечению кусков, если в процессе извлечения возникают ошибки в сопоставлении по TF-IDF?

Python UTF-8 Default Encoding: What Changes in Python 3.15

Открыть оригинал ↗ Перевод заголовка: Проверка по умолчанию UTF-8 в Python: что изменилось в Python 3.15

В Python 3.15 по умолчанию для функции open() без явного указания кодировки теперь используется UTF-8 на всех платформах, в отличие от предыдущих версий, где на Windows по умолчанию применялась кодировка системы (например, cp1252). Это изменение введено в рамках PEP 686 и затрагивает не только чтение файлов, но и другие интерфейсы, такие как pathlib и subprocess, которые теперь также используют UTF-8 по умолчанию. Без явного указания кодировки в open() Python 3.15 корректно расшифровывает UTF-8-файлы, а при попытке открыть файл в старой кодировке (например, cp1252) с акцентами — вызывает ошибку UnicodeDecodeError. Для надёжности и совместимости в любом коде следует всегда явно указывать кодировку при работе с файлами.

  1. Всегда передавайте явную кодировку (например, "utf-8") в вызове open(), чтобы избежать зависимости от системы и версии Python.
  2. При работе с файлами, созданными в старой кодировке (cp1252), используйте encoding="cp1252" для корректного чтения.
  3. Для файлов, которые должны соответствовать системной кодировке (например, ввод старых приложений), используйте encoding="locale".
  4. Важно понимать, что UTF-8-файлы, ранее читавшиеся как "малопонятные" в cp1252, теперь работают правильно, а cp1252-файлы требуют явного указания кодировки.
  5. Проверяйте код на совместимость с разными версиями Python и платформами, используя тесты с отключённым UTF-8-режимом (PYTHONUTF8=0).

Когда пригодится

Материал полезен при миграции существующего кода с Windows-кодировок (cp1252) на UTF-8, особенно в средах, где используются старые приложения или файлы, созданные в системной кодировке. Также полезен при разработке приложений, работающих на разных ОС и требующих стабильности поведения при чтении и записи текстовых файлов.

Вопросы для размышления

  1. В каких случаях использование encoding="locale" может привести к непредсказуемому поведению в production-среде, особенно при работе с разными системными настройками?
  2. Какой компромисс между совместимостью с старыми файлами и безопасностью при использовании UTF-8 по умолчанию в Python 3.15, если файлы были созданы в cp1252 и не переписывались?
  3. При каких условиях может возникнуть failure mode при использовании subprocess.run() с text=True на системах с cp1252-кодировкой, и как это можно предотвратить без изменения кода?

Stateless MCP: How the 2026-07-28 Spec Removes Sessions

Открыть оригинал ↗ Перевод заголовка: Stateless MCP: как спецификация 2026-07-28 удаляет сессии

Новая версия протокола MCP с даты 2026-07-28 полностью устраняет сессии: теперь каждый запрос содержит всю необходимую информацию, и сервер не должен хранить состояние между запросами. Вместо того чтобы инициализироваться и передавать сессионный идентификатор, клиент теперь отправляет все метаданные в заголовках и теле запроса, что позволяет любому экземпляру сервера обрабатывать запрос без предыдущего состояния. Вместо хранения данных в памяти одного экземпляра сервера, данные перемещаются в "хэндлы", возвращаемые инструментами, и хранятся в общем хранилище, доступном всем экземплярам.

  • Каждый запрос включает в себя версию протокола, информацию о клиенте и его возможностях в метаданных, что устраняет необходимость в инициализации и сессионных идентификаторах.
  • Сессионные идентификаторы и потоковые соединения удаляются: серверы больше не сохраняют открытые соединения или сессии, и при сбое потока клиент пересылает запрос с новым идентификатором.
  • Вместо хранения состояния в памяти одного экземпляра, данные (например, плейлисты) теперь сохраняются в общем хранилище, доступном всем инстансам, что обеспечивает масштабируемость и отказоустойчивость.
  • Вместо прямого вмешательства в поток, серверы возвращают "хэндлы", которые клиент передаёт в последующие вызовы, позволяя избежать зависимости от конкретного экземпляра.
  • В случае запросов, требующих ввода от пользователя, используется механизм input_required, при котором сервер возвращает состояние и запрос повторяется с новым идентификатором, что делает систему устойчивой к сбоям и перезапускам.

Когда пригодится

Материал полезен при разработке распределённых AI-сервисов, где требуется высокая масштабируемость и отказоустойчивость, особенно при использовании нескольких экземпляров сервера за прокси. Он особенно актуален для систем, где сессии и состояние между запросами являются источником проблем при масштабировании, и где важна возможность перезапуска без потери данных.

Вопросы для размышления

  1. Какой минимальный вклад в сложность алгоритма должен быть введён для обеспечения безопасности при повторном вызове инструмента с использованием requestState, если не предусмотрено проверка подписи на стороне клиента?
  2. В каких сценариях может привести к нестабильности отказ от поддержки потоковых соединений и резумации при сбое, особенно при работе с инструментами, требующими ввода от пользователя в процессе выполнения?
  3. Какие риски связаны с использованием общего хранилища данных при высокой нагрузке, если не предусмотрена синхронизация и контроль доступа между экземплярами сервера?

Upgrade Python Version: A Safe Migration Guide

Открыть оригинал ↗ Перевод заголовка: Обновление версии Python: руководство по безопасной миграции

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

  1. Проверяйте релиз-заметки перед миграцией, чтобы понять, какие модули удаляются или меняются в новой версии.
  2. Устанавливайте новую версию Python рядом с текущей, чтобы избежать потери обратной совместимости.
  3. Используйте виртуальные окружения для изоляции каждой версии Python и тестирования кода в отдельных средах.
  4. Включайте предупреждения как ошибки на старой версии, чтобы выявить все вызовы, которые будут прерваны в новой версии.
  5. После исправления — запускайте тесты на новой версии, чтобы убедиться, что миграция не повлияла на функциональность.

Когда пригодится

Материал полезен при планировании обновления Python в проектах, где требуется безопасная и стабильная миграция без риска сбоя в работе, особенно при наличии зависимостей, которые не поддерживают новые версии.

Вопросы для размышления

  1. Какой компромисс между скоростью миграции и полнотой обнаружения проблем возникает, если использовать только тесты на старой версии с включёнными предупреждениями, не проверяя поведение на новой версии до исправлений?
  2. В каких сценариях может привести к сбоям использование виртуальных окружений, если они не настроены правильно или не обновляются при изменении Python-версии?
  3. Какой риск остаётся, если зависимость не предоставляет wheel-пакета для новой версии Python, и как это влияет на выбор версии для миграции?

SKILL.md Guide: Write a Good Skill for Coding Agents

Открыть оригинал ↗ Перевод заголовка: Руководство по написанию хорошей навыка для агентов кода

Навык — это папка с инструкциями для повторяющегося действия, который загружается только тогда, когда возникает соответствующая задача. Ключевой файл — SKILL.md — содержит YAML-метаданные с именем и описанием, а в теле — пошаговые инструкции. Описание критически важно: оно определяет, будет ли агент загружать навык вообще. Хороший навык включает в себя чёткое описание его функции, сценариев использования и случаев, когда его не применять. Основные принципы: структурированные шаги вместо текста, конкретные методы выполнения, короткие и точные описания, разделение деталей на ссылки, которые загружаются только при необходимости.

  1. Описание должно быть ясным и включать три компонента: что делает навык, когда его использовать и когда не использовать.
  2. В теле навыка используются пронумерованные шаги, а не объяснительный текст, чтобы агент последовательно выполнял действия.
  3. Каждый шаг должен быть конкретным — либо гибким (с пространством для выбора), либо жёстким (с обязательным выполнением), чтобы избежать ошибок.
  4. Важно разделять детали, которые нужны только в определённых случаях, на отдельные файлы в папке references/, которые загружаются по условию.
  5. Навык должен быть коротким (менее 500 строк и 5000 токенов), и включать в себя проверки на корректность вывода и работы с внешними скриптами при необходимости.

Когда пригодится

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

Вопросы для размышления

  1. Какой компромисс между точностью и гибкостью в шагах навыка возникает, если в задаче есть несколько допустимых решений, но агент должен выбрать одно из них?
  2. При каких условиях использование скрипта в навыке может привести к снижению надёжности, если агент не может выполнить его в условиях, отличных от тестовых?
  3. Как изменится поведение навыка при масштабировании — например, если в репозитории будет 100 навыков, и каждый из них требует отдельных ресурсов для загрузки?

System Design

DEV System Design

System Design Interview Framework: Navigating the 45-Minute Architecture Round

Открыть оригинал ↗ Перевод заголовка: Фреймворк системного проектирования: навигация по 45-минутному архитектурному раунду

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

  1. Важно строго ограничивать параллельную обработку (через семафоры) и избегать накопления заданий в памяти, чтобы предотвратить перегрузку и OOM-ошибки.
  2. Все внешние вызовы должны сопровождаться жесткими таймаутами, чтобы избежать зависаний и блокировки рабочих потоков.
  3. Все операции, изменяющие состояние, должны быть идемпотентными, чтобы гарантировать корректность при повторных попытках выполнения.
  4. Архитектура должна включать в себя наблюдаемость через метрики (например, латентность, ошибки), чтобы быстро выявлять сбои.
  5. Нужно проектировать отказоустойчивость с механизмами быстрого отказа и падения, а не просто ждать восстановления.

Когда пригодится

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

Вопросы для размышления

  1. Как изменится поведение системы при увеличении количества задач, если не использовать лимиты на параллельную обработку, и что это может привести к ошибкам в production?
  2. В каких сценариях идемпотентность может быть нарушена, и как это влияет на надежность системы при повторных попытках выполнения операций?
  3. Какой минимальный набор метрик должен быть включен в систему для эффективного обнаружения узких мест и сбоев, и как эти метрики могут быть интегрированы в реальную инфраструктуру без перегрузки?

Monolith versus distributed system

Открыть оригинал ↗ Перевод заголовка: Монолит против распределённой системы

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

  • Монолиты обеспечивают стабильность и низкую задержку благодаря простоте и однородности логики.
  • Они лучше справляются с согласованностью и доступностью, что критично для высокопроизводительных приложений.
  • В условиях безопасности монолиты уступают, так как не позволяют эффективно применять принцип минимальных привилегий.
  • Распределённые системы, несмотря на сложность, предпочтительны в критически важных и защищённых средах.
  • Выбор между архитектурами зависит от баланса между производительностью, надёжностью и безопасностью.

Когда пригодится

Материал полезен при проектировании систем, где важны производительность и согласованность, например, в корпоративных приложениях с низкой задержкой и стабильностью, но не подходит для решений, где безопасность и изоляция компонентов являются приоритетом.

Вопросы для размышления

  1. Какой компромисс между производительностью и безопасностью может быть достигнут в гибридной архитектуре, сочетающей монолит и распределённые сервисы?
  2. В каких условиях распределённая система может быть менее надёжной, чем монолит, несмотря на наличие событийной архитектуры?
  3. Как влияет масштабирование на безопасность в распределённых системах, и какие failure modes могут возникнуть при неоптимальной настройке привилегий?

Designing an Email Service at Scale

Открыть оригинал ↗ Перевод заголовка: Проектирование сервиса электронной почты на масштабе

Чтобы построить распределённую платформу, способную обрабатывать 1 миллиард пользователей, 50 миллиардов сообщений в день и петабайты данных без потерь, необходимо разделять архитектуру на два независимых потока: отправку и получение. Каждый поток использует специализированные компоненты — от REST-интерфейсов и хранения в S3 до очередей на Kafka и проверок в DNS — чтобы обеспечить надёжность, безопасность и масштабируемость. Ключевые технические вызовы включают юридическую обязательность ответа SMTP 250 OK, риск потери данных из-за неудачной записи в базу, угрозу блокировки IP-адреса при отправке балансов на фальшивые адреса и необходимость оптимизации модели хранения для поддержания низкой задержки.

  1. Как обеспечить, чтобы ответ SMTP 250 OK не приводил к утрате данных, если он должен подтверждать доставку, но при этом требует репликации в нескольких зонах?
  2. В каких условиях использование атомарной операции в Cassandra может привести к неожиданному поведению при масштабировании или отказах?
  3. Какие альтернативы модели шардинга по user_id и временным квадратам могут снизить риски роста задержек при увеличении числа пользователей?

Когда пригодится

Материал полезен при проектировании масштабируемых сервисов, где критичны надёжность доставки, безопасность и юридические обязательства, особенно в системах с высокой нагрузкой и большим объёмом данных.

Вопросы для размышления

  1. Вопрос?
  2. Вопрос?
  3. Вопрос?

Operational Durability: Building and Operating Resilient Production Systems

Открыть оригинал ↗ Перевод заголовка: Операционная устойчивость: создание и эксплуатация устойчивых производственных систем

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

  1. Устранение единичных точек отказа: каждый слой системы должен иметь явный режим деградации, чтобы поддерживать критическую функциональность даже при сбоях.
  2. Определение RTO и RPO как ключевых метрик: время восстановления и допустимая потеря данных напрямую влияют на архитектуру хранения и вычислений.
  3. Использование GitOps и Policy-as-Code для обеспечения воспроизводимости и автоматической проверки конфигураций, что снижает риски человеческих ошибок.
  4. Внедрение стратегий развертывания (blue-green или canary) для минимизации рисков при обновлениях и быстрого возврата к предыдущей версии.
  5. Работа с контейнерами требует строгого контроля ресурсов, пробы и бюджетов, чтобы избежать сбоев из-за перегрузки или нестабильности.

Когда пригодится

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

Вопросы для размышления

  1. Какой компромисс между затратами на репликацию и RTO может быть допустим в среде с ограниченным бюджетом, где RPO не может быть ниже 1 часа?
  2. В каких условиях PDB может привести к нежелательному поведению в кластере, если не учитывать динамику нагрузки и типы сервисов?
  3. Какие риски связаны с использованием короткого TTL в кэшировании секретов, если секреты обновляются реже, чем TTL, и при этом система не обновляет конфигурацию в реальном времени?

Decide where the error lands

Открыть оригинал ↗ Перевод заголовка: Определите, где ошибка оказывается

В распределённых системах ошибки не должны просто ловиться и скрываться — они должны быть направлены на конкретные компоненты, которые способны их обработать. Простое вложение try/catch не решает проблему: ошибка может оказаться у пользователя, не дать никакого уведомления и привести к неправильному поведению, как в случае с покупкой, где клиент получил сообщение об успешной оплате, но ничего не получили. Ключевая ошибка — в том, что система не задумывается, где именно ошибка должна "попасть", а просто ловит её и отбрасывает в лог. Правильный подход — проектировать систему так, чтобы каждый возможный сбой оказался в блоке, способном его обработать: в очереди (для задержки и повтора), в рабочем процессе (с автоматическим повтором и идемпотентностью) или в обмене с внешним сервисом. Если ошибка попадает на пользователя — это критическая ошибка, и она должна быть исключена. АИ-ассистенты часто предлагают "робастные" обработчики, которые просто ловят ошибки и возвращают 200, не задавая вопроса, кто и когда узнает о сбое.

Когда пригодится

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

Вопросы для размышления

  1. Как изменится поведение системы, если в очереди добавить ограничение на размер пакета, и как это повлияет на идемпотентность повторных вызовов?
  2. При каких условиях идемпотентность рабочего процесса может привести к непредсказуемым результатам, и как это может быть обнаружено в production?
  3. Если внешний сервис возвращает "not done" вместо "fine", как это влияет на сроки повторных попыток и как это может быть оценено в условиях реального использования?

Reversibility and Recovery in Healthcare Agentic AI

Открыть оригинал ↗ Перевод заголовка: Восстановимость и восстановление в агентных ИИ для здравоохранения

В здравоохранении безопасность ИИ оценивается не только по точности моделей, но и по способности системы безопасно реагировать на сбои. Агентные системы требуют четкой структуры состояний — от предложения до выполнения и сбоев — с контролем переходов и защитами на каждом этапе. При этом для операций, которые теоретически могут быть отменены, предусмотрены механизмы отмены, компенсации или возврата к предыдущему состоянию, особенно важно для многошаговых процессов, где могут возникнуть несоответствия или частичные успехи. Логи должны фиксировать не только действия, но и их авторизацию и временные метки, чтобы избежать уничтожения истории изменений. Некоторые действия, особенно с высоким риском, не могут быть отменены, и тогда требуется строгая предварительная проверка, явное одобрение и возможность поднятия на уровень управления. Умная агентная система должна быть способна остановиться, если неясно, как продолжать, а не пытаться исправить ошибку, продолжая выполнение.

Когда пригодится

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

Вопросы для размышления

  1. Какие риски могут возникнуть при попытке отменить действие в медицинской системе, если оно уже было записано в истории пациента?
  2. В каких условиях отмена действия может быть не только технически, но и этически неприемлема, даже если технически возможно?
  3. Как можно оценить сложность и масштабируемость архитектуры, обеспечивающей восстановление, при наличии зависимостей между шагами в клиническом потоке?

I Added a Fourth Shard in Python and 75% of the Keys Moved

Открыть оригинал ↗ Перевод заголовка: Я добавил четвёртую шард и 75% ключей переместилось

При простом шардинге — хеширование ключа и деление на количество шардов — добавление нового шарда приводит к масштабному перемещению данных. При переходе от 3 к 4 шардам 75% ключей перемещаются, а при росте от 10 к 11 шардам — до 91%, что противоречит упрощённому утверждению о 9% перераспределении. Основная причина — ключ остаётся на том же шарде только тогда, когда остатки хеша по старому и новому количеству шардов совпадают, что случается редко (в среднем 1 из 11). В отличие от этого, в системе с кольцом (ring) перемещение данных значительно меньше — около 25%, при условии равномерного распределения виртуальных узлов. При неравномерном распределении (один виртуальный узел на реальный) доля перемещённых ключей может варьироваться от 0,8% до 71,4%, что показывает высокую чувствительность к начальным условиям. Виртуальные узлы решают проблему неравномерного распределения нагрузки, делая распределение более предсказуемым и стабильным.

Когда пригодится

Материал полезен при проектировании распределённых систем, где важно понимать, как изменение количества шардов влияет на перемещение данных, особенно при выборе между модульным шардингом и кольцевой моделью. Особенно актуален для разработчиков, работающих с кэшами, CDN или системами хранения, где требуется минимизировать перераспределение при масштабировании.

Вопросы для размышления

  1. Почему при переходе от 10 к 11 шардам перемещается почти 91% ключей, а не около 9%, и как это связано с вероятностью совпадения остатков хеша?
  2. Как влияет количество виртуальных узлов на дисперсию нагрузки в кольцевой модели, и при каких условиях доля перемещённых ключей может достигать 70%?
  3. В чём заключается фундаментальный компромисс между простотой реализации модульного шардинга и стабильностью распределения при добавлении новых узлов?

InfoQ Architecture

Amazon Acquires DuckLabs and Integrates DuckDB in Amazon Aurora PostgreSQL

Открыть оригинал ↗ Перевод заголовка: Амазон приобрела DuckLabs и интегрировала DuckDB в Amazon Aurora PostgreSQL

Amazon приобрела компанию DuckLabs, разработчика аналитической БД DuckDB, и внедрила её в сервис Amazon Aurora PostgreSQL. Участники проекта, включая создателей DuckDB, продолжат руководить разработкой и техническим направлением открытого исходного кода, оставаясь в рамках неприбыльной DuckDB Foundation. Интеграция позволяет в одном запросе объединять актуальные транзакционные данные из Aurora с историческими данными из Amazon S3, Apache Iceberg и Parquet, без необходимости в ETL-пайплайнах и дополнительных сетевых переходов. Это упрощает архитектуру, снижает затраты и минимизирует риски синхронизации данных.

  • DuckDB остаётся бесплатным и открытым исходным кодом под лицензией MIT, что поддерживает экосистему и привлекает разработчиков.
  • Интеграция в Aurora PostgreSQL позволяет выполнять сложные аналитические запросы прямо в базе, не требуя промежуточных этапов обработки.
  • Работа с данными в S3, Iceberg и Parquet происходит в рамках Aurora, что уменьшает задержки и снижает нагрузку на инфраструктуру.
  • Услуга доступна в всех коммерческих регионах AWS и AWS GovCloud без дополнительной платы — стоимость зависит только от вычислительных ресурсов и запросов к S3.
  • Внедрение ориентировано на AI-работы, где часто требуется быстрая экспертиза данных перед принятием решений.

Когда пригодится

Материал полезен для разработчиков и архитекторов, работающих над системами аналитики, где требуется быстрая интеграция операционных и исторических данных без сложных ETL-процессов, особенно в контексте AI-агентов и динамических аналитических запросов.

Вопросы для размышления

  1. Какова вероятность того, что подобная интеграция может быть применена к системам с высокой нагрузкой на транзакции, если DuckDB не оптимизирован для больших объёмов данных?
  2. В каких сценариях может возникнуть конфликт между производительностью DuckDB и требованиями к безопасности данных в AWS, особенно при работе с чувствительными данными в S3?
  3. Какие ограничения могут быть у DuckDB в контексте поддержки сложных JOIN-операций между большими таблицами в Iceberg и Aurora, если не используется расширенная архитектура?

React Java

20 System Design Courses That Actually Prepare You for Interviews

Открыть оригинал ↗ Перевод заголовка: 20 курсов по системному проектированию, которые действительно готовят к собеседованиям

Системное проектирование — одна из самых сложных тем в технических интервью, и многие кандидаты не справляются с вопросами по масштабируемости, балансировке нагрузки, отказоустойчивости и другим ключевым аспектам. В статье представлены 20 проверенных курсов от ведущих платформ, разработанных экспертами, имеющими опыт как в проведении, так и в подготовке к собеседованиям. Курсы охватывают как базовые принципы (например, масштабирование, кэширование, выбор баз данных), так и продвинутые темы (распределённые системы, шардинг, отказоустойчивость). Особое внимание уделено практическим задачам, интерактивным упражнениям и реальным сценариям, таким как разработка сервиса по сокращению URL или парковки. Многие из курсов включают мок-интервью с обратной связью, что позволяет лучше подготовиться к реальным условиям.

Когда пригодится

Материал особенно полезен для кандидатов, стремящихся пройти собеседования в FAANG-компаниях, и для тех, кто хочет не просто выучить ответы, а понять фундаментальные принципы проектирования систем. Особенно ценен для тех, кто уже имеет базу в программировании, но не имеет опыта работы с распределёнными системами или масштабируемыми архитектурами.

Вопросы для размышления

  1. В каких случаях использование NoSQL баз данных может быть более эффективным, чем SQL, и как это влияет на выбор архитектуры при проектировании системы?
  2. Какой компромисс между масштабируемостью и отказоустойчивостью может быть наиболее рациональным в условиях ограниченного бюджета ресурсов и времени?
  3. В каких сценариях шардинг базы данных может привести к ухудшению производительности, и как это может быть обнаружено на практике?

Coding / Algorithms

LeetCode.ca Solutions

4041 - Minimum Operations to Form Subset Sum II

Открыть оригинал ↗ Перевод заголовка: 4041 - Минимальное количество операций для формирования подмножественной суммы II

В задаче требуется найти минимальное количество операций (умножение на 2 или деление на 2 с округлением вниз), чтобы в массиве после операций существовало подмножество, сумма которого равна заданному значению. Каждая операция может быть выполнена в любом порядке, но любая последовательность умножения, за которой следует деление, не меняет значение элемента — это пустая операция. Следовательно, любая последовательность операций эквивалентна сначала делению на 2^i, затем умножению на 2^j, что приводит к значению ⌊x/2^i⌋×2^j за i+j операций. Задача сводится к 0-1-пакету: для каждого элемента мы рассматриваем все возможные пары (i,j), и ищем минимальное количество операций для достижения суммы sum. Если такой путь невозможен, возвращаем -1.

  • Каждый элемент может быть преобразован в значение ⌊x/2^i⌋×2^j за i+j операций, где i — количество делений, j — количество умножений.
  • Внутри пакета для каждого элемента перебираются все возможные значения i и j, и для каждого из них вычисляется минимальная стоимость достижения суммы.
  • Используется динамическое программирование с массивом f[w], где f[w] — минимальное количество операций для подмножества, сумма которого равна w.
  • При неразрешимости (f[sum] остается бесконечным) возвращается -1.

Когда пригодится

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

Вопросы для размышления

  1. Как изменится ответ, если в задаче разрешено использовать более чем один элемент для формирования одного значения через операции, и при этом сумма должна быть достигнута с помощью подмножества, а не всей последовательности?
  2. При каких условиях может быть достигнута минимальная стоимость, если исходный массив содержит только чётные числа, и как это повлияет на количество делений и умножений?
  3. Какова сложность алгоритма при увеличении суммы sum до 10000, и как это влияет на память и время выполнения при фиксированном размере массива?