{"version":1,"topics":[{"id":"js-types","title":"JS: типы и приведение","track":"js","careers":["frontend"],"pillar":"fundamentals","order":10,"grade":"intern","prerequisites":[]},{"id":"js-data","title":"JS: массивы, объекты, синтаксис","track":"js","careers":["frontend"],"pillar":"fundamentals","order":11,"grade":"intern","prerequisites":[]},{"id":"js-scope","title":"JS: области видимости, замыкания, this","track":"js","careers":["frontend"],"pillar":"fundamentals","order":20,"grade":"junior","prerequisites":["js-types"]},{"id":"js-oop","title":"JS: прототипы и классы","track":"js","careers":["frontend"],"pillar":"fundamentals","order":30,"grade":"junior","prerequisites":["js-scope"]},{"id":"js-async","title":"JS: Event Loop и промисы","track":"js","careers":["frontend"],"pillar":"fundamentals","order":31,"grade":"junior","prerequisites":["js-scope"]},{"id":"practice","title":"Практика: задачи с собеседований","track":"practice","careers":["frontend"],"pillar":"fundamentals","order":40,"grade":"junior","prerequisites":["js-data","js-scope"]},{"id":"ng-rxjs","title":"RxJS: операторы высшего порядка","track":"angular","careers":["frontend"],"pillar":"platform","order":50,"grade":"middle","prerequisites":["js-async"]},{"id":"supply-chain","title":"Безопасность цепочки поставок","track":"engineering","careers":["frontend"],"pillar":"reliability","order":60,"grade":"middle","prerequisites":["js-data","js-async"]},{"id":"html-basics","title":"HTML: структура и семантика","track":"web","careers":["frontend"],"pillar":"platform","order":100,"grade":"intern","prerequisites":[]},{"id":"css-basics","title":"CSS: каскад, боксовая модель, позиционирование","track":"web","careers":["frontend"],"pillar":"platform","order":101,"grade":"intern","prerequisites":["html-basics"]},{"id":"dom-events","title":"DOM и события","track":"web","careers":["frontend"],"pillar":"platform","order":102,"grade":"intern","prerequisites":["html-basics","js-types"]},{"id":"web-http","title":"HTTP, REST и работа с API","track":"web","careers":["frontend"],"pillar":"data","order":103,"grade":"intern","prerequisites":[]},{"id":"dev-tools-git","title":"Git, DevTools и инструменты разработчика","track":"engineering","careers":["frontend"],"pillar":"reliability","order":104,"grade":"intern","prerequisites":[]},{"id":"team-basics","title":"Как работать в команде: первые шаги","track":"engineering","careers":["frontend","backend"],"pillar":"people","order":105,"grade":"intern","prerequisites":[]},{"id":"css-layout","title":"Вёрстка: Flexbox, Grid, адаптивность, SCSS","track":"web","careers":["frontend"],"pillar":"platform","order":110,"grade":"junior","prerequisites":["css-basics"]},{"id":"a11y","title":"Доступность (a11y)","track":"web","careers":["frontend"],"pillar":"quality","order":111,"grade":"junior","prerequisites":["html-basics"]},{"id":"ux-quality","title":"UX-качество экранов: состояния и темы","track":"web","careers":["frontend"],"pillar":"quality","order":112,"grade":"junior","prerequisites":["css-basics"]},{"id":"ts-basics","title":"TypeScript: базовые типы и generics","track":"ts","careers":["frontend"],"pillar":"fundamentals","order":113,"grade":"junior","prerequisites":["js-types"]},{"id":"ng-components","title":"Angular: компоненты, шаблоны, control flow","track":"angular","careers":["frontend"],"pillar":"platform","order":114,"grade":"junior","prerequisites":["ts-basics"]},{"id":"ng-di","title":"Angular: внедрение зависимостей","track":"angular","careers":["frontend"],"pillar":"platform","order":115,"grade":"junior","prerequisites":["ng-components"]},{"id":"ng-forms","title":"Angular: формы и валидация","track":"angular","careers":["frontend"],"pillar":"platform","order":116,"grade":"junior","prerequisites":["ng-components"]},{"id":"ng-router","title":"Angular: роутинг","track":"angular","careers":["frontend"],"pillar":"platform","order":117,"grade":"junior","prerequisites":["ng-components"]},{"id":"ng-http","title":"Angular: HttpClient и интерсепторы","track":"angular","careers":["frontend"],"pillar":"data","order":118,"grade":"junior","prerequisites":["ng-di","web-http","js-async"]},{"id":"web-security-basics","title":"Безопасность веба: XSS, CSRF, секреты","track":"web","careers":["frontend"],"pillar":"reliability","order":119,"grade":"junior","prerequisites":["web-http"]},{"id":"dev-process","title":"Процесс разработки и код-ревью","track":"engineering","careers":["frontend","backend"],"pillar":"people","order":120,"grade":"junior","prerequisites":["team-basics"]},{"id":"unit-testing","title":"Юнит-тесты и сборка проекта","track":"engineering","careers":["frontend"],"pillar":"quality","order":121,"grade":"junior","prerequisites":["js-async"]},{"id":"ng-signals","title":"Angular: сигналы и состояние","track":"angular","careers":["frontend"],"pillar":"platform","order":130,"grade":"middle","prerequisites":["ng-components"]},{"id":"ng-change-detection","title":"Angular: change detection, zoneless, @defer","track":"angular","careers":["frontend"],"pillar":"platform","order":131,"grade":"middle","prerequisites":["ng-signals"]},{"id":"ng-ssr","title":"Angular: SSR, SSG и гидратация","track":"angular","careers":["frontend"],"pillar":"platform","order":132,"grade":"middle","prerequisites":["ng-router","dom-events"]},{"id":"web-vitals","title":"Core Web Vitals и оптимизация бандла","track":"web","careers":["frontend"],"pillar":"quality","order":133,"grade":"middle","prerequisites":["dom-events","web-http"]},{"id":"arch-patterns","title":"Архитектура: SOLID, паттерны, слои","track":"engineering","careers":["frontend"],"pillar":"architecture","order":134,"grade":"middle","prerequisites":["js-oop","ts-basics"]},{"id":"design-system","title":"Atomic Design и дизайн-система","track":"angular","careers":["frontend"],"pillar":"architecture","order":135,"grade":"middle","prerequisites":["ng-components"]},{"id":"testing-strategy","title":"Тестирование: интеграция, E2E, дублёры, качество","track":"engineering","careers":["frontend"],"pillar":"quality","order":136,"grade":"middle","prerequisites":["unit-testing"]},{"id":"frontend-delivery","title":"CI/CD, Git и зависимости во фронтенде","track":"engineering","careers":["frontend"],"pillar":"reliability","order":137,"grade":"middle","prerequisites":["dev-tools-git"]},{"id":"i18n-time","title":"i18n, локали и время","track":"web","careers":["frontend"],"pillar":"platform","order":138,"grade":"middle","prerequisites":["js-data"]},{"id":"engineering-craft","title":"Инженерная зрелость: оценка, ревью, улучшения","track":"engineering","careers":["frontend","backend"],"pillar":"people","order":139,"grade":"middle","prerequisites":["dev-process"]},{"id":"frameworks-landscape","title":"Другие фреймворки: React, Vue, Svelte","track":"web","careers":["frontend"],"pillar":"platform","order":140,"grade":"middle","prerequisites":["ng-components"]},{"id":"frontend-architecture","title":"Архитектура фронтенда: DDD, микрофронтенды, PWA, BFF","track":"engineering","careers":["frontend"],"pillar":"architecture","order":150,"grade":"senior","prerequisites":["arch-patterns"]},{"id":"perf-debugging","title":"Отладка производительности в продакшене","track":"angular","careers":["frontend"],"pillar":"quality","order":151,"grade":"senior","prerequisites":["ng-change-detection","web-vitals"]},{"id":"ts-advanced","title":"TypeScript на уровне типов и AST","track":"ts","careers":["frontend"],"pillar":"fundamentals","order":152,"grade":"senior","prerequisites":["ts-basics"]},{"id":"product-ux","title":"UX продукта и продуктовая аналитика","track":"engineering","careers":["frontend"],"pillar":"quality","order":153,"grade":"senior","prerequisites":["ux-quality"]},{"id":"incidents-oncall","title":"Дежурства, инциденты и постмортемы","track":"engineering","careers":["frontend","backend"],"pillar":"reliability","order":154,"grade":"senior","prerequisites":["engineering-craft"]},{"id":"tech-leadership","title":"Техническое лидерство в команде","track":"leadership","careers":["frontend","backend"],"pillar":"people","order":155,"grade":"senior","prerequisites":["engineering-craft"]},{"id":"frontend-platform","title":"Фронтенд-платформа и инфраструктура","track":"engineering","careers":["frontend"],"pillar":"architecture","order":160,"grade":"lead","prerequisites":["frontend-architecture"]},{"id":"people-leadership","title":"Люди: мотивация, делегирование, переговоры","track":"leadership","careers":["frontend","backend"],"pillar":"people","order":161,"grade":"lead","prerequisites":["tech-leadership"]},{"id":"org-transformation","title":"Трансформация и инженерная культура компании","track":"leadership","careers":["frontend","backend"],"pillar":"people","order":170,"grade":"principal","prerequisites":["people-leadership","tech-leadership"]},{"id":"cs-basics","title":"C#: синтаксис, типы, значимые и ссылочные","track":"csharp","careers":["backend"],"pillar":"fundamentals","order":300,"grade":"intern","prerequisites":[]},{"id":"cs-oop","title":"C#: ООП, классы, интерфейсы, record","track":"csharp","careers":["backend"],"pillar":"fundamentals","order":301,"grade":"intern","prerequisites":["cs-basics"]},{"id":"dotnet-basics","title":".NET: SDK, проекты, NuGet, консольное приложение","track":"dotnet","careers":["backend"],"pillar":"platform","order":302,"grade":"intern","prerequisites":[]},{"id":"be-http-rest","title":"HTTP, REST и форматы данных для бэкенда","track":"web","careers":["backend"],"pillar":"data","order":310,"grade":"junior","prerequisites":[]},{"id":"aspnet-basics","title":"ASP.NET Core: Minimal API, контроллеры, маршрутизация","track":"dotnet","careers":["backend"],"pillar":"platform","order":311,"grade":"junior","prerequisites":["dotnet-basics","cs-oop","be-http-rest"]},{"id":"aspnet-di-pipeline","title":"ASP.NET Core: DI, время жизни, middleware, конфигурация","track":"dotnet","careers":["backend"],"pillar":"platform","order":312,"grade":"junior","prerequisites":["aspnet-basics"]},{"id":"cs-async","title":"C#: async/await, Task, отмена","track":"csharp","careers":["backend"],"pillar":"fundamentals","order":313,"grade":"junior","prerequisites":["cs-basics"]},{"id":"sql-basics","title":"SQL: CRUD, JOIN, ключи и индексы","track":"data","careers":["backend"],"pillar":"data","order":314,"grade":"junior","prerequisites":[]},{"id":"ef-core-basics","title":"EF Core и Dapper: модели, запросы, миграции","track":"data","careers":["backend"],"pillar":"data","order":315,"grade":"junior","prerequisites":["sql-basics","cs-oop"]},{"id":"be-auth-basics","title":"Аутентификация и авторизация: JWT, OAuth, политики","track":"dotnet","careers":["backend"],"pillar":"reliability","order":316,"grade":"junior","prerequisites":["aspnet-di-pipeline"]},{"id":"be-testing-basics","title":"Юнит-тесты в .NET: xUnit, NUnit, дублёры","track":"dotnet","careers":["backend"],"pillar":"quality","order":317,"grade":"junior","prerequisites":["cs-oop"]},{"id":"be-git-docker","title":"Git и Docker для бэкенда","track":"engineering","careers":["backend"],"pillar":"reliability","order":318,"grade":"junior","prerequisites":[]},{"id":"be-debugging","title":"Отладка, исключения и логирование в .NET","track":"dotnet","careers":["backend"],"pillar":"quality","order":319,"grade":"junior","prerequisites":["cs-basics"]},{"id":"cs-linq-generics","title":"C#: LINQ, коллекции, дженерики, IQueryable","track":"csharp","careers":["backend"],"pillar":"fundamentals","order":330,"grade":"middle","prerequisites":["cs-oop"]},{"id":"db-design-perf","title":"БД: связи, транзакции, оптимизация запросов","track":"data","careers":["backend"],"pillar":"data","order":331,"grade":"middle","prerequisites":["ef-core-basics"]},{"id":"be-crypto","title":"Шифрование, хеширование и хранение паролей","track":"engineering","careers":["backend"],"pillar":"reliability","order":332,"grade":"middle","prerequisites":["be-auth-basics"]},{"id":"be-security-owasp","title":"Безопасность API: OWASP Top 10, инъекции, секреты","track":"engineering","careers":["backend"],"pillar":"reliability","order":333,"grade":"middle","prerequisites":["be-auth-basics"]},{"id":"be-architecture","title":"Архитектура бэкенда: Repository, UoW, CQRS, монолит и микросервисы","track":"engineering","careers":["backend"],"pillar":"architecture","order":334,"grade":"middle","prerequisites":["aspnet-di-pipeline"]},{"id":"be-messaging-cache","title":"Кеш и очереди: Redis, RabbitMQ, Kafka","track":"engineering","careers":["backend"],"pillar":"data","order":335,"grade":"middle","prerequisites":["aspnet-di-pipeline"]},{"id":"be-api-protocols","title":"API-протоколы: OpenAPI, gRPC, GraphQL, версионирование","track":"dotnet","careers":["backend"],"pillar":"data","order":336,"grade":"middle","prerequisites":["aspnet-basics"]},{"id":"be-testing-advanced","title":"Интеграционные, нагрузочные и мутационные тесты","track":"dotnet","careers":["backend"],"pillar":"quality","order":337,"grade":"middle","prerequisites":["be-testing-basics"]},{"id":"be-delivery","title":"CI/CD, Docker Compose, Kubernetes и облако","track":"engineering","careers":["backend"],"pillar":"reliability","order":338,"grade":"middle","prerequisites":["be-git-docker"]},{"id":"be-clean-ddd","title":"Clean/Hexagonal Architecture и DDD","track":"engineering","careers":["backend"],"pillar":"architecture","order":350,"grade":"senior","prerequisites":["be-architecture"]},{"id":"be-distributed","title":"Распределённые системы: события, outbox, саги, идемпотентность","track":"engineering","careers":["backend"],"pillar":"architecture","order":351,"grade":"senior","prerequisites":["be-architecture","be-messaging-cache"]},{"id":"be-observability","title":"Наблюдаемость: OpenTelemetry, логи, метрики, трейсы","track":"engineering","careers":["backend"],"pillar":"reliability","order":352,"grade":"senior","prerequisites":["be-debugging"]},{"id":"be-performance","title":"Производительность .NET и EF Core: память, GC, запросы","track":"dotnet","careers":["backend"],"pillar":"quality","order":353,"grade":"senior","prerequisites":["db-design-perf","cs-async"]},{"id":"be-appsec-pipeline","title":"AppSec в конвейере: SAST/DAST, секреты, контейнеры","track":"engineering","careers":["backend"],"pillar":"reliability","order":354,"grade":"senior","prerequisites":["be-security-owasp","be-delivery"]},{"id":"be-resilience","title":"Отказоустойчивость: resilience, multi-region, chaos","track":"engineering","careers":["backend"],"pillar":"reliability","order":370,"grade":"lead","prerequisites":["be-distributed"]},{"id":"be-enterprise","title":"Enterprise-платформа: HA, policy as code, OLTP/OLAP, compliance","track":"engineering","careers":["backend"],"pillar":"architecture","order":380,"grade":"principal","prerequisites":["be-resilience"]}],"concepts":[{"id":"release-age","title":"Возраст релиза и безопасное разрешение зависимостей","sources":[{"level":"beginner","title":"npm — min-release-age","url":"https://docs.npmjs.com/cli/v11/using-npm/config/#min-release-age","lang":"en"},{"level":"normal","title":"pnpm — minimumReleaseAge","url":"https://pnpm.io/settings/dependency-resolution#minimumreleaseage","lang":"en"},{"level":"advanced","title":"Bun — minimum release age","url":"https://bun.sh/docs/pm/cli/install#minimum-release-age","lang":"en"}],"rule":"Свежий релиз — ещё не проверенный релиз: дай версии отлежаться несколько дней, прежде чем ставить."},{"id":"dependency-trust","title":"Доверие к зависимостям и воспроизводимость сборки","sources":[{"level":"beginner","title":"npm — аудит зависимостей","url":"https://docs.npmjs.com/auditing-package-dependencies-for-security-vulnerabilities/","lang":"en"},{"level":"normal","title":"Yarn — настройки безопасности","url":"https://yarnpkg.com/configuration/yarnrc#npmMinimalAgeGate","lang":"en"},{"level":"advanced","title":"npm — provenance","url":"https://docs.npmjs.com/generating-provenance-statements/","lang":"en"}],"rule":"Lockfile фиксирует, что ставим; аудит и provenance — кому доверяем. Без lockfile сборку не повторить."},{"id":"types","title":"Типы данных","sources":[{"level":"beginner","title":"learn.javascript.ru — Типы данных","url":"https://learn.javascript.ru/types","lang":"ru"},{"level":"normal","title":"MDN — JavaScript data types and data structures","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures","lang":"en"},{"level":"advanced","title":"ECMA-262 — ECMAScript Language Types","url":"https://tc39.es/ecma262/#sec-ecmascript-language-types","lang":"en"}],"rule":"typeof знает 8 ответов. Две ловушки: typeof null → \"object\", функции и классы → \"function\"."},{"id":"coercion","title":"Приведение типов и сравнение","sources":[{"level":"beginner","title":"learn.javascript.ru — Преобразование типов","url":"https://learn.javascript.ru/type-conversions","lang":"ru"},{"level":"normal","title":"MDN — Equality comparisons and sameness","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness","lang":"en"},{"level":"advanced","title":"ECMA-262 — IsLooselyEqual","url":"https://tc39.es/ecma262/#sec-islooselyequal","lang":"en"}],"rule":"Плюс со строкой склеивает, остальная арифметика приводит к числу. Сравнивай через ===; == оставь для x == null."},{"id":"modern-syntax","title":"?. и ??","sources":[{"level":"beginner","title":"learn.javascript.ru — Оператор нулевого слияния","url":"https://learn.javascript.ru/nullish-coalescing-operator","lang":"ru"},{"level":"normal","title":"MDN — Optional chaining (?.)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining","lang":"en"},{"level":"advanced","title":"TC39 proposal — Optional Chaining","url":"https://github.com/tc39/proposal-optional-chaining","lang":"en"}],"rule":"?? заменяет только null/undefined, || — любое ложное (0, \"\", false). ?. обрывает цепочку на null/undefined."},{"id":"array-methods","title":"Методы массивов","sources":[{"level":"beginner","title":"learn.javascript.ru — Методы массивов","url":"https://learn.javascript.ru/array-methods","lang":"ru"},{"level":"normal","title":"MDN — Array","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array","lang":"en"},{"level":"advanced","title":"TC39 proposal — Change Array by Copy","url":"https://github.com/tc39/proposal-change-array-by-copy","lang":"en"}],"rule":"Мутируют: push, pop, shift, unshift, splice, sort, reverse, fill. Новый массив: map, filter, slice, toSorted, toReversed."},{"id":"spread-destructuring","title":"Spread, rest, деструктуризация","sources":[{"level":"beginner","title":"learn.javascript.ru — Деструктурирующее присваивание","url":"https://learn.javascript.ru/destructuring-assignment","lang":"ru"},{"level":"normal","title":"MDN — Spread syntax","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax","lang":"en"},{"level":"advanced","title":"ECMA-262 — Destructuring Assignment","url":"https://tc39.es/ecma262/#sec-destructuring-assignment","lang":"en"}],"rule":"... в выражении раскладывает (spread), в параметрах и слева от = собирает остаток (rest)."},{"id":"copying","title":"Поверхностное и глубокое копирование","sources":[{"level":"beginner","title":"learn.javascript.ru — Копирование объектов","url":"https://learn.javascript.ru/object-copy","lang":"ru"},{"level":"normal","title":"MDN — structuredClone()","url":"https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone","lang":"en"},{"level":"advanced","title":"HTML Standard — Safe passing of structured data","url":"https://html.spec.whatwg.org/multipage/structured-data.html","lang":"en"}],"rule":"Spread и Object.assign копируют один уровень. Глубоко — structuredClone (функции не копирует, прототип класса теряет)."},{"id":"closures","title":"Замыкания","sources":[{"level":"beginner","title":"learn.javascript.ru — Замыкание","url":"https://learn.javascript.ru/closure","lang":"ru"},{"level":"normal","title":"MDN — Closures","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures","lang":"en"},{"level":"advanced","title":"You Don't Know JS Yet — Scope & Closures","url":"https://github.com/getify/You-Dont-Know-JS/tree/2nd-ed/scope-closures","lang":"en"}],"rule":"Функция помнит переменные, а не их значения: она видит окружение, где создана, в его текущем состоянии."},{"id":"variables","title":"var/let/const, hoisting, TDZ","sources":[{"level":"beginner","title":"learn.javascript.ru — Устаревшее ключевое слово var","url":"https://learn.javascript.ru/var","lang":"ru"},{"level":"normal","title":"MDN — let (TDZ)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let","lang":"en"},{"level":"advanced","title":"ECMA-262 — Let and Const Declarations","url":"https://tc39.es/ecma262/#sec-let-and-const-declarations","lang":"en"}],"rule":"const по умолчанию, let если переприсваиваешь, var никогда. let/const до объявления — ReferenceError (TDZ), var — undefined."},{"id":"this","title":"this, call/apply/bind","sources":[{"level":"beginner","title":"learn.javascript.ru — Методы объекта, this","url":"https://learn.javascript.ru/object-methods","lang":"ru"},{"level":"normal","title":"MDN — this","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this","lang":"en"},{"level":"advanced","title":"ECMA-262 — OrdinaryCallBindThis","url":"https://tc39.es/ecma262/#sec-ordinarycallbindthis","lang":"en"}],"rule":"this задаёт вызов, а не объявление: смотри, что слева от точки. У стрелки своего this нет — она берёт внешний."},{"id":"prototypes","title":"Прототипное наследование","sources":[{"level":"beginner","title":"learn.javascript.ru — Прототипное наследование","url":"https://learn.javascript.ru/prototype-inheritance","lang":"ru"},{"level":"normal","title":"MDN — Inheritance and the prototype chain","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain","lang":"en"},{"level":"advanced","title":"V8 — Fast properties","url":"https://v8.dev/blog/fast-properties","lang":"en"}],"rule":"Нет свойства у объекта — поиск идёт по цепочке [[Prototype]] до null. Методы живут в prototype, данные — в экземпляре."},{"id":"classes","title":"Классы, приватные поля","sources":[{"level":"beginner","title":"learn.javascript.ru — Классы","url":"https://learn.javascript.ru/class","lang":"ru"},{"level":"normal","title":"MDN — Classes","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes","lang":"en"},{"level":"advanced","title":"V8 — Public and private class fields","url":"https://v8.dev/features/class-fields","lang":"en"}],"rule":"class — синтаксис над прототипами. #поле приватно в рантайме; private в TypeScript — только при компиляции."},{"id":"event-loop","title":"Event Loop","sources":[{"level":"beginner","title":"learn.javascript.ru — Событийный цикл","url":"https://learn.javascript.ru/event-loop","lang":"ru"},{"level":"normal","title":"MDN — JavaScript execution model","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Execution_model","lang":"en"},{"level":"advanced","title":"Jake Archibald — Tasks, microtasks, queues and schedules","url":"https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/","lang":"en"}],"rule":"Одна макрозадача → все микрозадачи → отрисовка, если нужна. Поэтому промис всегда раньше setTimeout(0)."},{"id":"promises","title":"Промисы и комбинаторы","sources":[{"level":"beginner","title":"learn.javascript.ru — Promise API","url":"https://learn.javascript.ru/promise-api","lang":"ru"},{"level":"normal","title":"MDN — Promise","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise","lang":"en"},{"level":"advanced","title":"V8 — Faster async functions and promises","url":"https://v8.dev/blog/fast-async","lang":"en"}],"rule":"all падает на первой ошибке; allSettled ждёт всех; race — первый завершённый; any — первый успешный."},{"id":"rxjs-flattening","title":"switchMap / mergeMap / concatMap / exhaustMap","sources":[{"level":"beginner","title":"Learn RxJS — switchMap","url":"https://www.learnrxjs.io/learn-rxjs/operators/transformation/switchmap","lang":"en"},{"level":"normal","title":"RxJS docs — switchMap","url":"https://rxjs.dev/api/operators/switchMap","lang":"en"},{"level":"advanced","title":"RxJS source — switchMap.ts","url":"https://github.com/ReactiveX/rxjs/blob/master/packages/rxjs/src/internal/operators/switchMap.ts","lang":"en"}],"rule":"switch — отменяет старый, merge — все параллельно, concat — по очереди, exhaust — игнорирует новые, пока занят."},{"id":"practice-algorithms","title":"Алгоритмические задачи","sources":[{"level":"beginner","title":"learn.javascript.ru — Рекурсия и стек","url":"https://learn.javascript.ru/recursion","lang":"ru"},{"level":"normal","title":"MDN — Array.prototype.flat()","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat","lang":"en"},{"level":"advanced","title":"javascript-algorithms (trekhleb)","url":"https://github.com/trekhleb/javascript-algorithms","lang":"en"}],"rule":"Сначала назови контракт: вход, выход и крайние случаи (пусто, null, вложенность). Потом код и его сложность."},{"id":"debounce-throttle","title":"Debounce и throttle","sources":[{"level":"beginner","title":"learn.javascript.ru — Декораторы и переадресация вызова","url":"https://learn.javascript.ru/call-apply-decorators","lang":"ru"},{"level":"normal","title":"CSS-Tricks — Debouncing and Throttling Explained","url":"https://css-tricks.com/debouncing-throttling-explained-examples/","lang":"en"},{"level":"advanced","title":"RxJS docs — throttleTime","url":"https://rxjs.dev/api/operators/throttleTime","lang":"en"}],"rule":"Debounce ждёт паузы (поиск при вводе). Throttle — не чаще раза за интервал (скролл, resize)."},{"id":"currying-purity","title":"Каррирование и чистые функции","sources":[{"level":"beginner","title":"learn.javascript.ru — Каррирование","url":"https://learn.javascript.ru/currying-partials","lang":"ru"},{"level":"normal","title":"Mostly Adequate Guide — Currying","url":"https://mostly-adequate.gitbook.io/mostly-adequate-guide/ch04","lang":"en"},{"level":"advanced","title":"Functional-Light JavaScript","url":"https://github.com/getify/Functional-Light-JS","lang":"en"}],"rule":"Чистая функция: тот же вход — тот же выход, без побочных эффектов. Каррирование: f(a, b) → f(a)(b)."},{"id":"polyfills","title":"Полифиллы","sources":[{"level":"beginner","title":"learn.javascript.ru — Полифилы и транспайлеры","url":"https://learn.javascript.ru/polyfills","lang":"ru"},{"level":"normal","title":"MDN — Function.prototype.bind()","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind","lang":"en"},{"level":"advanced","title":"core-js","url":"https://github.com/zloirock/core-js","lang":"en"}],"rule":"Полифилл добавляет недостающее API и сначала проверяет, есть ли оно. Новый синтаксис полифиллом не добавить — его транспилируют."},{"id":"html-semantics","title":"Семантическая разметка","sources":[{"level":"beginner","title":"web.dev Learn HTML — Semantic HTML","url":"https://web.dev/learn/html/semantic-html","lang":"en"},{"level":"normal","title":"MDN — HTML: A good basis for accessibility","url":"https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Accessibility/HTML","lang":"en"},{"level":"advanced","title":"HTML Standard — Semantics","url":"https://html.spec.whatwg.org/multipage/dom.html#semantics-2","lang":"en"}],"rule":"Тег выбирают по смыслу, а не по виду: header, nav, main, button. Семантика бесплатно даёт доступность и SEO."},{"id":"css-cascade","title":"Каскад и специфичность","sources":[{"level":"beginner","title":"web.dev Learn CSS — The cascade","url":"https://web.dev/learn/css/the-cascade","lang":"en"},{"level":"normal","title":"MDN — Specificity","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity","lang":"en"},{"level":"advanced","title":"MDN — @layer","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/@layer","lang":"en"}],"rule":"Порядок спора: важность → слой → специфичность → порядок в коде. Почему применился стиль — смотри Computed в DevTools."},{"id":"box-model","title":"Боксовая модель и позиционирование","sources":[{"level":"beginner","title":"web.dev Learn CSS — Box Model","url":"https://web.dev/learn/css/box-model","lang":"en"},{"level":"normal","title":"MDN — box-sizing","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing","lang":"en"},{"level":"advanced","title":"MDN — position","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/position","lang":"en"}],"rule":"Ширина = content + padding + border, пока нет box-sizing: border-box. absolute отсчитывается от ближайшего позиционированного предка."},{"id":"dom-api","title":"Поиск и изменение DOM","sources":[{"level":"beginner","title":"learn.javascript.ru — Поиск: getElement*, querySelector*","url":"https://learn.javascript.ru/searching-elements-dom","lang":"ru"},{"level":"normal","title":"MDN — Node.textContent","url":"https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent","lang":"en"},{"level":"advanced","title":"MDN — Element.innerHTML (security considerations)","url":"https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML","lang":"en"}],"rule":"querySelector — первый подходящий, querySelectorAll — все. textContent вставляет текст, innerHTML парсит HTML — с чужими данными опасно."},{"id":"event-propagation","title":"Всплытие событий и делегирование","sources":[{"level":"beginner","title":"learn.javascript.ru — Всплытие и погружение","url":"https://learn.javascript.ru/bubbling-and-capturing","lang":"ru"},{"level":"normal","title":"learn.javascript.ru — Делегирование событий","url":"https://learn.javascript.ru/event-delegation","lang":"ru"},{"level":"advanced","title":"MDN — Event bubbling","url":"https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/Event_bubbling","lang":"en"}],"rule":"Событие идёт вниз (погружение), к цели и вверх (всплытие). Делегирование: один обработчик на предке ловит события детей."},{"id":"http-basics","title":"Запрос, ответ и коды статуса","sources":[{"level":"beginner","title":"MDN — Overview of HTTP","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Overview","lang":"en"},{"level":"normal","title":"MDN — HTTP response status codes","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status","lang":"en"},{"level":"advanced","title":"RFC 9110 — HTTP Semantics","url":"https://www.rfc-editor.org/rfc/rfc9110.html","lang":"en"}],"rule":"Запрос = метод + URL + заголовки + тело. Ответ — код: 2xx успех, 3xx перенаправление, 4xx ошибка клиента, 5xx ошибка сервера."},{"id":"rest-api","title":"REST-методы и идемпотентность","sources":[{"level":"beginner","title":"MDN — HTTP request methods","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods","lang":"en"},{"level":"normal","title":"MDN — Idempotent","url":"https://developer.mozilla.org/en-US/docs/Glossary/Idempotent","lang":"en"},{"level":"advanced","title":"RFC 9110 — Method Definitions","url":"https://www.rfc-editor.org/rfc/rfc9110.html#name-method-definitions","lang":"en"}],"rule":"Ресурс — URL, действие — метод. GET безопасен, PUT и DELETE идемпотентны, POST — нет: повтор POST может создать дубль."},{"id":"cors","title":"CORS и same-origin policy","sources":[{"level":"beginner","title":"learn.javascript.ru — Fetch: запросы на другие сайты","url":"https://learn.javascript.ru/fetch-crossorigin","lang":"ru"},{"level":"normal","title":"MDN — Cross-Origin Resource Sharing (CORS)","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS","lang":"en"},{"level":"advanced","title":"Fetch Standard — CORS protocol","url":"https://fetch.spec.whatwg.org/#http-cors-protocol","lang":"en"}],"rule":"CORS ослабляет защиту браузера: сервер разрешает чужой origin заголовками, браузер их проверяет. curl и сервер CORS не видят."},{"id":"http-caching","title":"HTTP-кэширование и ETag","sources":[{"level":"beginner","title":"web.dev — Prevent unnecessary network requests with the HTTP Cache","url":"https://web.dev/articles/http-cache","lang":"en"},{"level":"normal","title":"MDN — HTTP caching","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching","lang":"en"},{"level":"advanced","title":"RFC 9111 — HTTP Caching","url":"https://www.rfc-editor.org/rfc/rfc9111.html","lang":"en"}],"rule":"Cache-Control решает, можно ли брать из кэша и сколько. ETag позволяет переспросить сервер и получить 304 без тела."},{"id":"git-basics","title":"Коммиты, ветки, remote","sources":[{"level":"beginner","title":"Pro Git — О ветвлении в двух словах","url":"https://git-scm.com/book/ru/v2/%D0%92%D0%B5%D1%82%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B2-Git-%D0%9E-%D0%B2%D0%B5%D1%82%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B8-%D0%B2-%D0%B4%D0%B2%D1%83%D1%85-%D1%81%D0%BB%D0%BE%D0%B2%D0%B0%D1%85","lang":"ru"},{"level":"normal","title":"git-pull documentation","url":"https://git-scm.com/docs/git-pull","lang":"en"},{"level":"advanced","title":"Pro Git — Git Objects","url":"https://git-scm.com/book/en/v2/Git-Internals-Git-Objects","lang":"en"}],"rule":"Коммит — снимок проекта, ветка — подвижный указатель на коммит. push отправляет коммиты в remote, pull = fetch + слияние."},{"id":"git-history","title":"Работа с историей Git","sources":[{"level":"beginner","title":"Pro Git — Перебазирование","url":"https://git-scm.com/book/ru/v2/%D0%92%D0%B5%D1%82%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B2-Git-%D0%9F%D0%B5%D1%80%D0%B5%D0%B1%D0%B0%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5","lang":"ru"},{"level":"normal","title":"git-revert documentation","url":"https://git-scm.com/docs/git-revert","lang":"en"},{"level":"advanced","title":"git-bisect documentation","url":"https://git-scm.com/docs/git-bisect","lang":"en"}],"rule":"merge сохраняет историю, rebase переписывает. Опубликованное не переписывают: в общей ветке откатывают через revert."},{"id":"devtools-debugging","title":"Отладка в DevTools","sources":[{"level":"beginner","title":"Chrome DevTools — Debug JavaScript","url":"https://developer.chrome.com/docs/devtools/javascript","lang":"en"},{"level":"normal","title":"Chrome DevTools — Inspect network activity","url":"https://developer.chrome.com/docs/devtools/network","lang":"en"},{"level":"advanced","title":"Chrome DevTools — Breakpoints reference","url":"https://developer.chrome.com/docs/devtools/javascript/breakpoints","lang":"en"}],"rule":"Сначала воспроизведи, потом наблюдай: точка останова вместо console.log, Network — что ушло и что пришло."},{"id":"lint-format","title":"Линтер, форматтер, хуки","sources":[{"level":"beginner","title":"Prettier — Prettier vs. Linters","url":"https://prettier.io/docs/comparison","lang":"en"},{"level":"normal","title":"ESLint — Getting Started","url":"https://eslint.org/docs/latest/use/getting-started","lang":"en"},{"level":"advanced","title":"lint-staged — README","url":"https://github.com/lint-staged/lint-staged","lang":"en"}],"rule":"Форматтер решает, как код выглядит, линтер — нет ли в нём ошибок. Оба запускает машина (хук, CI), а не человек на ревью."},{"id":"ask-for-help","title":"Как просить помощь и сообщать о проблемах","sources":[{"level":"beginner","title":"Stack Overflow — How do I ask a good question?","url":"https://stackoverflow.com/help/how-to-ask","lang":"en"},{"level":"normal","title":"Stack Overflow — How to create a Minimal, Reproducible Example","url":"https://stackoverflow.com/help/minimal-reproducible-example","lang":"en"},{"level":"advanced","title":"Simon Tatham — How to Report Bugs Effectively","url":"https://www.chiark.greenend.org.uk/~sgtatham/bugs.html","lang":"en"}],"rule":"Застрял дольше таймбокса — спроси. В вопросе: что делал, что ждал, что получил, что уже пробовал."},{"id":"code-review","title":"Код-ревью","sources":[{"level":"beginner","title":"Google eng-practices — How to write code review comments","url":"https://google.github.io/eng-practices/review/reviewer/comments.html","lang":"en"},{"level":"normal","title":"Google eng-practices — The Standard of Code Review","url":"https://google.github.io/eng-practices/review/reviewer/standard.html","lang":"en"},{"level":"advanced","title":"Google eng-practices — Small CLs","url":"https://google.github.io/eng-practices/review/developer/small-cls.html","lang":"en"}],"rule":"Ревью улучшает код, а не судит автора. Замечание = что не так, почему, как лучше. Своё ревью — повод учиться, а не защищаться."},{"id":"css-layout-modern","title":"Flexbox, Grid и container queries","sources":[{"level":"beginner","title":"web.dev Learn CSS — Flexbox","url":"https://web.dev/learn/css/flexbox","lang":"en"},{"level":"normal","title":"web.dev Learn CSS — Grid","url":"https://web.dev/learn/css/grid","lang":"en"},{"level":"advanced","title":"MDN — CSS container queries","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries","lang":"en"}],"rule":"Flexbox — раскладка по одной оси, Grid — по двум. Компонент подстраивай под контейнер (container queries), страницу — под viewport."},{"id":"responsive-design","title":"Адаптивность и mobile-first","sources":[{"level":"beginner","title":"web.dev Learn Responsive Design — Media queries","url":"https://web.dev/learn/design/media-queries","lang":"en"},{"level":"normal","title":"MDN — Viewport meta tag","url":"https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Viewport_meta_element","lang":"en"},{"level":"advanced","title":"MDN — Using media queries","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries","lang":"en"}],"rule":"Mobile-first: базовые стили для узкого экрана, расширения через min-width. Без meta viewport мобильный браузер покажет десктоп."},{"id":"scss-vs-custom-properties","title":"SCSS и CSS-переменные","sources":[{"level":"beginner","title":"Sass Basics","url":"https://sass-lang.com/guide/","lang":"en"},{"level":"normal","title":"Sass — @mixin and @include","url":"https://sass-lang.com/documentation/at-rules/mixin/","lang":"en"},{"level":"advanced","title":"MDN — Using CSS custom properties","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascading_variables/Using_CSS_custom_properties","lang":"en"}],"rule":"SCSS-переменные и миксины живут только при сборке. CSS custom properties живут в браузере: их можно менять в рантайме."},{"id":"aria-basics","title":"Основы доступности и ARIA","sources":[{"level":"beginner","title":"web.dev Learn Accessibility — ARIA and HTML","url":"https://web.dev/learn/accessibility/aria-html","lang":"en"},{"level":"normal","title":"W3C WAI — Read Me First (APG)","url":"https://www.w3.org/WAI/ARIA/apg/practices/read-me-first/","lang":"en"},{"level":"advanced","title":"W3C — Understanding SC 1.4.3 Contrast (Minimum)","url":"https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html","lang":"en"}],"rule":"Сначала нативный элемент, ARIA — только когда его нет. Неправильная ARIA хуже, чем никакой."},{"id":"focus-management","title":"Управление фокусом","sources":[{"level":"beginner","title":"web.dev Learn Accessibility — Keyboard focus","url":"https://web.dev/learn/accessibility/focus","lang":"en"},{"level":"normal","title":"MDN — <dialog>","url":"https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog","lang":"en"},{"level":"advanced","title":"W3C APG — Dialog (Modal) Pattern","url":"https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/","lang":"en"}],"rule":"Модалка держит фокус внутри и при закрытии возвращает его на кнопку, которая её открыла. Фокус всегда видно."},{"id":"ui-states","title":"Состояния интерфейса","sources":[{"level":"beginner","title":"NN/g — Visibility of System Status","url":"https://www.nngroup.com/articles/visibility-system-status/","lang":"en"},{"level":"normal","title":"NN/g — Error-Message Guidelines","url":"https://www.nngroup.com/articles/error-message-guidelines/","lang":"en"},{"level":"advanced","title":"NN/g — Designing Empty States in Complex Applications","url":"https://www.nngroup.com/articles/empty-state-interface-design/","lang":"en"}],"rule":"У экрана с данными минимум четыре состояния: загрузка, данные, пусто, ошибка. Интерфейс не молчит."},{"id":"css-theming","title":"Темы оформления","sources":[{"level":"beginner","title":"web.dev — prefers-color-scheme: Hello darkness, my old friend","url":"https://web.dev/articles/prefers-color-scheme","lang":"en"},{"level":"normal","title":"MDN — prefers-color-scheme","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme","lang":"en"},{"level":"advanced","title":"MDN — light-dark()","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark","lang":"en"}],"rule":"Тема = набор CSS-переменных. System берётся из prefers-color-scheme, выбор пользователя сохраняют и применяют без перезагрузки."},{"id":"ts-types","title":"Базовые типы TypeScript","sources":[{"level":"beginner","title":"TypeScript Handbook — Everyday Types","url":"https://www.typescriptlang.org/docs/handbook/2/everyday-types.html","lang":"en"},{"level":"normal","title":"TypeScript Handbook — More on Functions","url":"https://www.typescriptlang.org/docs/handbook/2/functions.html","lang":"en"},{"level":"advanced","title":"TypeScript Handbook — Generics","url":"https://www.typescriptlang.org/docs/handbook/2/generics.html","lang":"en"}],"rule":"Тип описывает форму значения. any выключает проверку, unknown заставляет проверить. Generic связывает тип входа и выхода."},{"id":"ng-component-model","title":"Компонент Angular","sources":[{"level":"beginner","title":"Angular — Components (essentials)","url":"https://v21.angular.dev/essentials/components","lang":"en"},{"level":"normal","title":"Angular — Components guide","url":"https://v21.angular.dev/guide/components","lang":"en"},{"level":"advanced","title":"Angular — Standalone migration","url":"https://v21.angular.dev/reference/migrations/standalone","lang":"en"}],"rule":"Компонент = класс + шаблон + селектор. Он standalone: зависимости шаблона перечисляют в imports самого компонента."},{"id":"ng-inputs-outputs","title":"Входы, выходы и model","sources":[{"level":"beginner","title":"Angular — Accepting data with input properties","url":"https://v21.angular.dev/guide/components/inputs","lang":"en"},{"level":"normal","title":"Angular — Custom events with outputs","url":"https://v21.angular.dev/guide/components/outputs","lang":"en"},{"level":"advanced","title":"Angular — Two-way binding","url":"https://v21.angular.dev/guide/templates/two-way-binding","lang":"en"}],"rule":"input() — данные сверху, output() — события вверх, model() — двусторонняя связь [(x)]. input-сигнал внутри компонента только читают."},{"id":"ng-lifecycle","title":"Жизненный цикл компонента","sources":[{"level":"beginner","title":"Angular — Component lifecycle","url":"https://v21.angular.dev/guide/components/lifecycle","lang":"en"},{"level":"normal","title":"Angular — DestroyRef API","url":"https://v21.angular.dev/api/core/DestroyRef","lang":"en"},{"level":"advanced","title":"Angular — afterNextRender API","url":"https://v21.angular.dev/api/core/afterNextRender","lang":"en"}],"rule":"constructor — внедрение, ngOnInit — входы уже есть, ngOnChanges — входы сменились, ngOnDestroy — уборка, afterNextRender — DOM готов."},{"id":"ng-control-flow","title":"Встроенный control flow","sources":[{"level":"beginner","title":"Angular — Control flow","url":"https://v21.angular.dev/guide/templates/control-flow","lang":"en"},{"level":"normal","title":"Angular — NgIf API (deprecated)","url":"https://v21.angular.dev/api/common/NgIf","lang":"en"},{"level":"advanced","title":"Angular — Control flow migration","url":"https://v21.angular.dev/reference/migrations/control-flow","lang":"en"}],"rule":"@if, @for, @switch встроены в шаблон. @for требует track: по ключу Angular понимает, какой элемент тот же, и не пересоздаёт DOM."},{"id":"ng-injection","title":"inject() и провайдеры","sources":[{"level":"beginner","title":"Angular — Dependency injection (essentials)","url":"https://v21.angular.dev/essentials/dependency-injection","lang":"en"},{"level":"normal","title":"Angular — Injection context","url":"https://v21.angular.dev/guide/di/dependency-injection-context","lang":"en"},{"level":"advanced","title":"Angular — Hierarchical injectors","url":"https://v21.angular.dev/guide/di/hierarchical-dependency-injection","lang":"en"}],"rule":"inject() работает только в контексте внедрения. providedIn: 'root' — один экземпляр на приложение, providers компонента — свой на каждый экземпляр."},{"id":"ng-providers","title":"InjectionToken и виды провайдеров","sources":[{"level":"beginner","title":"Angular — Defining dependency providers","url":"https://v21.angular.dev/guide/di/defining-dependency-providers","lang":"en"},{"level":"normal","title":"Angular — InjectionToken API","url":"https://v21.angular.dev/api/core/InjectionToken","lang":"en"},{"level":"advanced","title":"Angular — Resolution modifiers (hierarchical DI)","url":"https://v21.angular.dev/guide/di/hierarchical-dependency-injection","lang":"en"}],"rule":"Не класс — внедряй через InjectionToken. useClass, useValue, useFactory, useExisting решают, что получит потребитель."},{"id":"ng-reactive-forms","title":"Reactive Forms","sources":[{"level":"beginner","title":"Angular — Reactive forms","url":"https://v21.angular.dev/guide/forms/reactive-forms","lang":"en"},{"level":"normal","title":"Angular — Typed forms","url":"https://v21.angular.dev/guide/forms/typed-forms","lang":"en"},{"level":"advanced","title":"Angular — Validating form input","url":"https://v21.angular.dev/guide/forms/form-validation","lang":"en"}],"rule":"Модель формы живёт в классе: FormControl хранит значение, статус и валидаторы. Тип выводится из начального значения."},{"id":"ng-custom-controls","title":"Кастомные контролы и Signal Forms","sources":[{"level":"beginner","title":"Angular — ControlValueAccessor API","url":"https://v21.angular.dev/api/forms/ControlValueAccessor","lang":"en"},{"level":"normal","title":"Angular — Signal Forms overview","url":"https://v21.angular.dev/guide/forms/signals/overview","lang":"en"},{"level":"advanced","title":"Angular — Signal Forms validation","url":"https://v21.angular.dev/guide/forms/signals/validation","lang":"en"}],"rule":"Свой контрол подключают к формам через ControlValueAccessor. Signal Forms в v21 — experimental: в прод только осознанно."},{"id":"ng-routing","title":"Роутинг Angular","sources":[{"level":"beginner","title":"Angular — Define routes","url":"https://v21.angular.dev/guide/routing/define-routes","lang":"en"},{"level":"normal","title":"Angular — Route guards","url":"https://v21.angular.dev/guide/routing/route-guards","lang":"en"},{"level":"advanced","title":"Angular — Data resolvers","url":"https://v21.angular.dev/guide/routing/data-resolvers","lang":"en"}],"rule":"Маршрут = путь → компонент. loadComponent/loadChildren грузят код лениво, guard решает «пускать ли», resolver — «какие данные нужны»."},{"id":"ng-http-client","title":"HttpClient и интерсепторы","sources":[{"level":"beginner","title":"Angular — HTTP client overview","url":"https://v21.angular.dev/guide/http","lang":"en"},{"level":"normal","title":"Angular — Interceptors","url":"https://v21.angular.dev/guide/http/interceptors","lang":"en"},{"level":"advanced","title":"Angular — Test requests (HttpTestingController)","url":"https://v21.angular.dev/guide/http/testing","lang":"en"}],"rule":"HttpClient возвращает холодный Observable: без subscribe запроса нет. Общая логика запросов (токен, ошибки, ретраи) — в функциональных интерсепторах."},{"id":"xss-csrf","title":"XSS, CSRF и куки","sources":[{"level":"beginner","title":"MDN — Cross-site scripting (XSS)","url":"https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/XSS","lang":"en"},{"level":"normal","title":"OWASP — CSRF Prevention Cheat Sheet","url":"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html","lang":"en"},{"level":"advanced","title":"MDN — Set-Cookie (SameSite)","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie","lang":"en"}],"rule":"XSS — чужой скрипт в твоём origin. CSRF — браузер шлёт твои куки на запрос с чужого сайта. SameSite и CSRF-токен закрывают CSRF."},{"id":"dev-process-basics","title":"Спринт, борда и Definition of Done","sources":[{"level":"beginner","title":"Atlassian — What is the Definition of Done","url":"https://www.atlassian.com/agile/project-management/definition-of-done","lang":"en"},{"level":"normal","title":"The Scrum Guide","url":"https://scrumguides.org/scrum-guide.html","lang":"en"},{"level":"advanced","title":"Martin Fowler — Purpose of Estimation","url":"https://martinfowler.com/bliki/PurposeOfEstimation.html","lang":"en"}],"rule":"Definition of Done — общий для команды чек-лист «готово»: код, тесты, ревью, выкатка. Нет DoD — нет «готово»."},{"id":"unit-tests","title":"Юнит-тесты","sources":[{"level":"beginner","title":"Vitest — Getting Started","url":"https://vitest.dev/guide/","lang":"en"},{"level":"normal","title":"Angular — Testing overview","url":"https://v21.angular.dev/guide/testing","lang":"en"},{"level":"advanced","title":"Martin Fowler — Unit Test","url":"https://martinfowler.com/bliki/UnitTest.html","lang":"en"}],"rule":"Юнит-тест: подготовил, сделал, проверил. Один сценарий, без сети. Тестируй поведение, а не детали реализации."},{"id":"bundlers","title":"Сборщики и dev-сервер","sources":[{"level":"beginner","title":"Vite — Why Vite","url":"https://vite.dev/guide/why","lang":"en"},{"level":"normal","title":"Angular — Build system migration (application builder)","url":"https://v21.angular.dev/tools/cli/build-system-migration","lang":"en"},{"level":"advanced","title":"esbuild — API","url":"https://esbuild.github.io/api/","lang":"en"}],"rule":"Сборщик превращает граф модулей в файлы для браузера. Dev — быстро и с source maps, prod — минифицировано и разбито на чанки."},{"id":"ng-signal-basics","title":"Сигналы","sources":[{"level":"beginner","title":"Angular — Signals (essentials)","url":"https://v21.angular.dev/essentials/signals","lang":"en"},{"level":"normal","title":"Angular — Signals guide","url":"https://v21.angular.dev/guide/signals","lang":"en"},{"level":"advanced","title":"Angular — Effects","url":"https://v21.angular.dev/guide/signals/effect","lang":"en"}],"rule":"signal хранит значение, computed выводит и кэширует, effect — только для побочных эффектов наружу. Производное — через computed, не через effect."},{"id":"ng-async-signals","title":"linkedSignal, resource и RxJS-интероп","sources":[{"level":"beginner","title":"Angular — linkedSignal","url":"https://v21.angular.dev/guide/signals/linked-signal","lang":"en"},{"level":"normal","title":"Angular — Async reactivity with resources","url":"https://v21.angular.dev/guide/signals/resource","lang":"en"},{"level":"advanced","title":"Angular — RxJS interop with signals","url":"https://v21.angular.dev/ecosystem/rxjs-interop","lang":"en"}],"rule":"linkedSignal — записываемое значение со сбросом от источника. resource — асинхронные данные в сигналах (experimental в v21). toSignal/toObservable — мост с RxJS."},{"id":"state-management","title":"Управление состоянием","sources":[{"level":"beginner","title":"Angular — Signals guide","url":"https://v21.angular.dev/guide/signals","lang":"en"},{"level":"normal","title":"NgRx — SignalStore","url":"https://ngrx.io/guide/signals/signal-store","lang":"en"},{"level":"advanced","title":"NgRx — Store (Redux pattern)","url":"https://ngrx.io/guide/store","lang":"en"}],"rule":"Локальное — в компоненте, общее для фичи — в сервисе на сигналах, сложное — в SignalStore. Classic NgRx — для поддержки legacy."},{"id":"rxjs-subjects","title":"Subject и комбинирование потоков","sources":[{"level":"beginner","title":"RxJS — Subject","url":"https://rxjs.dev/guide/subject","lang":"en"},{"level":"normal","title":"RxJS — combineLatest","url":"https://rxjs.dev/api/index/function/combineLatest","lang":"en"},{"level":"advanced","title":"RxJS — Operators","url":"https://rxjs.dev/guide/operators","lang":"en"}],"rule":"Subject — и Observable, и Observer: раздаёт значения многим. BehaviorSubject помнит последнее. combineLatest ждёт хотя бы одно значение от каждого."},{"id":"ng-change-detection","title":"Change detection и zoneless","sources":[{"level":"beginner","title":"Angular — ChangeDetectionStrategy","url":"https://v21.angular.dev/api/core/ChangeDetectionStrategy","lang":"en"},{"level":"normal","title":"Angular — Zoneless","url":"https://v21.angular.dev/guide/zoneless","lang":"en"},{"level":"advanced","title":"Angular — Skipping component subtrees","url":"https://v21.angular.dev/best-practices/skipping-subtrees","lang":"en"}],"rule":"В v21 новые приложения zoneless: проверка запускается от сигналов, событий шаблона и markForCheck. OnPush пропускает поддерево, пока нет причины."},{"id":"ng-defer","title":"Отложенная загрузка @defer","sources":[{"level":"beginner","title":"Angular — Deferred loading with @defer","url":"https://v21.angular.dev/guide/templates/defer","lang":"en"},{"level":"normal","title":"Angular — @defer API","url":"https://v21.angular.dev/api/core/@defer","lang":"en"},{"level":"advanced","title":"Angular — Incremental hydration","url":"https://v21.angular.dev/guide/incremental-hydration","lang":"en"}],"rule":"@defer выносит standalone-зависимости блока в отдельный чанк и грузит по триггеру. Блоки @placeholder, @loading, @error закрывают состояния."},{"id":"virtual-scroll","title":"Виртуальный скролл","sources":[{"level":"beginner","title":"Angular CDK — Scrolling","url":"https://material.angular.dev/cdk/scrolling/overview","lang":"en"},{"level":"normal","title":"web.dev — How large DOM sizes affect interactivity","url":"https://web.dev/articles/dom-size-and-interactivity","lang":"en"},{"level":"advanced","title":"web.dev — Virtualize large lists","url":"https://web.dev/articles/virtualize-long-lists-react-window","lang":"en"}],"rule":"Виртуальный скролл рисует только видимые строки: DOM остаётся маленьким, сколько бы ни было данных."},{"id":"ng-hydration","title":"SSR, SSG и гидратация","sources":[{"level":"beginner","title":"Angular — Server and hybrid rendering","url":"https://v21.angular.dev/guide/ssr","lang":"en"},{"level":"normal","title":"Angular — Hydration","url":"https://v21.angular.dev/guide/hydration","lang":"en"},{"level":"advanced","title":"Angular — Incremental hydration","url":"https://v21.angular.dev/guide/incremental-hydration","lang":"en"}],"rule":"SSR рисует HTML на сервере на каждый запрос, SSG — при сборке. Гидратация переиспользует этот DOM вместо перерисовки; event replay не теряет ранние клики."},{"id":"core-web-vitals","title":"Core Web Vitals","sources":[{"level":"beginner","title":"web.dev — Web Vitals","url":"https://web.dev/articles/vitals","lang":"en"},{"level":"normal","title":"Chrome for Developers — Lighthouse overview","url":"https://developer.chrome.com/docs/lighthouse/overview","lang":"en"},{"level":"advanced","title":"web.dev — Time to First Byte (TTFB)","url":"https://web.dev/articles/ttfb","lang":"en"}],"rule":"LCP ≤ 2,5 с, INP ≤ 200 мс, CLS ≤ 0,1 на 75-м перцентиле. Lighthouse — лаборатория, CrUX и RUM — реальные пользователи."},{"id":"bundle-optimization","title":"Оптимизация бандла","sources":[{"level":"beginner","title":"web.dev — Reduce JavaScript payloads with code splitting","url":"https://web.dev/articles/reduce-javascript-payloads-with-code-splitting","lang":"en"},{"level":"normal","title":"MDN — Tree shaking","url":"https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking","lang":"en"},{"level":"advanced","title":"web.dev — Extract critical CSS","url":"https://web.dev/articles/extract-critical-css","lang":"en"}],"rule":"Меньше JS на старте: делить по маршрутам и @defer, выкидывать мёртвый код (tree shaking), смотреть состав бандла перед тем, как оптимизировать."},{"id":"design-principles","title":"SOLID и паттерны на фронтенде","sources":[{"level":"beginner","title":"Refactoring.Guru — Паттерны проектирования","url":"https://refactoring.guru/ru/design-patterns/catalog","lang":"ru"},{"level":"normal","title":"Robert C. Martin — The Single Responsibility Principle","url":"https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html","lang":"en"},{"level":"advanced","title":"Martin Fowler — GUI Architectures","url":"https://martinfowler.com/eaaDev/uiArchs.html","lang":"en"}],"rule":"У модуля одна причина меняться. Компонент показывает, сервис считает, стор хранит. Паттерн берут под проблему, а не ради паттерна."},{"id":"app-structure","title":"Слои и feature-based структура","sources":[{"level":"beginner","title":"Angular — Style guide","url":"https://v21.angular.dev/style-guide","lang":"en"},{"level":"normal","title":"Martin Fowler — Presentation Domain Data Layering","url":"https://martinfowler.com/bliki/PresentationDomainDataLayering.html","lang":"en"},{"level":"advanced","title":"Feature-Sliced Design — Overview","url":"https://feature-sliced.design/docs/get-started/overview","lang":"en"}],"rule":"Папки — по фичам, а не по типам файлов. Зависимости идут в одну сторону: UI → логика → данные."},{"id":"atomic-design","title":"Atomic Design","sources":[{"level":"beginner","title":"Brad Frost — Atomic Design, Chapter 2","url":"https://atomicdesign.bradfrost.com/chapter-2/","lang":"en"},{"level":"normal","title":"Storybook — Why Storybook","url":"https://storybook.js.org/docs/get-started/why-storybook","lang":"en"},{"level":"advanced","title":"Storybook — Autodocs","url":"https://storybook.js.org/docs/writing-docs/autodocs","lang":"en"}],"rule":"atoms → molecules → organisms → templates → pages. В каталог выносят примитив, который уже повторился, а не «на всякий случай»."},{"id":"test-doubles","title":"Тестовые дублёры и уровни тестов","sources":[{"level":"beginner","title":"Vitest — Mocking","url":"https://vitest.dev/guide/mocking","lang":"en"},{"level":"normal","title":"Martin Fowler — Mocks Aren't Stubs","url":"https://martinfowler.com/articles/mocksArentStubs.html","lang":"en"},{"level":"advanced","title":"Playwright — Best Practices","url":"https://playwright.dev/docs/best-practices","lang":"en"}],"rule":"Stub отдаёт заготовленный ответ, spy записывает вызовы, mock проверяет ожидаемые вызовы. Интеграционный тест проверяет стык, E2E — сценарий пользователя."},{"id":"static-analysis","title":"Статический анализ и покрытие","sources":[{"level":"beginner","title":"ESLint — Custom Rules","url":"https://eslint.org/docs/latest/extend/custom-rules","lang":"en"},{"level":"normal","title":"typescript-eslint — Custom Rules","url":"https://typescript-eslint.io/developers/custom-rules","lang":"en"},{"level":"advanced","title":"Martin Fowler — Test Coverage","url":"https://martinfowler.com/bliki/TestCoverage.html","lang":"en"}],"rule":"Линтер работает с AST: правило — посетитель узлов дерева. Покрытие показывает, какой код выполнился, а не что он проверен."},{"id":"frontend-ci","title":"CI/CD фронтенда","sources":[{"level":"beginner","title":"GitLab — Review apps","url":"https://docs.gitlab.com/ci/review_apps/","lang":"en"},{"level":"normal","title":"Docker — Multi-stage builds","url":"https://docs.docker.com/build/building/multi-stage/","lang":"en"},{"level":"advanced","title":"nginx — try_files","url":"https://nginx.org/en/docs/http/ngx_http_core_module.html#try_files","lang":"en"}],"rule":"Каждый PR проходит одинаковый путь: линт, типы, тесты, сборка, preview. Ручная проверка — исключение, а не шаг пайплайна."},{"id":"dependency-updates","title":"Обновление зависимостей","sources":[{"level":"beginner","title":"GitHub Docs — About Dependabot version updates","url":"https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/about-dependabot-version-updates","lang":"en"},{"level":"normal","title":"Renovate — Automerge configuration","url":"https://docs.renovatebot.com/key-concepts/automerge/","lang":"en"},{"level":"advanced","title":"libyear","url":"https://libyear.com/","lang":"en"}],"rule":"Обновляют часто и мелко: бот открывает PR, CI проверяет, группы и automerge снимают рутину. libyear показывает, насколько отстали."},{"id":"graphql-client","title":"GraphQL на клиенте","sources":[{"level":"beginner","title":"GraphQL — Queries","url":"https://graphql.org/learn/queries/","lang":"en"},{"level":"normal","title":"GraphQL — Mutations","url":"https://graphql.org/learn/mutations/","lang":"en"},{"level":"advanced","title":"GraphQL — Caching","url":"https://graphql.org/learn/caching/","lang":"en"}],"rule":"Клиент сам выбирает поля, запросы идут в один endpoint. Кэш нормализуют по типу и id объекта, а не по URL."},{"id":"realtime","title":"WebSocket и SSE","sources":[{"level":"beginner","title":"learn.javascript.ru — WebSocket","url":"https://learn.javascript.ru/websocket","lang":"ru"},{"level":"normal","title":"learn.javascript.ru — Server Sent Events","url":"https://learn.javascript.ru/server-sent-events","lang":"ru"},{"level":"advanced","title":"MDN — Using server-sent events","url":"https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events","lang":"en"}],"rule":"SSE — односторонний поток сервер → клиент поверх HTTP с автопереподключением. WebSocket — двусторонний канал, переподключение пишешь сам."},{"id":"i18n-l10n","title":"Интернационализация и время","sources":[{"level":"beginner","title":"Angular — Internationalization","url":"https://v21.angular.dev/guide/i18n","lang":"en"},{"level":"normal","title":"MDN — Intl.DateTimeFormat","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat","lang":"en"},{"level":"advanced","title":"MDN — Temporal","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal","lang":"en"}],"rule":"Храни время в UTC, показывай в зоне пользователя. Даты, числа и валюты форматирует Intl по локали, а не ручная склейка строк."},{"id":"estimation","title":"Оценка и декомпозиция","sources":[{"level":"beginner","title":"Atlassian — Story points and estimation","url":"https://www.atlassian.com/agile/project-management/estimation","lang":"en"},{"level":"normal","title":"Martin Fowler — Purpose of Estimation","url":"https://martinfowler.com/bliki/PurposeOfEstimation.html","lang":"en"},{"level":"advanced","title":"Mountain Goat Software — What Are Story Points?","url":"https://www.mountaingoatsoftware.com/blog/what-are-story-points","lang":"en"}],"rule":"Оценка — диапазон с допущениями, а не обещание. Режь задачу, пока каждый кусок не станет понятным, проверяемым и коротким."},{"id":"continuous-improvement","title":"Стандарты, ретро и пилоты","sources":[{"level":"beginner","title":"Atlassian Team Playbook — Retrospective","url":"https://www.atlassian.com/team-playbook/plays/retrospective","lang":"en"},{"level":"normal","title":"Thoughtworks — Technology Radar FAQ","url":"https://www.thoughtworks.com/radar/faq","lang":"en"},{"level":"advanced","title":"Google SRE — Postmortem Culture","url":"https://sre.google/sre-book/postmortem-culture/","lang":"en"}],"rule":"Улучшение = гипотеза + метрика до/после + владелец. Договорённость без автоматики или чек-листа забывается за спринт."},{"id":"ui-frameworks","title":"Модели реактивности фреймворков","sources":[{"level":"beginner","title":"React — State: A Component's Memory","url":"https://react.dev/learn/state-a-components-memory","lang":"en"},{"level":"normal","title":"Vue — Reactivity Fundamentals","url":"https://vuejs.org/guide/essentials/reactivity-fundamentals.html","lang":"en"},{"level":"advanced","title":"Svelte — $state","url":"https://svelte.dev/docs/svelte/$state","lang":"en"}],"rule":"Везде компоненты + состояние + шаблон. Разница — как ловят изменения: React перерендерит компонент, Vue и Svelte отслеживают зависимости."},{"id":"webassembly","title":"WebAssembly","sources":[{"level":"beginner","title":"MDN — WebAssembly concepts","url":"https://developer.mozilla.org/en-US/docs/WebAssembly/Guides/Concepts","lang":"en"},{"level":"normal","title":"webassembly.org","url":"https://webassembly.org/","lang":"en"},{"level":"advanced","title":"web.dev — WebAssembly","url":"https://web.dev/explore/webassembly","lang":"en"}],"rule":"Wasm — бинарный формат для тяжёлых вычислений рядом с JS. Он не трогает DOM сам и не делает приложение быстрым автоматически."},{"id":"test-pyramid","title":"Тестовая стратегия","sources":[{"level":"beginner","title":"Martin Fowler (Ham Vocke) — The Practical Test Pyramid","url":"https://martinfowler.com/articles/practical-test-pyramid.html","lang":"en"},{"level":"normal","title":"Playwright — Visual comparisons","url":"https://playwright.dev/docs/test-snapshots","lang":"en"},{"level":"advanced","title":"Stryker Mutator — Introduction","url":"https://stryker-mutator.io/docs/","lang":"en"}],"rule":"Много быстрых юнит- и интеграционных тестов, мало E2E. Флаки-тест хуже отсутствующего: ему перестают верить."},{"id":"bounded-contexts","title":"Границы модулей и DDD во фронтенде","sources":[{"level":"beginner","title":"Martin Fowler — Bounded Context","url":"https://martinfowler.com/bliki/BoundedContext.html","lang":"en"},{"level":"normal","title":"Nx — Enforce module boundaries","url":"https://nx.dev/docs/features/enforce-module-boundaries","lang":"en"},{"level":"advanced","title":"Martin Fowler — Conway's Law","url":"https://martinfowler.com/bliki/ConwaysLaw.html","lang":"en"}],"rule":"Граница модуля = граница бизнес-контекста. Модули общаются через публичный API, а правила зависимостей проверяет инструмент, не память."},{"id":"micro-frontends","title":"Микрофронтенды и монорепозиторий","sources":[{"level":"beginner","title":"Martin Fowler (Cam Jackson) — Micro Frontends","url":"https://martinfowler.com/articles/micro-frontends.html","lang":"en"},{"level":"normal","title":"Module Federation — Quick start","url":"https://module-federation.io/guide/start/","lang":"en"},{"level":"advanced","title":"Martin Fowler — Strangler Fig Application","url":"https://martinfowler.com/bliki/StranglerFigApplication.html","lang":"en"}],"rule":"Микрофронтенды решают проблему организации, а не кода: их берут, когда команды мешают друг другу релизить. Иначе хватит монорепо с границами."},{"id":"pwa-service-worker","title":"PWA и service worker","sources":[{"level":"beginner","title":"web.dev Learn PWA — Service workers","url":"https://web.dev/learn/pwa/service-workers","lang":"en"},{"level":"normal","title":"Angular — Service workers & PWAs","url":"https://v21.angular.dev/ecosystem/service-workers","lang":"en"},{"level":"advanced","title":"MDN — Push API","url":"https://developer.mozilla.org/en-US/docs/Web/API/Push_API","lang":"en"}],"rule":"Service worker — прокси между приложением и сетью: кэш, офлайн, push. Ошибка в его кэше живёт у пользователя, пока сам SW не обновится."},{"id":"backend-for-frontend","title":"Как устроен бэкенд и BFF","sources":[{"level":"beginner","title":"Express — Routing","url":"https://expressjs.com/en/guide/routing.html","lang":"en"},{"level":"normal","title":"Sam Newman — Backends For Frontends","url":"https://samnewman.io/patterns/architectural/bff/","lang":"en"},{"level":"advanced","title":"Martin Fowler — Presentation Domain Data Layering","url":"https://martinfowler.com/bliki/PresentationDomainDataLayering.html","lang":"en"}],"rule":"Бэкенд = маршруты → бизнес-логика → данные. BFF — тонкий сервер под один фронтенд: собирает данные, прячет секреты, не держит чужую бизнес-логику."},{"id":"profiling","title":"Профилирование и длинные задачи","sources":[{"level":"beginner","title":"Chrome DevTools — Performance panel reference","url":"https://developer.chrome.com/docs/devtools/performance/reference","lang":"en"},{"level":"normal","title":"web.dev — Optimize long tasks","url":"https://web.dev/articles/optimize-long-tasks","lang":"en"},{"level":"advanced","title":"Angular — DevTools","url":"https://v21.angular.dev/tools/devtools","lang":"en"}],"rule":"Сначала измерь, потом чини. Задача дольше 50 мс блокирует ввод: найди в профиле, кто её создал, и раздели или убери работу."},{"id":"source-maps","title":"Source maps и разбор прод-ошибок","sources":[{"level":"beginner","title":"web.dev — What are source maps?","url":"https://web.dev/articles/source-maps","lang":"en"},{"level":"normal","title":"Chrome DevTools — Debug your original code with source maps","url":"https://developer.chrome.com/docs/devtools/javascript/source-maps","lang":"en"},{"level":"advanced","title":"Sentry — Source Maps (JavaScript)","url":"https://docs.sentry.io/platforms/javascript/sourcemaps/","lang":"en"}],"rule":"Source map связывает минифицированный код с исходником. Публично их не раздают — загружают в систему мониторинга ошибок."},{"id":"type-level-ts","title":"Типы как программа","sources":[{"level":"beginner","title":"TypeScript Handbook — Mapped Types","url":"https://www.typescriptlang.org/docs/handbook/2/mapped-types.html","lang":"en"},{"level":"normal","title":"TypeScript Handbook — Conditional Types","url":"https://www.typescriptlang.org/docs/handbook/2/conditional-types.html","lang":"en"},{"level":"advanced","title":"TypeScript — Using the Compiler API","url":"https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API","lang":"en"}],"rule":"Conditional и mapped типы вычисляют новые типы из старых, infer достаёт часть типа. Сложный тип оправдан, только если упрощает вызовы."},{"id":"safe-releases","title":"Безопасные релизы","sources":[{"level":"beginner","title":"Martin Fowler (Pete Hodgson) — Feature Toggles","url":"https://martinfowler.com/articles/feature-toggles.html","lang":"en"},{"level":"normal","title":"Google SRE Workbook — Canarying Releases","url":"https://sre.google/workbook/canarying-releases/","lang":"en"},{"level":"advanced","title":"OWASP — Top 10 CI/CD Security Risks","url":"https://owasp.org/www-project-top-10-ci-cd-security-risks/","lang":"en"}],"rule":"Деплой ≠ релиз: код выкатывают выключенным за флагом, включают на малую долю (канарейка) и откатывают за минуты, а не часы."},{"id":"a11y-ownership","title":"Доступность как свойство продукта","sources":[{"level":"beginner","title":"Angular — Accessibility best practices","url":"https://v21.angular.dev/best-practices/a11y","lang":"en"},{"level":"normal","title":"axe-core (Deque)","url":"https://github.com/dequelabs/axe-core","lang":"en"},{"level":"advanced","title":"W3C — How to Meet WCAG (Quick Reference)","url":"https://www.w3.org/WAI/WCAG22/quickref/","lang":"en"}],"rule":"Автоматика (axe, Lighthouse) ловит примерно треть проблем. Остальное — клавиатура, скринридер и критерии WCAG в Definition of Done."},{"id":"experiments","title":"Продуктовые метрики и A/B-тесты","sources":[{"level":"beginner","title":"NN/g — A/B Testing 101","url":"https://www.nngroup.com/articles/ab-testing/","lang":"en"},{"level":"normal","title":"Evan Miller — Sample Size Calculator","url":"https://www.evanmiller.org/ab-testing/sample-size.html","lang":"en"},{"level":"advanced","title":"Evan Miller — How Not To Run an A/B Test","url":"https://www.evanmiller.org/how-not-to-run-an-ab-test.html","lang":"en"}],"rule":"Изменение UX доказывают экспериментом: гипотеза, одна главная метрика, контрольная группа, выборка посчитана заранее."},{"id":"oncall-postmortem","title":"Дежурства и постмортемы","sources":[{"level":"beginner","title":"Google SRE Book — Being On-Call","url":"https://sre.google/sre-book/being-on-call/","lang":"en"},{"level":"normal","title":"Google SRE Book — Postmortem Culture","url":"https://sre.google/sre-book/postmortem-culture/","lang":"en"},{"level":"advanced","title":"Google SRE Workbook — Postmortem Culture: Learning from Failure","url":"https://sre.google/workbook/postmortem-culture/","lang":"en"}],"rule":"На инциденте сначала восстанавливают сервис, потом ищут причину. Постмортем без поиска виноватых заканчивается действиями с владельцами."},{"id":"technical-influence","title":"Влияние без власти","sources":[{"level":"beginner","title":"NN/g — Inverted Pyramid","url":"https://www.nngroup.com/articles/inverted-pyramid/","lang":"en"},{"level":"normal","title":"Malte Ubl — Design Docs at Google","url":"https://www.industrialempathy.com/posts/design-docs-at-google/","lang":"en"},{"level":"advanced","title":"Will Larson — Staff archetypes","url":"https://staffeng.com/guides/staff-archetypes/","lang":"en"}],"rule":"Влияют письменно и данными: проблема, варианты, цена каждого, рекомендация. Сначала вывод, потом детали — особенно для нетехнической аудитории."},{"id":"edge-delivery","title":"CDN, edge и релиз статики","sources":[{"level":"beginner","title":"web.dev — Content delivery networks (CDNs)","url":"https://web.dev/articles/content-delivery-networks","lang":"en"},{"level":"normal","title":"Cloudflare — Cache","url":"https://developers.cloudflare.com/cache/","lang":"en"},{"level":"advanced","title":"RFC 9111 — HTTP Caching (shared caches)","url":"https://www.rfc-editor.org/rfc/rfc9111.html#name-cache-control","lang":"en"}],"rule":"Хэшированная статика — на CDN навсегда, HTML — коротко или с ревалидацией. Релиз = выложить новые файлы и переключить HTML, откат — переключить обратно."},{"id":"hybrid-apps","title":"Гибридные и нативные приложения","sources":[{"level":"beginner","title":"Capacitor — Docs","url":"https://capacitorjs.com/docs","lang":"en"},{"level":"normal","title":"React Native — Introduction","url":"https://reactnative.dev/docs/getting-started","lang":"en"},{"level":"advanced","title":"web.dev — Learn PWA","url":"https://web.dev/learn/pwa/","lang":"en"}],"rule":"Capacitor заворачивает веб-приложение в нативную оболочку, React Native рисует нативными компонентами. Выбор — по команде и нужному нативному опыту."},{"id":"custom-tooling","title":"Свой инструментарий","sources":[{"level":"beginner","title":"Angular — Authoring schematics","url":"https://v21.angular.dev/tools/cli/schematics-authoring","lang":"en"},{"level":"normal","title":"Angular — CLI builders","url":"https://v21.angular.dev/tools/cli/cli-builder","lang":"en"},{"level":"advanced","title":"ts-morph","url":"https://ts-morph.com/","lang":"en"}],"rule":"Повторяющееся решение превращают в инструмент: генератор, правило линтера, миграцию. Инструмент без владельца и тестов становится долгом."},{"id":"platform-thinking","title":"Что выносить в платформу","sources":[{"level":"beginner","title":"Team Topologies — Key concepts","url":"https://teamtopologies.com/key-concepts","lang":"en"},{"level":"normal","title":"Martin Fowler (Evan Bottcher) — What I Talk About When I Talk About Platforms","url":"https://martinfowler.com/articles/talk-about-platforms.html","lang":"en"},{"level":"advanced","title":"InnerSource Commons — Learn","url":"https://innersourcecommons.org/learn/","lang":"en"}],"rule":"В общее выносят то, что нужно нескольким командам и уже стабильно. Общий код без владельца хуже дубля."},{"id":"delegation-feedback","title":"Делегирование и обратная связь","sources":[{"level":"beginner","title":"CCL — SBI feedback model","url":"https://www.ccl.org/articles/leading-effectively-articles/closing-the-gap-between-intent-vs-impact-sbii/","lang":"en"},{"level":"normal","title":"Radical Candor — Our approach","url":"https://www.radicalcandor.com/our-approach/","lang":"en"},{"level":"advanced","title":"HBR — To Be a Great Leader, You Have to Learn How to Delegate Well","url":"https://hbr.org/2017/10/to-be-a-great-leader-you-have-to-learn-how-to-delegate-well","lang":"en"}],"rule":"Делегируют результат, а не шаги: цель, критерий готовности, рамки и точки сверки. Обратная связь: ситуация, поведение, влияние."},{"id":"interest-negotiation","title":"Переговоры со стейкхолдерами","sources":[{"level":"beginner","title":"PON Harvard — Principled Negotiation","url":"https://www.pon.harvard.edu/daily/negotiation-skills-daily/principled-negotiation-focus-interests-create-value/","lang":"en"},{"level":"normal","title":"PON Harvard — BATNA","url":"https://www.pon.harvard.edu/daily/batna/translate-your-batna-to-the-current-deal/","lang":"en"},{"level":"advanced","title":"Will Larson — Present to executives","url":"https://staffeng.com/guides/present-to-executives/","lang":"en"}],"rule":"Договариваются об интересах, а не о позициях: что каждой стороне на самом деле нужно. Знай свою лучшую альтернативу до разговора."},{"id":"org-change","title":"Изменения на уровне компании","sources":[{"level":"beginner","title":"Kotter — The 8 Steps for Leading Change","url":"https://www.kotterinc.com/methodology/8-steps/","lang":"en"},{"level":"normal","title":"DORA — Generative organizational culture","url":"https://dora.dev/capabilities/generative-organizational-culture/","lang":"en"},{"level":"advanced","title":"Open Source Guides — How to Contribute","url":"https://opensource.guide/how-to-contribute/","lang":"en"}],"rule":"Большое изменение идёт волнами: срочность, коалиция, пилот с видимой победой, закрепление в процессах. Культуру меняют поведением лидеров и метриками."},{"id":"cs-numbers","title":"Числа в C#: деление, округление, переполнение","sources":[{"level":"beginner","title":"Целочисленные типы (C#)","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/language-reference/builtin-types/integral-numeric-types","lang":"ru"},{"level":"normal","title":"Arithmetic operators (C# reference)","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/arithmetic-operators","lang":"en"},{"level":"advanced","title":"MidpointRounding enum","url":"https://learn.microsoft.com/en-us/dotnet/api/system.midpointrounding","lang":"en"}],"rule":"Целые делятся нацело, Round — к чётному, double — двоичный (0.1+0.2≠0.3), деньги — в decimal, переполнение молчит без checked."},{"id":"cs-value-reference-types","title":"Value types и reference types","sources":[{"level":"beginner","title":"Microsoft Learn — Типы значений","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/language-reference/builtin-types/value-types","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Built-in reference types","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/reference-types","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Boxing and Unboxing","url":"https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing","lang":"en"}],"rule":"Value type копирует данные, reference type копирует ссылку. Положил int в object — получил boxing и аллокацию в куче."},{"id":"cs-strings","title":"Строки в C#","sources":[{"level":"beginner","title":"Microsoft Learn — Строки","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/programming-guide/strings/","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Using the StringBuilder Class","url":"https://learn.microsoft.com/en-us/dotnet/standard/base-types/stringbuilder","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Best Practices for Comparing Strings","url":"https://learn.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings","lang":"en"}],"rule":"string неизменяем: каждая «правка» — новая строка. Много правок — StringBuilder. Сравнение ключей — Ordinal."},{"id":"cs-nullable","title":"Nullable reference и value types","sources":[{"level":"beginner","title":"Microsoft Learn — Ссылочные типы, допускающие значение NULL","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/fundamentals/null-safety/nullable-reference-types","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Nullable value types","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-value-types","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Attributes for nullable static analysis","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/attributes/nullable-analysis","lang":"en"}],"rule":"int? — настоящий Nullable<int>. string? — только подсказка компилятору, в runtime это тот же string."},{"id":"cs-method-params","title":"Передача параметров ref, out, in","sources":[{"level":"beginner","title":"Microsoft Learn — Параметры методов","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/language-reference/keywords/method-parameters","lang":"ru"},{"level":"normal","title":"Microsoft Learn — ref keyword","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ref","lang":"en"},{"level":"advanced","title":"Microsoft Learn — out keyword","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out","lang":"en"}],"rule":"ref — читаю и меняю, out — обязан записать, in — только читаю по ссылке."},{"id":"cs-oop-principles","title":"Принципы ООП в C#","sources":[{"level":"beginner","title":"Microsoft Learn — Объектно-ориентированное программирование","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/fundamentals/tutorials/oop","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Polymorphism","url":"https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/object-oriented/polymorphism","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Inheritance","url":"https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/object-oriented/inheritance","lang":"en"}],"rule":"АИНП: абстракция, инкапсуляция, наследование, полиморфизм. virtual в базе + override в наследнике = полиморфизм."},{"id":"cs-abstraction","title":"Интерфейсы, абстрактные классы, модификаторы доступа","sources":[{"level":"beginner","title":"Microsoft Learn — Интерфейсы","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/fundamentals/types/interfaces","lang":"ru"},{"level":"normal","title":"Microsoft Learn — abstract","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/abstract","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Access Modifiers","url":"https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers","lang":"en"}],"rule":"Интерфейс — «что умеет» (много), абстрактный класс — «кем является» (один). Закрывай всё, что можно: private по умолчанию."},{"id":"cs-records","title":"Records","sources":[{"level":"beginner","title":"Microsoft Learn — Типы записей","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/fundamentals/types/records","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Records (C# reference)","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/record","lang":"en"},{"level":"advanced","title":"Microsoft Learn — The with expression","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/with-expression","lang":"en"}],"rule":"record — для данных: равенство по значению и with для копии. class — равенство по ссылке."},{"id":"cs-csharp14","title":"Новое в C# 14","sources":[{"level":"beginner","title":"Microsoft Learn — What's new in C# 14","url":"https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14","lang":"en"},{"level":"normal","title":"Microsoft Learn — field keyword","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/field","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Extension declaration","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/extension","lang":"en"}],"rule":"C# 14 = field в свойствах + extension-блоки (свойства-расширения) + ?. слева от присваивания."},{"id":"dotnet-runtime","title":"Как устроен .NET runtime","sources":[{"level":"beginner","title":"Microsoft Learn — Процесс управляемого выполнения","url":"https://learn.microsoft.com/ru-ru/dotnet/standard/managed-execution-process","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Introduction to .NET","url":"https://learn.microsoft.com/en-us/dotnet/core/introduction","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Fundamentals of garbage collection","url":"https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals","lang":"en"}],"rule":"C# → CIL (dll) → JIT → машинный код. Мусор убирает GC: поколения 0, 1, 2, молодые собираются чаще."},{"id":"dotnet-sdk-projects","title":".NET SDK, CLI и проекты","sources":[{"level":"beginner","title":"Microsoft Learn — .NET CLI overview","url":"https://learn.microsoft.com/en-us/dotnet/core/tools/","lang":"en"},{"level":"normal","title":"Microsoft Learn — Target frameworks in SDK-style projects","url":"https://learn.microsoft.com/en-us/dotnet/standard/frameworks","lang":"en"},{"level":"advanced","title":"Microsoft Learn — global.json overview","url":"https://learn.microsoft.com/en-us/dotnet/core/tools/global-json","lang":"en"}],"rule":"SDK — чтобы собирать, runtime — чтобы запускать. TargetFramework в csproj, версия SDK — в global.json."},{"id":"dotnet-nuget","title":"NuGet-пакеты","sources":[{"level":"beginner","title":"Microsoft Learn — Что такое NuGet","url":"https://learn.microsoft.com/ru-ru/nuget/what-is-nuget","lang":"ru"},{"level":"normal","title":"Microsoft Learn — PackageReference in project files","url":"https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Central Package Management","url":"https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management","lang":"en"}],"rule":"PackageReference — в проекте, версии для всего решения — в Directory.Packages.props."},{"id":"dotnet-releases","title":"Релизы .NET и новое в .NET 10","sources":[{"level":"beginner","title":"Microsoft Learn — .NET releases, patches, and support","url":"https://learn.microsoft.com/en-us/dotnet/core/releases-and-support","lang":"en"},{"level":"normal","title":"Microsoft Learn — What's new in the SDK for .NET 10","url":"https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-10/sdk","lang":"en"},{"level":"advanced","title":"Microsoft Learn — File-based apps","url":"https://learn.microsoft.com/en-us/dotnet/core/sdk/file-based-apps","lang":"en"}],"rule":"LTS — 3 года, STS — 2 года, качество одинаковое. .NET 10 — LTS до ноября 2028, .NET 9 — STS до ноября 2026."},{"id":"http-status-codes","title":"HTTP-статусы и ошибки API","sources":[{"level":"beginner","title":"MDN — HTTP response status codes","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status","lang":"en"},{"level":"normal","title":"Microsoft Learn — Handle errors in ASP.NET Core APIs","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling-api","lang":"en"},{"level":"advanced","title":"RFC 9457 — Problem Details for HTTP APIs","url":"https://www.rfc-editor.org/rfc/rfc9457.html","lang":"en"}],"rule":"2xx — ок, 4xx — виноват клиент, 5xx — виноват сервер. 401 — «кто ты?», 403 — «тебе нельзя»."},{"id":"http-rest-design","title":"Проектирование REST API","sources":[{"level":"beginner","title":"Martin Fowler — Richardson Maturity Model","url":"https://martinfowler.com/articles/richardsonMaturityModel.html","lang":"en"},{"level":"normal","title":"MDN — PATCH request method","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/PATCH","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Create web APIs with ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/web-api/","lang":"en"}],"rule":"Ресурс — существительное в URL, действие — HTTP-метод. PUT заменяет целиком и идемпотентен, PATCH — частично."},{"id":"http-content-negotiation","title":"Content negotiation и форматы данных","sources":[{"level":"beginner","title":"MDN — Content negotiation","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Content_negotiation","lang":"en"},{"level":"normal","title":"Microsoft Learn — Format response data in ASP.NET Core Web API","url":"https://learn.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting","lang":"en"},{"level":"advanced","title":"RFC 8259 — The JSON Data Interchange Format","url":"https://www.rfc-editor.org/rfc/rfc8259.html","lang":"en"}],"rule":"Accept — что хочу получить, Content-Type — что отправляю. Нет формата — 406, не понял тело — 415."},{"id":"https-tls","title":"HTTPS и TLS","sources":[{"level":"beginner","title":"MDN — HTTPS","url":"https://developer.mozilla.org/ru/docs/Glossary/HTTPS","lang":"ru"},{"level":"normal","title":"MDN — Transport Layer Security (TLS)","url":"https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Transport_Layer_Security","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Enforce HTTPS in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/security/enforcing-ssl","lang":"en"}],"rule":"HTTPS = HTTP + TLS. UseHttpsRedirection переводит на HTTPS, UseHsts запрещает браузеру HTTP в будущем."},{"id":"aspnet-api-styles","title":"Minimal API и контроллеры","sources":[{"level":"beginner","title":"Microsoft Learn — Обзор API в ASP.NET Core","url":"https://learn.microsoft.com/ru-ru/aspnet/core/fundamentals/apis","lang":"ru"},{"level":"normal","title":"Microsoft Learn — APIs overview","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/apis","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Minimal APIs quick reference","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis","lang":"en"}],"rule":"Новый проект — Minimal API (MapGet/MapPost). Контроллеры — классы с [ApiController] и атрибутами."},{"id":"aspnet-routing","title":"Маршрутизация в ASP.NET Core","sources":[{"level":"beginner","title":"Microsoft Learn — Маршрутизация в ASP.NET Core","url":"https://learn.microsoft.com/ru-ru/aspnet/core/fundamentals/routing","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Routing in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Routing to controller actions","url":"https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing","lang":"en"}],"rule":"Шаблон {id:int} выбирает маршрут; constraint — не валидация (иначе 404 вместо 400)."},{"id":"aspnet-binding","title":"Model binding и результаты endpoints","sources":[{"level":"beginner","title":"Microsoft Learn — Model Binding in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding","lang":"en"},{"level":"normal","title":"Microsoft Learn — Parameter binding in Minimal API apps","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/parameter-binding","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Create responses in Minimal API apps","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/responses","lang":"en"}],"rule":"Вход: [FromRoute]/[FromQuery]/[FromBody]. Выход: TypedResults — типы видны тестам и OpenAPI."},{"id":"aspnet-validation","title":"Валидация в Minimal API (.NET 10)","sources":[{"level":"beginner","title":"Microsoft Learn — Проверка в ASP.NET Core","url":"https://learn.microsoft.com/ru-ru/aspnet/core/fundamentals/validation","lang":"ru"},{"level":"normal","title":"Microsoft Learn — What's new in ASP.NET Core 10","url":"https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-10.0","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Validation in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/validation","lang":"en"}],"rule":"AddValidation() + атрибуты DataAnnotations = автоматический 400 до вызова обработчика."},{"id":"aspnet-openapi","title":"OpenAPI в ASP.NET Core","sources":[{"level":"beginner","title":"Microsoft Learn — OpenAPI support overview","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/overview","lang":"en"},{"level":"normal","title":"Microsoft Learn — Generate OpenAPI documents","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/aspnetcore-openapi","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Use OpenAPI documents","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/using-openapi-documents","lang":"en"}],"rule":"AddOpenApi + MapOpenApi = документ /openapi/v1.json (OpenAPI 3.1 в .NET 10). UI — отдельный пакет."},{"id":"aspnet-razor","title":"Razor и Razor Pages","sources":[{"level":"beginner","title":"Microsoft Learn — Razor Pages","url":"https://learn.microsoft.com/ru-ru/aspnet/core/razor-pages/","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Razor syntax reference","url":"https://learn.microsoft.com/en-us/aspnet/core/mvc/views/razor","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Razor Pages architecture and concepts","url":"https://learn.microsoft.com/en-us/aspnet/core/razor-pages/","lang":"en"}],"rule":"@ переключает с HTML на C#. @page первой строкой делает .cshtml страницей Razor Pages."},{"id":"aspnet-di-lifetimes","title":"Время жизни сервисов в DI","sources":[{"level":"beginner","title":"Microsoft Learn — Внедрение зависимостей в ASP.NET Core","url":"https://learn.microsoft.com/ru-ru/aspnet/core/fundamentals/dependency-injection","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Dependency injection in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Dependency injection guidelines","url":"https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection/guidelines","lang":"en"}],"rule":"Зависимость живёт не меньше того, кто её держит. Scoped внутри Singleton — captive dependency."},{"id":"aspnet-middleware","title":"Middleware и конвейер запроса","sources":[{"level":"beginner","title":"Microsoft Learn — ПО промежуточного слоя ASP.NET Core","url":"https://learn.microsoft.com/ru-ru/aspnet/core/fundamentals/middleware/","lang":"ru"},{"level":"normal","title":"Microsoft Learn — ASP.NET Core Middleware","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Write custom ASP.NET Core middleware","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write","lang":"en"}],"rule":"Запрос идёт сверху вниз, ответ — снизу вверх. Обработчик ошибок — первым, Run — последним."},{"id":"aspnet-configuration","title":"Конфигурация и Options pattern","sources":[{"level":"beginner","title":"Microsoft Learn — Конфигурация в ASP.NET Core","url":"https://learn.microsoft.com/ru-ru/aspnet/core/fundamentals/configuration/","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Configuration in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Options pattern in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options","lang":"en"}],"rule":"Кто последний добавлен, тот и прав: командная строка > env > appsettings. Snapshot — Scoped, Monitor — живой Singleton."},{"id":"cs-async-await","title":"async/await и Task","sources":[{"level":"beginner","title":"Microsoft Learn — Асинхронное программирование","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/asynchronous-programming/","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Asynchronous programming scenarios","url":"https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/async-scenarios","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Task.WhenAll","url":"https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.whenall","lang":"en"}],"rule":"await не блокирует поток, а отпускает его. Независимые задачи — запусти все и await Task.WhenAll."},{"id":"cs-async-pitfalls","title":"Ловушки async в ASP.NET Core","sources":[{"level":"beginner","title":"Microsoft Learn — ASP.NET Core Best Practices","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/best-practices","lang":"en"},{"level":"normal","title":"MSDN Magazine — Async/Await Best Practices","url":"https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming","lang":"en"},{"level":"advanced","title":".NET Blog — ConfigureAwait FAQ","url":"https://devblogs.microsoft.com/dotnet/configureawait-faq/","lang":"en"}],"rule":"Async all the way: без .Result/.Wait, без async void, без лишнего Task.Run. ConfigureAwait(false) — для библиотек."},{"id":"cs-cancellation","title":"Отмена через CancellationToken","sources":[{"level":"beginner","title":"Microsoft Learn — Cancellation in managed threads","url":"https://learn.microsoft.com/en-us/dotnet/standard/threading/cancellation-in-managed-threads","lang":"en"},{"level":"normal","title":"Microsoft Learn — Task cancellation","url":"https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-cancellation","lang":"en"},{"level":"advanced","title":"Microsoft Learn — CancellationTokenSource class","url":"https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource","lang":"en"}],"rule":"Токен — пульт отмены: принял в endpoint — передай дальше в каждый async-вызов."},{"id":"sql-joins","title":"Типы JOIN","sources":[{"level":"beginner","title":"PostgreSQL | INNER JOIN (metanit)","url":"https://metanit.com/sql/postgresql/6.2.php","lang":"ru"},{"level":"normal","title":"PostgreSQL — 2.6. Joins Between Tables","url":"https://www.postgresql.org/docs/current/tutorial-join.html","lang":"en"},{"level":"advanced","title":"Joins (SQL Server): логические и физические соединения","url":"https://learn.microsoft.com/en-us/sql/relational-databases/performance/joins","lang":"en"}],"rule":"INNER — только пары. LEFT — вся левая таблица, справа NULL, если пары нет. FULL — обе стороны. CROSS — каждая с каждой, N × M."},{"id":"sql-grouping","title":"GROUP BY, WHERE и HAVING","sources":[{"level":"beginner","title":"PostgreSQL | Группировка (metanit)","url":"https://metanit.com/sql/postgresql/4.6.php","lang":"ru"},{"level":"normal","title":"PostgreSQL — 2.7. Aggregate Functions","url":"https://www.postgresql.org/docs/current/tutorial-agg.html","lang":"en"},{"level":"advanced","title":"GROUP BY (Transact-SQL): ROLLUP, CUBE, GROUPING SETS","url":"https://learn.microsoft.com/en-us/sql/t-sql/queries/select-group-by-transact-sql","lang":"en"}],"rule":"WHERE фильтрует строки до группировки, HAVING — группы после. Агрегат в WHERE нельзя. В SELECT — только столбцы из GROUP BY или агрегаты."},{"id":"sql-keys","title":"Первичные и внешние ключи","sources":[{"level":"beginner","title":"PostgreSQL | Внешние ключи (metanit)","url":"https://metanit.com/sql/postgresql/2.5.php","lang":"ru"},{"level":"normal","title":"PostgreSQL — 5.5. Constraints","url":"https://www.postgresql.org/docs/current/ddl-constraints.html","lang":"en"},{"level":"advanced","title":"Primary and foreign key constraints (SQL Server)","url":"https://learn.microsoft.com/en-us/sql/relational-databases/tables/primary-and-foreign-key-constraints","lang":"en"}],"rule":"PK = уникально + NOT NULL, один на таблицу, индекс создаётся сам. FK = ссылочная целостность, индекс на нём сам НЕ создаётся."},{"id":"sql-indexes","title":"Индексы","sources":[{"level":"beginner","title":"Кластеризованные и некластеризованные индексы (SQL Server)","url":"https://learn.microsoft.com/ru-ru/sql/relational-databases/indexes/clustered-and-nonclustered-indexes-described","lang":"ru"},{"level":"normal","title":"PostgreSQL — 11.1. Indexes: Introduction","url":"https://www.postgresql.org/docs/current/indexes-intro.html","lang":"en"},{"level":"advanced","title":"Use The Index, Luke (Markus Winand)","url":"https://use-the-index-luke.com/","lang":"en"}],"rule":"Индекс ускоряет чтение, но замедляет запись: каждый INSERT/UPDATE/DELETE обновляет все индексы таблицы. Кластерный индекс — один на таблицу."},{"id":"sql-dml-paging","title":"CRUD и постраничная выборка","sources":[{"level":"beginner","title":"PostgreSQL — Chapter 6. Data Manipulation","url":"https://www.postgresql.org/docs/current/dml.html","lang":"en"},{"level":"normal","title":"PostgreSQL — 7.6. LIMIT and OFFSET","url":"https://www.postgresql.org/docs/current/queries-limit.html","lang":"en"},{"level":"advanced","title":"ORDER BY clause: OFFSET и FETCH (Transact-SQL)","url":"https://learn.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql","lang":"en"}],"rule":"UPDATE/DELETE без WHERE меняют все строки — сначала SELECT с тем же WHERE. Пагинация без ORDER BY — случайные страницы."},{"id":"sql-injection-params","title":"Параметризованные запросы против SQL-инъекций","sources":[{"level":"beginner","title":"Инъекция SQL (SQL Server)","url":"https://learn.microsoft.com/ru-ru/sql/relational-databases/security/sql-injection","lang":"ru"},{"level":"normal","title":"OWASP SQL Injection Prevention Cheat Sheet","url":"https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html","lang":"en"},{"level":"advanced","title":"SQL Queries — EF Core (FromSql, FromSqlRaw, SqlQuery)","url":"https://learn.microsoft.com/en-us/ef/core/querying/sql-queries","lang":"en"}],"rule":"Ввод пользователя — только параметром, никогда склейкой строк. Имя таблицы или столбца параметром не передать — только белый список."},{"id":"efcore-dbcontext-lifetime","title":"Время жизни DbContext","sources":[{"level":"beginner","title":"Время существования, настройка и инициализация DbContext","url":"https://learn.microsoft.com/ru-ru/ef/core/dbcontext-configuration/","lang":"ru"},{"level":"normal","title":"DbContext Lifetime, Configuration, and Initialization","url":"https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/","lang":"en"},{"level":"advanced","title":"Advanced Performance Topics — DbContext pooling","url":"https://learn.microsoft.com/en-us/ef/core/performance/advanced-performance-topics","lang":"en"}],"rule":"DbContext = одна единица работы: Scoped на запрос, коротко живёт, не потокобезопасен. Одна операция за раз — всегда await."},{"id":"efcore-migrations","title":"Миграции EF Core","sources":[{"level":"beginner","title":"EF Core | Управление схемой БД и миграции (metanit)","url":"https://metanit.com/sharp/efcore/2.2.php","lang":"ru"},{"level":"normal","title":"Migrations Overview — EF Core","url":"https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/","lang":"en"},{"level":"advanced","title":"Applying Migrations — EF Core","url":"https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/applying","lang":"en"}],"rule":"migrations add — сравнить модель со снапшотом и создать миграцию; database update — применить. На прод — проверяемый SQL-скрипт или bundle, а не Migrate() при старте."},{"id":"efcore-tracking","title":"Change tracking и AsNoTracking","sources":[{"level":"beginner","title":"EF Core | Отслеживание объектов и AsNoTracking (metanit)","url":"https://metanit.com/sharp/efcore/5.7.php","lang":"ru"},{"level":"normal","title":"Tracking vs. No-Tracking Queries — EF Core","url":"https://learn.microsoft.com/en-us/ef/core/querying/tracking","lang":"en"},{"level":"advanced","title":"Change Tracking — EF Core","url":"https://learn.microsoft.com/en-us/ef/core/change-tracking/","lang":"en"}],"rule":"Читаю, чтобы изменить и сохранить — tracking. Читаю только для показа — AsNoTracking: быстрее, но SaveChanges такие объекты не увидит."},{"id":"efcore-loading","title":"Загрузка связанных данных и N+1","sources":[{"level":"beginner","title":"EF Core | Загрузка связанных данных. Метод Include (metanit)","url":"https://metanit.com/sharp/efcore/3.3.php","lang":"ru"},{"level":"normal","title":"Eager Loading of Related Data — EF Core","url":"https://learn.microsoft.com/en-us/ef/core/querying/related-data/eager","lang":"en"},{"level":"advanced","title":"Single vs. Split Queries — EF Core","url":"https://learn.microsoft.com/en-us/ef/core/querying/single-split-queries","lang":"en"}],"rule":"Запрос в цикле = N+1. Лечение: Include или Select-проекция. Много Include коллекций = cartesian explosion, тогда AsSplitQuery."},{"id":"dapper-vs-efcore","title":"Dapper или EF Core","sources":[{"level":"beginner","title":"Learn Dapper — Dapper Tutorial for C# and .NET Core","url":"https://www.learndapper.com/","lang":"en"},{"level":"normal","title":"Dapper — a simple object mapper for .NET (README)","url":"https://github.com/DapperLib/Dapper","lang":"en"},{"level":"advanced","title":"Efficient Querying — EF Core","url":"https://learn.microsoft.com/en-us/ef/core/performance/efficient-querying","lang":"en"}],"rule":"Dapper — ты пишешь SQL, он мапит строки в объекты. EF Core — LINQ в SQL, tracking, миграции. Обычный CRUD — EF, ручной контроль SQL — Dapper."},{"id":"authn-vs-authz","title":"Аутентификация и авторизация","sources":[{"level":"beginner","title":"Обзор проверки подлинности ASP.NET Core","url":"https://learn.microsoft.com/ru-ru/aspnet/core/security/authentication/?view=aspnetcore-10.0","lang":"ru"},{"level":"normal","title":"Introduction to authorization in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/security/authorization/introduction?view=aspnetcore-10.0","lang":"en"},{"level":"advanced","title":"MDN — 401 Unauthorized","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/401","lang":"en"}],"rule":"Аутентификация — кто ты (иначе 401). Авторизация — что тебе можно (иначе 403). Сначала личность, потом права."},{"id":"jwt-bearer","title":"JWT и его проверка","sources":[{"level":"beginner","title":"JSON Web Token Introduction (jwt.io)","url":"https://jwt.io/introduction","lang":"en"},{"level":"normal","title":"Configure JWT bearer authentication in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/security/authentication/configure-jwt-bearer-authentication?view=aspnetcore-10.0","lang":"en"},{"level":"advanced","title":"RFC 8725 — JSON Web Token Best Current Practices","url":"https://www.rfc-editor.org/rfc/rfc8725","lang":"en"}],"rule":"JWT подписан, но не зашифрован: payload читает любой. API проверяет подпись, iss, aud и exp — не доверяй alg из заголовка."},{"id":"oauth-oidc","title":"OAuth 2.0 и OpenID Connect","sources":[{"level":"beginner","title":"OAuth 2.0 (oauth.net)","url":"https://oauth.net/2/","lang":"en"},{"level":"normal","title":"Microsoft identity platform and OAuth 2.0 authorization code flow","url":"https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow","lang":"en"},{"level":"advanced","title":"RFC 9700 — Best Current Practice for OAuth 2.0 Security","url":"https://www.rfc-editor.org/rfc/rfc9700","lang":"en"}],"rule":"OAuth — доступ (access token), OIDC — личность (ID token + scope openid). Для пользователя — Authorization Code + PKCE; Implicit и Password — нет."},{"id":"cookie-vs-bearer","title":"Cookie или Bearer-токен","sources":[{"level":"beginner","title":"MDN — Using HTTP cookies","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies","lang":"en"},{"level":"normal","title":"Use cookie authentication without ASP.NET Core Identity","url":"https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-10.0","lang":"en"},{"level":"advanced","title":"OWASP Session Management Cheat Sheet","url":"https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html","lang":"en"}],"rule":"Cookie браузер шлёт сам — защищайся от CSRF (SameSite, antiforgery). Bearer клиент кладёт в Authorization сам. Токены — не в localStorage."},{"id":"aspnet-authz-policies","title":"[Authorize], роли и политики","sources":[{"level":"beginner","title":"Простая авторизация в ASP.NET Core","url":"https://learn.microsoft.com/ru-ru/aspnet/core/security/authorization/simple?view=aspnetcore-10.0","lang":"ru"},{"level":"normal","title":"Policy-based authorization in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-10.0","lang":"en"},{"level":"advanced","title":"Resource-based authorization in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/security/authorization/resource-based?view=aspnetcore-10.0","lang":"en"}],"rule":"Roles = \"A,B\" — любая из ролей; два [Authorize] — обе. Сложное правило = политика: requirement + handler. Доступ к конкретному объекту — AuthorizeAsync в коде."},{"id":"xunit-basics","title":"Основы xUnit","sources":[{"level":"beginner","title":"Модульное тестирование C# в .NET с dotnet test и xUnit","url":"https://learn.microsoft.com/ru-ru/dotnet/core/testing/unit-testing-csharp-with-xunit","lang":"ru"},{"level":"normal","title":"Getting Started with xUnit.net v3","url":"https://xunit.net/docs/getting-started/v3/getting-started","lang":"en"},{"level":"advanced","title":"xUnit.net — Sharing Context between Tests","url":"https://xunit.net/docs/shared-context","lang":"en"}],"rule":"Fact — тест без данных, Theory — тест на наборе данных. Новый экземпляр класса на каждый тест: setup в конструкторе, общий ресурс — IClassFixture."},{"id":"dotnet-mocking","title":"Moq, NSubstitute и подмена зависимостей","sources":[{"level":"beginner","title":"NSubstitute — Getting started","url":"https://nsubstitute.github.io/help/getting-started/","lang":"en"},{"level":"normal","title":"Moq Quickstart (wiki)","url":"https://github.com/devlooped/moq/wiki/Quickstart","lang":"en"},{"level":"advanced","title":"Martin Fowler — Mocks Aren't Stubs","url":"https://martinfowler.com/articles/mocksArentStubs.html","lang":"en"}],"rule":"Подменить можно только то, что пришло извне: интерфейс через DI, время через TimeProvider. Moq: Setup/Returns/Verify; NSubstitute: Returns/Received."},{"id":"aspnet-integration-tests","title":"Интеграционные тесты ASP.NET Core","sources":[{"level":"beginner","title":"Интеграционные тесты на платформе ASP.NET Core","url":"https://learn.microsoft.com/ru-ru/aspnet/core/test/integration-tests?view=aspnetcore-10.0","lang":"ru"},{"level":"normal","title":"Integration tests in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-10.0","lang":"en"},{"level":"advanced","title":"Overview of testing applications that use EF Core","url":"https://learn.microsoft.com/en-us/ef/core/testing/","lang":"en"}],"rule":"WebApplicationFactory<Program> поднимает API в памяти, CreateClient даёт HttpClient. База — настоящая СУБД, а не InMemory-провайдер."},{"id":"pull-requests","title":"Pull request и code review","sources":[{"level":"beginner","title":"GitHub Docs — Запросы на слияние","url":"https://docs.github.com/ru/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests","lang":"ru"},{"level":"normal","title":"GitHub Docs — Pull requests","url":"https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests","lang":"en"},{"level":"advanced","title":"Google Engineering Practices — Code Review","url":"https://google.github.io/eng-practices/review/","lang":"en"}],"rule":"PR — предложение влить ветку: обсуждение, ревью и зелёный CI до merge. Незаконченное — draft."},{"id":"docker-image-container","title":"Образ, контейнер и слои","sources":[{"level":"beginner","title":"Docker — What is a container?","url":"https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-container/","lang":"en"},{"level":"normal","title":"Docker — What is an image?","url":"https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-an-image/","lang":"en"},{"level":"advanced","title":"Docker — Building best practices","url":"https://docs.docker.com/build/building/best-practices/","lang":"en"}],"rule":"Образ — неизменяемый шаблон из слоёв (класс), контейнер — запущенный из него процесс (экземпляр). Изменился слой — пересобираются все слои после него."},{"id":"dotnet-containers","title":"Контейнеры для .NET","sources":[{"level":"beginner","title":"Контейнеризация приложения .NET с помощью Docker","url":"https://learn.microsoft.com/ru-ru/dotnet/core/docker/build-container","lang":"ru"},{"level":"normal","title":"Docker — Multi-stage builds","url":"https://docs.docker.com/build/building/multi-stage/","lang":"en"},{"level":"advanced","title":"Containerize an app with dotnet publish","url":"https://learn.microsoft.com/en-us/dotnet/core/containers/sdk-publish","lang":"en"}],"rule":"Собирай на sdk, запускай на aspnet: multi-stage. Или вообще без Dockerfile — dotnet publish /t:PublishContainer. Порт по умолчанию 8080."},{"id":"dotnet-debugger","title":"Отладчик: breakpoints и шаги","sources":[{"level":"beginner","title":"Обзор отладчика Visual Studio","url":"https://learn.microsoft.com/ru-ru/visualstudio/debugger/debugger-feature-tour","lang":"ru"},{"level":"normal","title":"Debug code with Visual Studio Code","url":"https://code.visualstudio.com/docs/debugtest/debugging","lang":"en"},{"level":"advanced","title":"Use the right type of breakpoint — Visual Studio","url":"https://learn.microsoft.com/en-us/visualstudio/debugger/using-breakpoints","lang":"en"}],"rule":"F10 — через строку, F11 — внутрь метода, Shift+F11 — наружу. Редкий баг — условный breakpoint, посмотреть без остановки — logpoint/tracepoint."},{"id":"dotnet-exceptions","title":"Частые исключения .NET","sources":[{"level":"beginner","title":"Рекомендации по исключениям (.NET)","url":"https://learn.microsoft.com/ru-ru/dotnet/standard/exceptions/best-practices-for-exceptions","lang":"ru"},{"level":"normal","title":"NullReferenceException Class","url":"https://learn.microsoft.com/en-us/dotnet/api/system.nullreferenceexception","lang":"en"},{"level":"advanced","title":"Nullable reference types — C#","url":"https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/null-safety/nullable-reference-types","lang":"en"}],"rule":"Плохой аргумент — ArgumentException, неподходящее состояние — InvalidOperationException, NullReferenceException — баг в коде, его не ловят. Пробрасывай через throw;, не throw ex;."},{"id":"aspnet-error-handling","title":"Обработка ошибок и ProblemDetails","sources":[{"level":"beginner","title":"Обработка ошибок в ASP.NET Core","url":"https://learn.microsoft.com/ru-ru/aspnet/core/fundamentals/error-handling?view=aspnetcore-10.0","lang":"ru"},{"level":"normal","title":"Handle errors in ASP.NET Core APIs","url":"https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling-api?view=aspnetcore-10.0","lang":"en"},{"level":"advanced","title":"RFC 9457 — Problem Details for HTTP APIs","url":"https://www.rfc-editor.org/rfc/rfc9457","lang":"en"}],"rule":"AddProblemDetails + UseExceptionHandler: клиенту — ProblemDetails без stack trace, детали — в лог. Developer Exception Page — только в Development."},{"id":"dotnet-logging","title":"ILogger и структурированное логирование","sources":[{"level":"beginner","title":"Ведение журнала в .NET и ASP.NET Core","url":"https://learn.microsoft.com/ru-ru/aspnet/core/fundamentals/logging/?view=aspnetcore-10.0","lang":"ru"},{"level":"normal","title":"Logging in C# — .NET","url":"https://learn.microsoft.com/en-us/dotnet/core/extensions/logging/overview","lang":"en"},{"level":"advanced","title":"Compile-time logging source generation","url":"https://learn.microsoft.com/en-us/dotnet/core/extensions/logging/source-generation","lang":"en"}],"rule":"Уровни: Trace < Debug < Information < Warning < Error < Critical. Пиши шаблон {OrderId}, а не $-интерполяцию: поле останется полем."},{"id":"clean-architecture","title":"Clean и Hexagonal Architecture","sources":[{"level":"beginner","title":"Microsoft Learn — Распространённые архитектуры веб-приложений","url":"https://learn.microsoft.com/ru-ru/dotnet/architecture/modern-web-apps-azure/common-web-application-architectures","lang":"ru"},{"level":"normal","title":"Robert C. Martin — The Clean Architecture","url":"https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html","lang":"en"},{"level":"advanced","title":"Alistair Cockburn — Hexagonal architecture","url":"https://alistair.cockburn.us/hexagonal-architecture/","lang":"en"}],"rule":"Зависимости смотрят внутрь: домен не знает про EF Core и HTTP. Порт — интерфейс ядра, адаптер — технология за ним."},{"id":"ddd-building-blocks","title":"Тактический DDD: агрегаты, value objects, доменные события","sources":[{"level":"beginner","title":"Microsoft Learn — Проектирование микрослужбы, ориентированной на DDD","url":"https://learn.microsoft.com/ru-ru/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/ddd-oriented-microservice","lang":"ru"},{"level":"normal","title":"Martin Fowler — DDD Aggregate","url":"https://martinfowler.com/bliki/DDD_Aggregate.html","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Domain events: design and implementation","url":"https://learn.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/domain-events-design-implementation","lang":"en"}],"rule":"Агрегат — граница транзакции: меняй один агрегат за раз, остальных извещай событием. Entity сравнивают по Id, value object — по значению."},{"id":"simple-design-tradeoffs","title":"SOLID, DRY, KISS и YAGNI: баланс принципов","sources":[{"level":"beginner","title":"Martin Fowler — Yagni","url":"https://martinfowler.com/bliki/Yagni.html","lang":"en"},{"level":"normal","title":"Martin Fowler — Beck Design Rules","url":"https://martinfowler.com/bliki/BeckDesignRules.html","lang":"en"},{"level":"advanced","title":"Martin Fowler — Is Design Dead?","url":"https://martinfowler.com/articles/designDead.html","lang":"en"}],"rule":"YAGNI режет фичи «на будущее», но не рефакторинг. Абстракцию вводят на втором реальном случае, а не на первом."},{"id":"outbox-inbox","title":"Transactional outbox и inbox","sources":[{"level":"beginner","title":"microservices.io — Transactional outbox","url":"https://microservices.io/patterns/data/transactional-outbox.html","lang":"en"},{"level":"normal","title":"microservices.io — Idempotent Consumer","url":"https://microservices.io/patterns/communication-style/idempotent-consumer.html","lang":"en"},{"level":"advanced","title":"Azure Architecture Center — Transactional outbox with Azure Cosmos DB","url":"https://learn.microsoft.com/en-us/azure/architecture/databases/guide/transactional-out-box-cosmos","lang":"en"}],"rule":"Outbox: событие пишем в ту же транзакцию, что и данные. Inbox: запоминаем MessageId. Вместе: «хотя бы раз» + «обработано как один раз»."},{"id":"idempotent-processing","title":"Идемпотентность и миф exactly-once","sources":[{"level":"beginner","title":"MDN — Idempotent","url":"https://developer.mozilla.org/en-US/docs/Glossary/Idempotent","lang":"en"},{"level":"normal","title":"IETF draft — The Idempotency-Key HTTP Header Field","url":"https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/","lang":"en"},{"level":"advanced","title":"Azure Service Bus — Duplicate message detection","url":"https://learn.microsoft.com/en-us/azure/service-bus-messaging/duplicate-detection","lang":"en"}],"rule":"Сеть даёт «не больше раза» или «хотя бы раз». Exactly-once на практике = at-least-once + идемпотентная обработка."},{"id":"saga-compensation","title":"Саги и компенсирующие транзакции","sources":[{"level":"beginner","title":"Azure Architecture Center — Шаблон Saga","url":"https://learn.microsoft.com/ru-ru/azure/architecture/patterns/saga","lang":"ru"},{"level":"normal","title":"microservices.io — Saga","url":"https://microservices.io/patterns/data/saga.html","lang":"en"},{"level":"advanced","title":"Azure Architecture Center — Compensating Transaction pattern","url":"https://learn.microsoft.com/en-us/azure/architecture/patterns/compensating-transaction","lang":"en"}],"rule":"Сага — цепочка локальных транзакций. Откат — не rollback, а компенсация вперёд; изоляции (I из ACID) у саги нет."},{"id":"event-driven-styles","title":"Виды event-driven взаимодействия","sources":[{"level":"beginner","title":"Azure Architecture Center — Стиль событийно-ориентированной архитектуры","url":"https://learn.microsoft.com/ru-ru/azure/architecture/guide/architecture-styles/event-driven","lang":"ru"},{"level":"normal","title":"Martin Fowler — What do you mean by “Event-Driven”?","url":"https://martinfowler.com/articles/201701-event-driven.html","lang":"en"},{"level":"advanced","title":"microservices.io — Event sourcing","url":"https://microservices.io/patterns/data/event-sourcing.html","lang":"en"}],"rule":"Notification — «что-то случилось, спроси сам»; state transfer — «вот данные»; event sourcing — «события и есть данные»."},{"id":"service-mesh-traffic","title":"Service mesh","sources":[{"level":"beginner","title":"Istio — The Istio service mesh","url":"https://istio.io/latest/about/service-mesh/","lang":"en"},{"level":"normal","title":"Linkerd — What is a service mesh?","url":"https://linkerd.io/what-is-a-service-mesh/","lang":"en"},{"level":"advanced","title":"Istio — Ambient mode overview","url":"https://istio.io/latest/docs/ambient/overview/","lang":"en"}],"rule":"Mesh переносит сетевые заботы (mTLS, трафик, телеметрию) из кода в прокси. Платишь за это операционной сложностью."},{"id":"otel-dotnet","title":"OpenTelemetry в .NET","sources":[{"level":"beginner","title":"Microsoft Learn — Наблюдаемость .NET с помощью OpenTelemetry","url":"https://learn.microsoft.com/ru-ru/dotnet/core/diagnostics/observability-with-otel","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Add distributed tracing instrumentation","url":"https://learn.microsoft.com/en-us/dotnet/core/diagnostics/distributed-tracing-instrumentation-walkthroughs","lang":"en"},{"level":"advanced","title":"W3C — Trace Context","url":"https://www.w3.org/TR/trace-context/","lang":"en"}],"rule":"Пиши через ILogger, Meter и ActivitySource — OpenTelemetry только собирает и экспортирует. Activity в .NET = span."},{"id":"slo-error-budget","title":"SLI, SLO и error budget","sources":[{"level":"beginner","title":"Google SRE Book — Service Level Objectives","url":"https://sre.google/sre-book/service-level-objectives/","lang":"en"},{"level":"normal","title":"Google SRE Book — Embracing Risk","url":"https://sre.google/sre-book/embracing-risk/","lang":"en"},{"level":"advanced","title":"Google SRE Workbook — Alerting on SLOs","url":"https://sre.google/workbook/alerting-on-slos/","lang":"en"}],"rule":"SLI — что меряем, SLO — сколько хотим, SLA — что обещали по договору. 100% минус SLO = error budget."},{"id":"dotnet-memory-perf","title":"Память в .NET: Span, ArrayPool, GC","sources":[{"level":"beginner","title":"Microsoft Learn — Типы, связанные с памятью и диапазоном","url":"https://learn.microsoft.com/ru-ru/dotnet/standard/memory-and-spans/","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Workstation vs. server garbage collection","url":"https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/workstation-server-gc","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Garbage collector config settings","url":"https://learn.microsoft.com/en-us/dotnet/core/runtime-config/garbage-collector","lang":"en"}],"rule":"Меньше аллокаций — меньше работы GC: Span вместо Substring, ArrayPool вместо new byte[]. Span живёт только на стеке."},{"id":"benchmarkdotnet","title":"Измерение производительности в .NET","sources":[{"level":"beginner","title":"BenchmarkDotNet — Overview","url":"https://benchmarkdotnet.org/articles/overview.html","lang":"en"},{"level":"normal","title":"BenchmarkDotNet — Good Practices","url":"https://benchmarkdotnet.org/articles/guides/good-practices.html","lang":"en"},{"level":"advanced","title":".NET Blog — Performance Improvements in .NET 10","url":"https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-10/","lang":"en"}],"rule":"Без замера нет оптимизации: BenchmarkDotNet в Release, [MemoryDiagnoser] для аллокаций. Stopwatch в цикле обманывает."},{"id":"ef-core-tuning","title":"Производительность EF Core","sources":[{"level":"beginner","title":"Microsoft Learn — Производительность EF Core: общие сведения","url":"https://learn.microsoft.com/ru-ru/ef/core/performance/","lang":"ru"},{"level":"normal","title":"EF Core — Single vs. Split Queries","url":"https://learn.microsoft.com/en-us/ef/core/querying/single-split-queries","lang":"en"},{"level":"advanced","title":"EF Core — Advanced Performance Topics","url":"https://learn.microsoft.com/en-us/ef/core/performance/advanced-performance-topics","lang":"en"}],"rule":"Меньше данных и round trip: проекции, AsSplitQuery против cartesian explosion, ExecuteUpdate вместо загрузки сущностей."},{"id":"sast-dast","title":"SAST, DAST и quality gates","sources":[{"level":"beginner","title":"OWASP — Source Code Analysis Tools","url":"https://community.owasp.org/Source_Code_Analysis_Tools","lang":"en"},{"level":"normal","title":"OWASP DevSecOps Guideline — Dynamic Application Security Testing","url":"https://owasp.github.io/www-project-devsecops-guideline/latest/02b-Dynamic-Application-Security-Testing","lang":"en"},{"level":"advanced","title":"SonarQube Server — Understanding quality gates","url":"https://docs.sonarsource.com/sonarqube-server/quality-standards-administration/managing-quality-gates/introduction-to-quality-gates","lang":"en"}],"rule":"SAST читает код без запуска, DAST атакует запущенное приложение снаружи. Первый — в каждый PR, второй — на стенде."},{"id":"secrets-dotnet","title":"Секреты в .NET-приложениях","sources":[{"level":"beginner","title":"Microsoft Learn — Безопасное хранение секретов приложений в разработке","url":"https://learn.microsoft.com/ru-ru/aspnet/core/security/app-secrets","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Azure Key Vault configuration provider","url":"https://learn.microsoft.com/en-us/aspnet/core/security/key-vault-configuration","lang":"en"},{"level":"advanced","title":"OWASP — Secrets Management Cheat Sheet","url":"https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html","lang":"en"}],"rule":"user-secrets — только для разработки; в проде — Key Vault и managed identity. Утёк секрет — сначала отзови, потом чисти."},{"id":"container-hardening","title":"Безопасность .NET-контейнеров","sources":[{"level":"beginner","title":"Microsoft Learn — .NET container images","url":"https://learn.microsoft.com/en-us/dotnet/core/docker/container-images","lang":"en"},{"level":"normal","title":".NET Blog — Secure your .NET cloud apps with rootless Linux Containers","url":"https://devblogs.microsoft.com/dotnet/securing-containers-with-rootless/","lang":"en"},{"level":"advanced","title":".NET Blog — Announcing .NET Chiseled Containers","url":"https://devblogs.microsoft.com/dotnet/announcing-dotnet-chiseled-containers/","lang":"en"}],"rule":"Меньше в образе — меньше атак: non-root (USER $APP_UID), порт 8080, chiseled без shell и пакетного менеджера."},{"id":"resilience-pipeline","title":"Resilience в .NET: retry, circuit breaker, timeout, hedging","sources":[{"level":"beginner","title":"Microsoft Learn — Создание устойчивых HTTP-приложений","url":"https://learn.microsoft.com/ru-ru/dotnet/core/resilience/http-resilience","lang":"ru"},{"level":"normal","title":"Polly — Resilience strategies","url":"https://www.pollydocs.org/strategies/","lang":"en"},{"level":"advanced","title":"Azure Architecture Center — Circuit Breaker pattern","url":"https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker","lang":"en"}],"rule":"Retry — для коротких сбоев, circuit breaker — для долгих, timeout — всегда. Повторяй только идемпотентное."},{"id":"high-availability","title":"HA, fault tolerance и multi-region","sources":[{"level":"beginner","title":"Microsoft Learn — Что такое зоны доступности Azure","url":"https://learn.microsoft.com/ru-ru/azure/reliability/availability-zones-overview","lang":"ru"},{"level":"normal","title":"Azure Well-Architected — Designing for redundancy","url":"https://learn.microsoft.com/en-us/azure/well-architected/reliability/redundancy","lang":"en"},{"level":"advanced","title":"AWS Whitepaper — Disaster recovery options in the cloud","url":"https://docs.aws.amazon.com/whitepapers/latest/disaster-recovery-workloads-on-aws/disaster-recovery-options-in-the-cloud.html","lang":"en"}],"rule":"HA — быстро поднимаемся, FT — не падаем, DR — переживаем потерю региона. Чем меньше RTO/RPO, тем дороже."},{"id":"chaos-engineering","title":"Chaos engineering","sources":[{"level":"beginner","title":"Principles of Chaos Engineering","url":"https://principlesofchaos.org/","lang":"en"},{"level":"normal","title":"Microsoft Learn — What is Azure Chaos Studio?","url":"https://learn.microsoft.com/en-us/azure/chaos-studio/chaos-studio-overview","lang":"en"},{"level":"advanced","title":"Azure Well-Architected — Reliability testing strategy","url":"https://learn.microsoft.com/en-us/azure/well-architected/reliability/reliability-test","lang":"en"}],"rule":"Хаос — эксперимент, а не поломка: steady state → гипотеза → реальный сбой → ищем отклонение. Blast radius держим маленьким."},{"id":"threat-modeling","title":"Threat modeling","sources":[{"level":"beginner","title":"Threat Modeling Manifesto","url":"https://www.threatmodelingmanifesto.org/","lang":"en"},{"level":"normal","title":"OWASP — Threat Modeling Cheat Sheet","url":"https://cheatsheetseries.owasp.org/cheatsheets/Threat_Modeling_Cheat_Sheet.html","lang":"en"},{"level":"advanced","title":"Microsoft Threat Modeling Tool — Threats (STRIDE)","url":"https://learn.microsoft.com/en-us/azure/security/develop/threat-modeling-tool-threats","lang":"en"}],"rule":"Четыре вопроса: что строим, что сломается, что делаем, хорошо ли сделали. STRIDE проходим по каждой стрелке DFD."},{"id":"policy-as-code","title":"Policy as code","sources":[{"level":"beginner","title":"Open Policy Agent — Introduction","url":"https://www.openpolicyagent.org/docs","lang":"en"},{"level":"normal","title":"Open Policy Agent — Kubernetes admission control","url":"https://www.openpolicyagent.org/docs/kubernetes","lang":"en"},{"level":"advanced","title":"Conftest","url":"https://www.conftest.dev/","lang":"en"}],"rule":"Политика — это код: Rego в git, ревью и тесты. OPA принимает решение, а Kubernetes, CI или шлюз его исполняют."},{"id":"iac-provisioning","title":"Infrastructure as Code и cloud provisioning","sources":[{"level":"beginner","title":"Microsoft Learn — Что такое инфраструктура как код (IaC)?","url":"https://learn.microsoft.com/ru-ru/devops/deliver/what-is-infrastructure-as-code","lang":"ru"},{"level":"normal","title":"HashiCorp — What is Terraform?","url":"https://developer.hashicorp.com/terraform/intro","lang":"en"},{"level":"advanced","title":"Martin Fowler — Infrastructure As Code","url":"https://martinfowler.com/bliki/InfrastructureAsCode.html","lang":"en"}],"rule":"Инфраструктура только из git: plan — на ревью, apply — из конвейера. Ручная правка в консоли = drift."},{"id":"oltp-olap","title":"OLTP и OLAP","sources":[{"level":"beginner","title":"Azure Architecture Center — Online transaction processing (OLTP)","url":"https://learn.microsoft.com/en-us/azure/architecture/data-guide/relational-data/online-transaction-processing","lang":"en"},{"level":"normal","title":"Azure Architecture Center — Online analytical processing (OLAP)","url":"https://learn.microsoft.com/en-us/azure/architecture/data-guide/relational-data/online-analytical-processing","lang":"en"},{"level":"advanced","title":"Debezium — Architecture (change data capture)","url":"https://debezium.io/documentation/reference/stable/architecture.html","lang":"en"}],"rule":"OLTP — много коротких транзакций по строкам, OLAP — тяжёлые агрегаты по колонкам. Аналитику уносят из рабочей базы."},{"id":"compliance-scope","title":"Compliance: PCI DSS и PDPA","sources":[{"level":"beginner","title":"Microsoft Learn — PCI DSS (Azure compliance)","url":"https://learn.microsoft.com/en-us/azure/compliance/offerings/offering-pci-dss","lang":"en"},{"level":"normal","title":"PCI SSC FAQ — How does encrypted cardholder data impact PCI DSS scope?","url":"https://www.pcisecuritystandards.org/faq/articles/Frequently_Asked_Question/how-does-encrypted-cardholder-data-impact-pci-dss-scope/","lang":"en"},{"level":"advanced","title":"PDPC — Guide on Managing and Notifying Data Breaches under the PDPA","url":"https://www.pdpc.gov.sg/-/media/files/pdpc/pdf-files/other-guides/guide-on-managing-and-notifying-data-breaches-under-the-pdpa-15-mar-2021.pdf","lang":"en"}],"rule":"Лучшие данные карт — те, которых у тебя нет: токенизация сужает PCI scope. Шифрование само по себе scope не убирает."},{"id":"build-vs-buy","title":"Build vs buy: свой код, SaaS, PaaS, open source","sources":[{"level":"beginner","title":"Martin Fowler — Utility Vs Strategic Dichotomy","url":"https://martinfowler.com/bliki/UtilityVsStrategicDichotomy.html","lang":"en"},{"level":"normal","title":"Microsoft Learn — Shared responsibility in the cloud","url":"https://learn.microsoft.com/en-us/azure/security/fundamentals/shared-responsibility","lang":"en"},{"level":"advanced","title":"Martin Fowler (Evan Bottcher) — What I Talk About When I Talk About Platforms","url":"https://martinfowler.com/articles/talk-about-platforms.html","lang":"en"}],"rule":"Utility — покупай и подстраивай процесс; strategic — строй сам. Своё решение без владельца хуже чужого."},{"id":"linq-deferred-execution","title":"Отложенное выполнение LINQ","sources":[{"level":"beginner","title":"Microsoft Learn — LINQ в C#","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/linq/","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Introduction to LINQ queries","url":"https://learn.microsoft.com/en-us/dotnet/csharp/linq/get-started/introduction-to-linq-queries","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Deferred execution and lazy evaluation","url":"https://learn.microsoft.com/en-us/dotnet/standard/linq/deferred-execution-lazy-evaluation","lang":"en"}],"rule":"LINQ-запрос — рецепт, а не результат: он выполняется при каждом переборе. Нужны данные несколько раз — материализуй один раз через ToList()."},{"id":"linq-iqueryable","title":"IEnumerable и IQueryable","sources":[{"level":"beginner","title":"Microsoft Learn — Как работают запросы EF Core","url":"https://learn.microsoft.com/ru-ru/ef/core/querying/how-query-works","lang":"ru"},{"level":"normal","title":"EF Core — Client vs. server evaluation","url":"https://learn.microsoft.com/en-us/ef/core/querying/client-eval","lang":"en"},{"level":"advanced","title":"IQueryable<T> interface","url":"https://learn.microsoft.com/en-us/dotnet/api/system.linq.iqueryable-1","lang":"en"}],"rule":"IEnumerable фильтрует в памяти делегатами, IQueryable строит дерево выражений и отдаёт его в БД как SQL. Ранний ToList() — и всё остальное уже в памяти."},{"id":"dotnet-collections","title":"Коллекции .NET и их сложность","sources":[{"level":"beginner","title":"Microsoft Learn — Коллекции и структуры данных","url":"https://learn.microsoft.com/ru-ru/dotnet/standard/collections/","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Collections and data structures (complexity)","url":"https://learn.microsoft.com/en-us/dotnet/standard/collections/","lang":"en"},{"level":"advanced","title":"FrozenDictionary<TKey,TValue>","url":"https://learn.microsoft.com/en-us/dotnet/api/system.collections.frozen.frozendictionary-2","lang":"en"}],"rule":"По индексу — List (O(1)), по ключу — Dictionary, уникальные значения — HashSet (O(1) в среднем). Contains у List — O(n)."},{"id":"cs-generic-constraints","title":"Дженерики и ограничения where","sources":[{"level":"beginner","title":"Microsoft Learn — Универсальные типы в C#","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/fundamentals/types/generics","lang":"ru"},{"level":"normal","title":"Constraints on type parameters","url":"https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters","lang":"en"},{"level":"advanced","title":"Generics in .NET","url":"https://learn.microsoft.com/en-us/dotnet/standard/generics/","lang":"en"}],"rule":"Без where у T есть только методы object. Ограничение — обещание компилятору: where T : IComparable<T> — значит, CompareTo можно вызывать."},{"id":"cs-variance","title":"Ковариантность и контравариантность","sources":[{"level":"beginner","title":"Microsoft Learn — Ковариация и контравариация","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/programming-guide/concepts/covariance-contravariance/","lang":"ru"},{"level":"normal","title":"Variance in generic interfaces (C#)","url":"https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/variance-in-generic-interfaces","lang":"en"},{"level":"advanced","title":"Covariance and contravariance in generics","url":"https://learn.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance","lang":"en"}],"rule":"out — только отдаёт: IEnumerable<Cat> → IEnumerable<Animal>. in — только принимает: Action<Animal> → Action<Cat>. Value types не участвуют."},{"id":"linq-extension-members","title":"Методы и члены расширения","sources":[{"level":"beginner","title":"Microsoft Learn — Члены расширения","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/programming-guide/classes-and-structs/extension-methods","lang":"ru"},{"level":"normal","title":"Extension members (C#)","url":"https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods","lang":"en"},{"level":"advanced","title":"What's new in C# 14","url":"https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14","lang":"en"}],"rule":"LINQ — это методы расширения в Enumerable и Queryable. C# 14 добавил блоки extension: теперь можно расширять типы ещё и свойствами и static-членами."},{"id":"db-relationships","title":"Связи 1:1, 1:N, N:M в SQL и EF Core","sources":[{"level":"beginner","title":"Microsoft Learn — Общие сведения о связях в EF Core","url":"https://learn.microsoft.com/ru-ru/ef/core/modeling/relationships","lang":"ru"},{"level":"normal","title":"EF Core — One-to-many relationships","url":"https://learn.microsoft.com/en-us/ef/core/modeling/relationships/one-to-many","lang":"en"},{"level":"advanced","title":"EF Core — Many-to-many relationships","url":"https://learn.microsoft.com/en-us/ef/core/modeling/relationships/many-to-many","lang":"en"}],"rule":"FK живёт на стороне «многих». 1:1 — уникальный FK у зависимой таблицы. N:M — всегда таблица связей с двумя FK."},{"id":"db-isolation-levels","title":"Транзакции и уровни изоляции","sources":[{"level":"beginner","title":"Microsoft Learn — SET TRANSACTION ISOLATION LEVEL","url":"https://learn.microsoft.com/ru-ru/sql/t-sql/statements/set-transaction-isolation-level-transact-sql","lang":"ru"},{"level":"normal","title":"EF Core — Using transactions","url":"https://learn.microsoft.com/en-us/ef/core/saving/transactions","lang":"en"},{"level":"advanced","title":"SQL Server — Transaction locking and row versioning guide","url":"https://learn.microsoft.com/en-us/sql/relational-databases/sql-server-transaction-locking-and-row-versioning-guide","lang":"en"}],"rule":"Выше изоляция — меньше аномалий, больше блокировок. READ COMMITTED убирает грязное чтение, REPEATABLE READ — неповторяемое, SERIALIZABLE — фантомы."},{"id":"db-indexes","title":"Индексы: составные и покрывающие","sources":[{"level":"beginner","title":"Microsoft Learn — Индексы","url":"https://learn.microsoft.com/ru-ru/sql/relational-databases/indexes/indexes","lang":"ru"},{"level":"normal","title":"Use The Index, Luke — Concatenated indexes","url":"https://use-the-index-luke.com/sql/where-clause/the-equals-operator/concatenated-keys","lang":"en"},{"level":"advanced","title":"SQL Server index architecture and design guide","url":"https://learn.microsoft.com/en-us/sql/relational-databases/sql-server-index-design-guide","lang":"en"}],"rule":"Составной индекс — как телефонная книга: сначала фамилия, потом имя. Равенство — первым, диапазон — последним. INCLUDE убирает Key Lookup."},{"id":"db-query-tuning","title":"Медленные запросы и план выполнения","sources":[{"level":"beginner","title":"Use The Index, Luke — SQL indexing for developers","url":"https://use-the-index-luke.com/","lang":"en"},{"level":"normal","title":"SQL Server — Display an actual execution plan","url":"https://learn.microsoft.com/en-us/sql/relational-databases/performance/display-an-actual-execution-plan","lang":"en"},{"level":"advanced","title":"EF Core — Efficient querying","url":"https://learn.microsoft.com/en-us/ef/core/performance/efficient-querying","lang":"en"}],"rule":"Сначала план, потом выводы: Scan вместо Seek, Key Lookup, разрыв оценки и факта. Функция над колонкой в WHERE ломает Seek."},{"id":"crypto-algorithms","title":"AES, RSA, ECC","sources":[{"level":"beginner","title":"Microsoft Learn — Службы криптографии","url":"https://learn.microsoft.com/ru-ru/dotnet/standard/security/cryptographic-services","lang":"ru"},{"level":"normal","title":".NET cryptography model","url":"https://learn.microsoft.com/en-us/dotnet/standard/security/cryptography-model","lang":"en"},{"level":"advanced","title":"OWASP — Cryptographic Storage Cheat Sheet","url":"https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html","lang":"en"}],"rule":"AES — один ключ, быстро, для данных. RSA/ECC — пара ключей, медленно, для обмена ключом и подписи. ECC 256 ≈ RSA 3072 по стойкости."},{"id":"password-hashing","title":"Хеширование и хранение паролей","sources":[{"level":"beginner","title":"ASP.NET Core — Hash passwords","url":"https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/consumer-apis/password-hashing","lang":"en"},{"level":"normal","title":"OWASP — Password Storage Cheat Sheet","url":"https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html","lang":"en"},{"level":"advanced","title":"RFC 9106 — Argon2","url":"https://www.rfc-editor.org/rfc/rfc9106.html","lang":"en"}],"rule":"Шифрование обратимо, хеш — нет. Пароль — только медленным хешем с солью: Argon2id, bcrypt, PBKDF2. Не MD5 и не голый SHA-256."},{"id":"aspnet-data-protection","title":"ASP.NET Core Data Protection","sources":[{"level":"beginner","title":"Microsoft Learn — Защита данных в ASP.NET Core","url":"https://learn.microsoft.com/ru-ru/aspnet/core/security/data-protection/introduction","lang":"ru"},{"level":"normal","title":"Data Protection — Consumer APIs overview","url":"https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/consumer-apis/overview","lang":"en"},{"level":"advanced","title":"Data Protection — Key management and lifetime","url":"https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/default-settings","lang":"en"}],"rule":"Data Protection — для коротко живущих данных: куки, токены. Ключи ротируются раз в 90 дней; на нескольких инстансах key ring должен быть общим."},{"id":"owasp-top10","title":"OWASP Top 10:2025","sources":[{"level":"beginner","title":"OWASP Top 10:2025","url":"https://top10.owasp.org/2025/","lang":"en"},{"level":"normal","title":"A01:2025 — Broken Access Control","url":"https://top10.owasp.org/2025/A01_2025-Broken_Access_Control/","lang":"en"},{"level":"advanced","title":"OWASP Top 10 Proactive Controls","url":"https://owasp.org/www-project-proactive-controls/","lang":"en"}],"rule":"Top 10:2025 открывает A01 Broken Access Control. Новые — A03 Supply Chain и A10 обработка исключений. Права проверяй на каждый объект на сервере."},{"id":"secure-sdlc","title":"Secure SDLC и least privilege","sources":[{"level":"beginner","title":"Microsoft Security Development Lifecycle","url":"https://www.microsoft.com/en-us/securityengineering/sdl","lang":"en"},{"level":"normal","title":"Microsoft SDL — Practices","url":"https://www.microsoft.com/en-us/securityengineering/sdl/practices","lang":"en"},{"level":"advanced","title":"OWASP SAMM","url":"https://owasp.org/www-project-samm/","lang":"en"}],"rule":"Безопасность — на каждом шаге: threat modeling на дизайне, проверки в CI, минимум прав в рантайме. Меньше прав — меньше ущерб при взломе."},{"id":"repository-uow","title":"Repository и Unit of Work","sources":[{"level":"beginner","title":"Martin Fowler — Repository","url":"https://martinfowler.com/eaaCatalog/repository.html","lang":"en"},{"level":"normal","title":"Microsoft Learn — Проектирование уровня сохраняемости инфраструктуры","url":"https://learn.microsoft.com/ru-ru/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-design","lang":"ru"},{"level":"advanced","title":"DbContext class (EF Core)","url":"https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext","lang":"en"}],"rule":"DbContext уже Unit of Work, а DbSet — репозиторий. Свой Repository поверх EF — ради языка домена и границ агрегата, а не «по шаблону»."},{"id":"cqrs","title":"CQRS","sources":[{"level":"beginner","title":"Azure Architecture Center — Шаблон CQRS","url":"https://learn.microsoft.com/ru-ru/azure/architecture/patterns/cqrs","lang":"ru"},{"level":"normal","title":"Martin Fowler — CQRS","url":"https://martinfowler.com/bliki/CQRS.html","lang":"en"},{"level":"advanced","title":"microservices.io — CQRS","url":"https://microservices.io/patterns/data/cqrs.html","lang":"en"}],"rule":"Команды меняют и ничего не возвращают, запросы читают и ничего не меняют. Разные модели — только когда чтение и запись реально разные."},{"id":"event-sourcing","title":"Event Sourcing","sources":[{"level":"beginner","title":"Azure Architecture Center — Шаблон Event Sourcing","url":"https://learn.microsoft.com/ru-ru/azure/architecture/patterns/event-sourcing","lang":"ru"},{"level":"normal","title":"Martin Fowler — Event Sourcing","url":"https://martinfowler.com/eaaDev/EventSourcing.html","lang":"en"},{"level":"advanced","title":"microservices.io — Event sourcing","url":"https://microservices.io/patterns/data/event-sourcing.html","lang":"en"}],"rule":"Храним не состояние, а цепочку событий; состояние — их свёртка. События не правят, а добавляют новые. Snapshot — только ускорение."},{"id":"monolith-vs-microservices","title":"Монолит, модульный монолит, микросервисы","sources":[{"level":"beginner","title":"microservices.io — Monolithic architecture","url":"https://microservices.io/patterns/monolithic.html","lang":"en"},{"level":"normal","title":"Martin Fowler, James Lewis — Microservices","url":"https://martinfowler.com/articles/microservices.html","lang":"en"},{"level":"advanced","title":"Martin Fowler — MonolithFirst","url":"https://martinfowler.com/bliki/MonolithFirst.html","lang":"en"}],"rule":"Микросервисы покупают независимый деплой ценой распределённой сложности. Начинай с модульного монолита: сначала границы модулей, потом сеть."},{"id":"cache-aside","title":"Cache-aside и инвалидация","sources":[{"level":"beginner","title":"Azure Architecture Center — Шаблон Cache-Aside","url":"https://learn.microsoft.com/ru-ru/azure/architecture/patterns/cache-aside","lang":"ru"},{"level":"normal","title":"Redis — Cache invalidation","url":"https://redis.io/glossary/cache-invalidation/","lang":"en"},{"level":"advanced","title":"Azure Architecture Center — Cache-Aside pattern","url":"https://learn.microsoft.com/en-us/azure/architecture/patterns/cache-aside","lang":"en"}],"rule":"Чтение: кеш → промах → БД → положить в кеш с TTL. Запись: сначала БД, потом удалить ключ. Кеш не источник истины."},{"id":"aspnet-caching-abstractions","title":"IMemoryCache, IDistributedCache, HybridCache","sources":[{"level":"beginner","title":"Microsoft Learn — Общие сведения о кешировании в ASP.NET Core","url":"https://learn.microsoft.com/ru-ru/aspnet/core/performance/caching/overview","lang":"ru"},{"level":"normal","title":"Distributed caching in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/performance/caching/distributed","lang":"en"},{"level":"advanced","title":"HybridCache library in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/performance/caching/hybrid","lang":"en"}],"rule":"IMemoryCache — память одного процесса, IDistributedCache — общий кеш в байтах (Redis), HybridCache — оба уровня плюс защита от stampede."},{"id":"message-brokers","title":"RabbitMQ и Kafka","sources":[{"level":"beginner","title":".NET microservices — Asynchronous message-based communication","url":"https://learn.microsoft.com/en-us/dotnet/architecture/microservices/architect-microservice-container-applications/asynchronous-message-based-communication","lang":"en"},{"level":"normal","title":"RabbitMQ — AMQP 0-9-1 model explained","url":"https://www.rabbitmq.com/tutorials/amqp-concepts","lang":"en"},{"level":"advanced","title":"Apache Kafka — Introduction","url":"https://kafka.apache.org/intro","lang":"en"}],"rule":"RabbitMQ — очередь: маршрутизирует и удаляет сообщение после ack. Kafka — лог: хранит события, порядок — внутри партиции, читатель сам двигает offset."},{"id":"grpc-dotnet","title":"gRPC в .NET","sources":[{"level":"beginner","title":"Microsoft Learn — Общие сведения о gRPC в .NET","url":"https://learn.microsoft.com/ru-ru/aspnet/core/grpc/","lang":"ru"},{"level":"normal","title":"Compare gRPC services with HTTP APIs","url":"https://learn.microsoft.com/en-us/aspnet/core/grpc/comparison","lang":"en"},{"level":"advanced","title":"Reliable gRPC services with deadlines and cancellation","url":"https://learn.microsoft.com/en-us/aspnet/core/grpc/deadlines-cancellation","lang":"en"}],"rule":"gRPC = контракт .proto + Protobuf + HTTP/2: быстро и строго между сервисами. Браузеру нужен gRPC-Web. Всегда задавай deadline."},{"id":"graphql-server","title":"GraphQL на сервере","sources":[{"level":"beginner","title":"GraphQL — Learn","url":"https://graphql.org/learn/","lang":"en"},{"level":"normal","title":"GraphQL — Performance","url":"https://graphql.org/learn/performance/","lang":"en"},{"level":"advanced","title":"Hot Chocolate v15 documentation","url":"https://chillicream.com/docs/hotchocolate/v15","lang":"en"}],"rule":"Один endpoint, клиент выбирает поля. Цена — N+1 в резолверах (лечит DataLoader) и тяжёлые запросы (лимит глубины и сложности)."},{"id":"api-versioning","title":"Версионирование API","sources":[{"level":"beginner","title":"Azure Architecture Center — Web API design best practices","url":"https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design","lang":"en"},{"level":"normal","title":"dotnet/aspnet-api-versioning (Asp.Versioning)","url":"https://github.com/dotnet/aspnet-api-versioning","lang":"en"},{"level":"advanced","title":"Microsoft Azure REST API Guidelines","url":"https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md","lang":"en"}],"rule":"Ломающее изменение — новая версия, новое необязательное поле — нет. Версия в URI, query, заголовке или media type; старую выключают по графику."},{"id":"testcontainers","title":"Testcontainers","sources":[{"level":"beginner","title":"Getting started with Testcontainers for .NET","url":"https://testcontainers.com/guides/getting-started-with-testcontainers-for-dotnet/","lang":"en"},{"level":"normal","title":"Testcontainers for .NET","url":"https://dotnet.testcontainers.org/","lang":"en"},{"level":"advanced","title":"EF Core — Choosing a testing strategy","url":"https://learn.microsoft.com/en-us/ef/core/testing/choosing-a-testing-strategy","lang":"en"}],"rule":"Тестируй на настоящей БД в Docker, а не на InMemory: InMemory — не SQL. Контейнер — один на набор тестов, данные чистим между тестами."},{"id":"load-testing","title":"Нагрузочное тестирование (k6)","sources":[{"level":"beginner","title":"k6 — Write your first test","url":"https://grafana.com/docs/k6/latest/get-started/write-your-first-test/","lang":"en"},{"level":"normal","title":"k6 — Load test types","url":"https://grafana.com/docs/k6/latest/testing-guides/test-types/","lang":"en"},{"level":"advanced","title":"k6 — Thresholds","url":"https://grafana.com/docs/k6/latest/using-k6/thresholds/","lang":"en"}],"rule":"Smoke — работает ли, load — обычная нагрузка, stress — выше нормы, spike — всплеск, soak — долго. Успех — пороги по p95, а не среднее."},{"id":"mutation-testing","title":"Мутационное тестирование","sources":[{"level":"beginner","title":"Stryker Mutator — Introduction","url":"https://stryker-mutator.io/docs/","lang":"en"},{"level":"normal","title":"Stryker.NET — Introduction","url":"https://stryker-mutator.io/docs/stryker-net/introduction/","lang":"en"},{"level":"advanced","title":"Mutant states and metrics","url":"https://stryker-mutator.io/docs/mutation-testing-elements/mutant-states-and-metrics/","lang":"en"}],"rule":"Мутант — испорченный код. Тесты упали — мутант убит; прошли — выжил, значит тест не проверяет поведение. Покрытие строк этого не видит."},{"id":"ci-cd-pipelines","title":"CI/CD и pipeline","sources":[{"level":"beginner","title":"Microsoft Learn — Ключевые понятия Azure Pipelines","url":"https://learn.microsoft.com/ru-ru/azure/devops/pipelines/get-started/key-pipelines-concepts","lang":"ru"},{"level":"normal","title":"GitHub Docs — Understanding GitHub Actions","url":"https://docs.github.com/en/actions/get-started/understand-github-actions","lang":"en"},{"level":"advanced","title":"Martin Fowler — Continuous Delivery","url":"https://martinfowler.com/bliki/ContinuousDelivery.html","lang":"en"}],"rule":"CI — каждый коммит собран и протестирован. Continuous Delivery — готов к релизу по кнопке. Continuous Deployment — уезжает в прод сам."},{"id":"docker-compose","title":"Docker Compose","sources":[{"level":"beginner","title":"Docker Compose overview","url":"https://docs.docker.com/compose/","lang":"en"},{"level":"normal","title":"How Compose works","url":"https://docs.docker.com/compose/intro/compose-application-model/","lang":"en"},{"level":"advanced","title":"Control startup order in Compose","url":"https://docs.docker.com/compose/how-tos/startup-order/","lang":"en"}],"rule":"Compose описывает весь стенд в одном YAML. depends_on ждёт запуска, а не готовности: для готовности нужен healthcheck и service_healthy."},{"id":"k8s-basics","title":"Kubernetes: базовые объекты","sources":[{"level":"beginner","title":"Kubernetes — Что такое Kubernetes","url":"https://kubernetes.io/ru/docs/concepts/overview/what-is-kubernetes/","lang":"ru"},{"level":"normal","title":"Kubernetes — Pods","url":"https://kubernetes.io/docs/concepts/workloads/pods/","lang":"en"},{"level":"advanced","title":"Kubernetes — Service","url":"https://kubernetes.io/docs/concepts/services-networking/service/","lang":"en"}],"rule":"Pod — запущенные контейнеры, Deployment держит N копий и обновляет их, Service — стабильный адрес. Конфиг — ConfigMap, секреты — Secret."},{"id":"health-checks","title":"Health checks: liveness и readiness","sources":[{"level":"beginner","title":"Microsoft Learn — Проверки работоспособности в ASP.NET Core","url":"https://learn.microsoft.com/ru-ru/aspnet/core/host-and-deploy/health-checks","lang":"ru"},{"level":"normal","title":"Health checks in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks","lang":"en"},{"level":"advanced","title":"Kubernetes — Liveness, readiness and startup probes","url":"https://kubernetes.io/docs/concepts/configuration/liveness-readiness-startup-probes/","lang":"en"}],"rule":"Liveness: «жив?» — нет → рестарт. Readiness: «готов к трафику?» — нет → трафик не шлют. Внешние зависимости проверяй только в readiness."},{"id":"deployment-strategies","title":"Rolling, blue-green, canary","sources":[{"level":"beginner","title":"Martin Fowler — BlueGreenDeployment","url":"https://martinfowler.com/bliki/BlueGreenDeployment.html","lang":"en"},{"level":"normal","title":"Martin Fowler (Danilo Sato) — CanaryRelease","url":"https://martinfowler.com/bliki/CanaryRelease.html","lang":"en"},{"level":"advanced","title":"Azure Well-Architected — Safe deployment practices","url":"https://learn.microsoft.com/en-us/azure/well-architected/operational-excellence/safe-deployments","lang":"en"}],"rule":"Rolling — меняем Pod'ы по одному. Blue-green — две среды, трафик переключаем целиком. Canary — сначала малая доля пользователей."},{"id":"cloud-basics","title":"Облако для бэкенда","sources":[{"level":"beginner","title":"Microsoft Learn — Управляемые удостоверения для ресурсов Azure","url":"https://learn.microsoft.com/ru-ru/entra/identity/managed-identities-azure-resources/overview","lang":"ru"},{"level":"normal","title":"Azure Architecture Center — Choose an Azure compute service","url":"https://learn.microsoft.com/en-us/azure/architecture/guide/technology-choices/compute-decision-tree","lang":"en"},{"level":"advanced","title":"AWS — Overview of Amazon Web Services","url":"https://docs.aws.amazon.com/whitepapers/latest/aws-overview/introduction.html","lang":"en"}],"rule":"Облако — это управляемые сервисы: меньше своей инфраструктуры, больше работы с правами. В ресурсы ходим через Managed Identity, а не по паролю."},{"id":"decision-records","title":"RFC, ADR и права на решение","sources":[{"level":"beginner","title":"Architectural Decision Records (ADRs)","url":"https://adr.github.io/","lang":"en"},{"level":"normal","title":"Michael Nygard — Documenting Architecture Decisions","url":"https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions","lang":"en"},{"level":"advanced","title":"The Pragmatic Engineer — Companies Using RFCs or Design Docs","url":"https://blog.pragmaticengineer.com/rfcs-and-design-docs/","lang":"en"}],"rule":"RFC — до решения, чтобы собрать мнения; ADR — после, чтобы помнить почему. Дверь двусторонняя — решай быстро."},{"id":"engineering-metrics","title":"Инженерные метрики (DORA, SPACE) и их злоупотребление","sources":[{"level":"beginner","title":"DORA — software delivery performance metrics","url":"https://dora.dev/guides/dora-metrics/","lang":"en"},{"level":"normal","title":"Martin Fowler — Cannot Measure Productivity","url":"https://martinfowler.com/bliki/CannotMeasureProductivity.html","lang":"en"},{"level":"advanced","title":"Microsoft Research — The SPACE of Developer Productivity","url":"https://www.microsoft.com/en-us/research/publication/the-space-of-developer-productivity-theres-more-to-it-than-you-think/","lang":"en"}],"rule":"Метрика — для команды, а не для рейтинга людей. Одна цифра врёт: смотри скорость и стабильность вместе."},{"id":"engineering-strategy","title":"Инженерная стратегия и техническое видение","sources":[{"level":"beginner","title":"StaffEng — Writing engineering strategy","url":"https://staffeng.com/guides/engineering-strategy/","lang":"en"},{"level":"normal","title":"Will Larson — Writing an engineering strategy","url":"https://lethain.com/eng-strategies/","lang":"en"},{"level":"advanced","title":"StaffEng — Work on what matters","url":"https://staffeng.com/guides/work-on-what-matters/","lang":"en"}],"rule":"Стратегия — обобщение 5 реальных решений; видение — куда они приведут через 2 года. Нет спорных решений — нет стратегии."},{"id":"hiring-leveling","title":"Процесс найма, уровни и калибровка","sources":[{"level":"beginner","title":"Google re:Work — A guide to structured interviewing","url":"https://rework.withgoogle.com/intl/en/guides/a-guide-to-structured-interviewing-for-better-hiring-practices","lang":"en"},{"level":"normal","title":"Will Larson — Your hiring funnel","url":"https://lethain.com/hiring-funnel/","lang":"en"},{"level":"advanced","title":"Dropbox Engineering Career Framework","url":"https://dropbox.github.io/dbx-career-framework/","lang":"en"}],"rule":"Одни вопросы, одна рубрика, независимые оценки до обсуждения. Воронка: найти → заинтересовать → оценить → закрыть."},{"id":"incident-command","title":"Командование инцидентом и коммуникация","sources":[{"level":"beginner","title":"PagerDuty Incident Response Documentation","url":"https://response.pagerduty.com/","lang":"en"},{"level":"normal","title":"PagerDuty — Incident Commander training","url":"https://response.pagerduty.com/training/incident_commander/","lang":"en"},{"level":"advanced","title":"Google SRE Book — Managing Incidents","url":"https://sre.google/sre-book/managing-incidents/","lang":"en"}],"rule":"Incident Commander командует, а не чинит. Сначала восстановить сервис, потом искать причину. Апдейты — по расписанию."},{"id":"performance-conversations","title":"Разговоры о результатах, рост и калибровка","sources":[{"level":"beginner","title":"Dropbox Career Framework — Promotion principles","url":"https://dropbox.github.io/dbx-career-framework/promotion_principles.html","lang":"en"},{"level":"normal","title":"GitLab Handbook — Underperformance","url":"https://handbook.gitlab.com/handbook/leadership/underperformance/","lang":"en"},{"level":"advanced","title":"Will Larson — Designations, levels and calibrations","url":"https://lethain.com/perf-management-system/","lang":"en"}],"rule":"Ожидания — заранее и письменно, отзыв — сразу и с фактами. Оценка в ревью не должна быть сюрпризом."},{"id":"ssdlc-culture","title":"Культура безопасной разработки (SSDLC)","sources":[{"level":"beginner","title":"OWASP Security Champions Guide","url":"https://securitychampions.owasp.org/","lang":"en"},{"level":"normal","title":"Microsoft Security Development Lifecycle","url":"https://www.microsoft.com/en-us/securityengineering/sdl","lang":"en"},{"level":"advanced","title":"NIST — Secure Software Development Framework","url":"https://csrc.nist.gov/projects/ssdf","lang":"en"}],"rule":"Безопасность — шаг процесса, а не проверка в конце: угрозы на дизайне, проверки в CI, champion в каждой команде."},{"id":"tech-debt-strategy","title":"Стратегия работы с техническим долгом","sources":[{"level":"beginner","title":"Martin Fowler — Technical Debt","url":"https://martinfowler.com/bliki/TechnicalDebt.html","lang":"en"},{"level":"normal","title":"Martin Fowler — Technical Debt Quadrant","url":"https://martinfowler.com/bliki/TechnicalDebtQuadrant.html","lang":"en"},{"level":"advanced","title":"StaffEng — Manage technical quality","url":"https://staffeng.com/guides/manage-technical-quality/","lang":"en"}],"rule":"Долг — это проценты: плати там, где код часто меняют. Не «спринт рефакторинга», а постоянная доля и метрика."},{"id":"org-design","title":"Дизайн организации — закон Конвея и Team Topologies","sources":[{"level":"beginner","title":"Martin Fowler — Conway's Law","url":"https://martinfowler.com/bliki/ConwaysLaw.html","lang":"en"},{"level":"normal","title":"Team Topologies — Key concepts","url":"https://teamtopologies.com/key-concepts","lang":"en"},{"level":"advanced","title":"Will Larson — Sizing engineering teams","url":"https://lethain.com/sizing-engineering-teams/","lang":"en"}],"rule":"Система копирует структуру общения команд. Хочешь другую архитектуру — сначала поменяй команды (Inverse Conway)."},{"id":"finops","title":"FinOps — стоимость облака как инженерная ответственность","sources":[{"level":"beginner","title":"FinOps Foundation — What is FinOps?","url":"https://www.finops.org/introduction/what-is-finops/","lang":"en"},{"level":"normal","title":"FinOps Principles","url":"https://www.finops.org/framework/principles/","lang":"en"},{"level":"advanced","title":"AWS Well-Architected — Cost Optimization Pillar","url":"https://docs.aws.amazon.com/wellarchitected/latest/cost-optimization-pillar/welcome.html","lang":"en"}],"rule":"Кто тратит — тот и отвечает за стоимость. Считай цену на клиента (unit economics), а не общий счёт."},{"id":"cs-declarations","title":"var, const и readonly","sources":[{"level":"beginner","title":"Microsoft Learn — Операторы объявления (var)","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/language-reference/statements/declarations","lang":"ru"},{"level":"normal","title":"Microsoft Learn — The const keyword","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/const","lang":"en"},{"level":"advanced","title":"Microsoft Learn — readonly keyword","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/readonly","lang":"en"}],"rule":"var — тип выводит компилятор, но он строгий, не dynamic. const — значение при компиляции, readonly — присваивается один раз: в объявлении или конструкторе."},{"id":"cs-loops","title":"Циклы и перебор коллекций","sources":[{"level":"beginner","title":"Microsoft Learn — Операторы итерации: for, foreach, do, while","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/language-reference/statements/iteration-statements","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Iteration statements","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/iteration-statements","lang":"en"},{"level":"advanced","title":"Microsoft Learn — List<T>.GetEnumerator","url":"https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.getenumerator","lang":"en"}],"rule":"foreach — чтобы читать коллекцию. Изменил List внутри foreach — получишь InvalidOperationException. Удаляй через RemoveAll или for с конца."},{"id":"cs-casting","title":"Проверка и приведение типов","sources":[{"level":"beginner","title":"Microsoft Learn — Операторы проверки типов и выражения приведения","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/language-reference/operators/type-testing-and-cast","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Type-testing operators and cast expressions","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/type-testing-and-cast","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Pattern matching: is and switch","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns","lang":"en"}],"rule":"(T)x — бросит исключение, as — вернёт null, is T t — проверит и сразу объявит переменную. По умолчанию бери is T t."},{"id":"cs-classes-objects","title":"Классы, объекты и static","sources":[{"level":"beginner","title":"Microsoft Learn — Классы в C#","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/fundamentals/types/classes","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Instance constructors","url":"https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/instance-constructors","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Static classes and static class members","url":"https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members","lang":"en"}],"rule":"Класс — чертёж, объект — дом по этому чертежу (new). static — одна вещь на весь тип, а не своя у каждого объекта."},{"id":"cs-properties","title":"Свойства и поля","sources":[{"level":"beginner","title":"Microsoft Learn — Свойства (C#)","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/programming-guide/classes-and-structs/properties","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Properties","url":"https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties","lang":"en"},{"level":"advanced","title":"Microsoft Learn — The field contextual keyword (C# 14)","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/field","lang":"en"}],"rule":"Поле — сами данные, свойство — дверь к ним через get и set. Наружу открывай свойства, поля держи private."},{"id":"cs-inheritance","title":"Наследование, sealed и цепочки конструкторов","sources":[{"level":"beginner","title":"Microsoft Learn — Наследование в C#","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/fundamentals/object-oriented/inheritance","lang":"ru"},{"level":"normal","title":"Microsoft Learn — sealed modifier","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/sealed","lang":"en"},{"level":"advanced","title":"Microsoft Learn — override modifier","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/override","lang":"en"}],"rule":"Базовый класс — один, интерфейсов — сколько угодно. sealed закрывает наследование, base(...) зовёт конструктор родителя, this(...) — соседний."},{"id":"cs-equality","title":"Равенство: ==, Equals и GetHashCode","sources":[{"level":"beginner","title":"Microsoft Learn — Сравнение на равенство в C#","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/fundamentals/expressions/equality","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Equality comparisons","url":"https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/expressions/equality","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Object.GetHashCode","url":"https://learn.microsoft.com/en-us/dotnet/api/system.object.gethashcode","lang":"en"}],"rule":"Объект класса по умолчанию равен только самому себе (ссылка). Переопределил Equals — переопредели GetHashCode, иначе Dictionary не найдёт ключ."},{"id":"dotnet-namespaces","title":"Namespace и директивы using","sources":[{"level":"beginner","title":"Microsoft Learn — Пространства имён и директивы using","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/fundamentals/program-structure/namespaces","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Namespaces and using directives","url":"https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/namespaces","lang":"en"},{"level":"advanced","title":"Microsoft Learn — .NET project SDK overview (implicit usings)","url":"https://learn.microsoft.com/en-us/dotnet/core/project-sdk/overview","lang":"en"}],"rule":"Namespace — фамилия типа, using — разрешение звать тип по имени. ImplicitUsings в SDK-проекте подключает частые namespace сам."},{"id":"dotnet-build-configurations","title":"Конфигурации Debug и Release","sources":[{"level":"beginner","title":"Microsoft Learn — Настройка конфигураций отладки и выпуска","url":"https://learn.microsoft.com/ru-ru/visualstudio/debugger/how-to-set-debug-and-release-configurations","lang":"ru"},{"level":"normal","title":"Microsoft Learn — Set debug and release configurations","url":"https://learn.microsoft.com/en-us/visualstudio/debugger/how-to-set-debug-and-release-configurations","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Compiler options: code generation (Optimize, DebugType)","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/code-generation","lang":"en"}],"rule":"Debug — для отладки, без оптимизаций. Release — для прода, с оптимизациями. В прод выкладывают только Release."},{"id":"dotnet-dispose","title":"IDisposable и инструкция using","sources":[{"level":"beginner","title":"Microsoft Learn — Оператор using","url":"https://learn.microsoft.com/ru-ru/dotnet/csharp/language-reference/statements/using","lang":"ru"},{"level":"normal","title":"Microsoft Learn — using statement","url":"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/using","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Implement a Dispose method","url":"https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose","lang":"en"}],"rule":"GC убирает память, но не файлы и соединения. Взял такой ресурс — оберни в using: Dispose вызовется даже при исключении."},{"id":"dotnet-flavors","title":".NET Framework, .NET и .NET Standard","sources":[{"level":"beginner","title":"DEV Community — .NET Framework, Core and Standard (interview questions)","url":"https://dev.to/ypedroo/little-drops-on-c-interview-questions-net-framework-core-and-standard-1nm8","lang":"en"},{"level":"normal","title":"Microsoft Learn — .NET Standard","url":"https://learn.microsoft.com/en-us/dotnet/standard/net-standard","lang":"en"},{"level":"advanced","title":"Microsoft Learn — Upgrade .NET apps overview","url":"https://learn.microsoft.com/en-us/dotnet/core/porting/","lang":"en"}],"rule":"Framework — старый и только Windows, .NET — новый и везде, Standard — общий список API для обоих, и новых версий у него не будет."},{"id":"capacity-overload","title":"Capacity planning и защита от перегрузки","sources":[{"level":"beginner","title":"Google SRE Book — Handling Overload","url":"https://sre.google/sre-book/handling-overload/","lang":"en"},{"level":"normal","title":"Google SRE Book — Addressing Cascading Failures","url":"https://sre.google/sre-book/addressing-cascading-failures/","lang":"en"},{"level":"advanced","title":"Google SRE Book — Software Engineering in SRE (intent-based capacity planning)","url":"https://sre.google/sre-book/software-engineering-in-sre/","lang":"en"}],"rule":"Планируй с запасом N+2 и тестируй до отказа. При перегрузке отказывай быстро (503), а не копи очередь; ретраи — только с бюджетом."},{"id":"k8s-operators","title":"Kubernetes operators","sources":[{"level":"beginner","title":"TechTarget — When to use Kubernetes operators vs. Helm charts","url":"https://www.techtarget.com/searchitoperations/tip/When-to-use-Kubernetes-operators-vs-Helm-charts","lang":"en"},{"level":"normal","title":"Kubernetes docs — Operator pattern","url":"https://kubernetes.io/docs/concepts/extend-kubernetes/operator/","lang":"en"},{"level":"advanced","title":"Kubernetes docs — Custom Resources","url":"https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/","lang":"en"}],"rule":"Operator = custom resource + контроллер, который в цикле чинит состояние. Сначала ищи готовый: свой operator — код, которым владеешь навсегда."},{"id":"multi-region-data","title":"Данные в нескольких регионах","sources":[{"level":"beginner","title":"AWS — Testing disaster recovery","url":"https://docs.aws.amazon.com/whitepapers/latest/disaster-recovery-workloads-on-aws/testing-disaster-recovery.html","lang":"en"},{"level":"normal","title":"AWS Multi-Region Fundamentals — Understanding the data","url":"https://docs.aws.amazon.com/prescriptive-guidance/latest/aws-multi-region-fundamentals/fundamental-2.html","lang":"en"},{"level":"advanced","title":"AWS — Availability and Beyond: CAP theorem","url":"https://docs.aws.amazon.com/whitepapers/latest/availability-and-beyond-improving-resilience/cap-theorem.html","lang":"en"}],"rule":"Синхронная репликация — согласованно, но медленно; асинхронная — быстро, но RPO больше нуля. Всё сразу не получить — это CAP."},{"id":"event-contracts","title":"Контракты событий и эволюция схем","sources":[{"level":"beginner","title":"Martin Fowler — Tolerant Reader","url":"https://martinfowler.com/bliki/TolerantReader.html","lang":"en"},{"level":"normal","title":"martinfowler.com — Consumer-Driven Contracts","url":"https://martinfowler.com/articles/consumerDrivenContracts.html","lang":"en"},{"level":"advanced","title":"Confluent — Schema evolution and compatibility","url":"https://docs.confluent.io/platform/current/schema-registry/fundamentals/schema-evolution.html","lang":"en"}],"rule":"Читай терпимо (tolerant reader), меняй совместимо (BACKWARD). Ломающее изменение — новая версия события, а не правка старой."},{"id":"observability-strategy","title":"Observability на уровне платформы","sources":[{"level":"beginner","title":"Google SRE Book — Monitoring Distributed Systems","url":"https://sre.google/sre-book/monitoring-distributed-systems/","lang":"en"},{"level":"normal","title":"OpenTelemetry — Sampling","url":"https://opentelemetry.io/docs/concepts/sampling/","lang":"en"},{"level":"advanced","title":"OpenTelemetry — Agent-to-gateway deployment pattern","url":"https://opentelemetry.io/docs/collector/deploy/other/agent-to-gateway/","lang":"en"}],"rule":"Одни имена (semantic conventions), один путь (Collector), будим только по симптомам (golden signals). Дорого — сэмплируй, а не выключай."},{"id":"vuln-management","title":"Приоритизация уязвимостей","sources":[{"level":"beginner","title":"CISA — Known Exploited Vulnerabilities Catalog","url":"https://www.cisa.gov/known-exploited-vulnerabilities-catalog","lang":"en"},{"level":"normal","title":"FIRST — Exploit Prediction Scoring System (EPSS)","url":"https://www.first.org/epss/","lang":"en"},{"level":"advanced","title":"FIRST — EPSS FAQ (EPSS vs CVSS)","url":"https://www.first.org/epss/faq","lang":"en"}],"rule":"CVSS — насколько плохо, EPSS — насколько вероятно, KEV — уже атакуют. Сначала чини KEV и то, что достижимо из кода."},{"id":"strangler-fig","title":"Strangler fig и замена legacy","sources":[{"level":"beginner","title":"Martin Fowler — Strangler Fig","url":"https://martinfowler.com/bliki/StranglerFigApplication.html","lang":"en"},{"level":"normal","title":"Azure Architecture Center — Strangler Fig pattern","url":"https://learn.microsoft.com/en-us/azure/architecture/patterns/strangler-fig","lang":"en"},{"level":"advanced","title":"martinfowler.com — Patterns of Legacy Displacement","url":"https://martinfowler.com/articles/patterns-legacy-displacement/","lang":"en"}],"rule":"Не переписывай всё разом: фасад впереди, кусок за куском в новый код, старое выключай. Временные подпорки — плата за низкий риск."},{"id":"multi-tenancy","title":"Multi-tenant SaaS: изоляция тенантов","sources":[{"level":"beginner","title":"Azure Architecture Center — Tenancy models","url":"https://learn.microsoft.com/en-us/azure/architecture/guide/multitenant/considerations/tenancy-models","lang":"en"},{"level":"normal","title":"Azure Architecture Center — Noisy Neighbor antipattern","url":"https://learn.microsoft.com/en-us/azure/architecture/antipatterns/noisy-neighbor/noisy-neighbor","lang":"en"},{"level":"advanced","title":"Azure Architecture Center — Deployment Stamps pattern","url":"https://learn.microsoft.com/en-us/azure/architecture/patterns/deployment-stamp","lang":"en"}],"rule":"Общая инфраструктура — дёшево, но noisy neighbor и риск утечки; выделенная — изолированно, но дорого. Чаще всего выбирают смесь по тарифам."},{"id":"finops-governance","title":"FinOps и управление облачными затратами","sources":[{"level":"beginner","title":"FinOps Foundation — What is FinOps","url":"https://www.finops.org/introduction/what-is-finops/","lang":"en"},{"level":"normal","title":"FinOps Foundation — FinOps Phases","url":"https://www.finops.org/framework/phases/","lang":"en"},{"level":"advanced","title":"FinOps Foundation — FinOps Principles","url":"https://www.finops.org/framework/principles/","lang":"en"}],"rule":"Inform → Optimize → Operate, и снова по кругу. Каждая команда видит свои затраты и отвечает за них; цель — ценность за деньги, а не минимальный счёт."},{"id":"architecture-governance","title":"Архитектурное управление и техническая стратегия","sources":[{"level":"beginner","title":"martinfowler.com — Scaling the Practice of Architecture, Conversationally","url":"https://martinfowler.com/articles/scaling-architecture-conversationally.html","lang":"en"},{"level":"normal","title":"Thoughtworks Radar — Architectural fitness function","url":"https://www.thoughtworks.com/radar/techniques/architectural-fitness-function","lang":"en"},{"level":"advanced","title":"StaffEng — Writing engineering strategy","url":"https://staffeng.com/guides/engineering-strategy/","lang":"en"}],"rule":"Решает любой, но спрашивает совета и пишет ADR. Правила, важные всем, проверяет CI (fitness functions), а не память архитектора."},{"id":"data-platform-strategy","title":"Data lake, warehouse и data mesh","sources":[{"level":"beginner","title":"Martin Fowler — Data Lake","url":"https://martinfowler.com/bliki/DataLake.html","lang":"en"},{"level":"normal","title":"martinfowler.com — Data Mesh Principles and Logical Architecture","url":"https://martinfowler.com/articles/data-mesh-principles.html","lang":"en"},{"level":"advanced","title":"Debezium — Features (log-based CDC)","url":"https://debezium.io/documentation/reference/stable/features.html","lang":"en"}],"rule":"Lake — сырые данные, warehouse — единая схема, mesh — данные как продукт у доменов. Mesh — для большого масштаба, а не для двух команд."},{"id":"privacy-erasure","title":"Право на удаление персональных данных","sources":[{"level":"beginner","title":"ICO — Right to erasure","url":"https://ico.org.uk/for-organisations/uk-gdpr-guidance-and-resources/individual-rights/individual-rights/right-to-erasure/","lang":"en"},{"level":"normal","title":"GDPR Art. 17 — Right to erasure","url":"https://gdpr-info.eu/art-17-gdpr/","lang":"en"},{"level":"advanced","title":"Wikipedia — Crypto-shredding","url":"https://en.wikipedia.org/wiki/Crypto-shredding","lang":"en"}],"rule":"Знай, где лежат данные, и удаляй везде, включая получателей. Бэкап — вывести из использования, неизменяемый журнал — crypto-shredding."},{"id":"context-mapping","title":"Context map, поддомены и отношения контекстов","sources":[{"level":"beginner","title":"Martin Fowler — Bounded Context","url":"https://martinfowler.com/bliki/BoundedContext.html","lang":"en"},{"level":"normal","title":"Azure Architecture Center — Domain analysis for microservices","url":"https://learn.microsoft.com/en-us/azure/architecture/microservices/model/domain-analysis","lang":"en"},{"level":"advanced","title":"Azure Architecture Center — Anti-corruption Layer pattern","url":"https://learn.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer","lang":"en"}],"rule":"Карта контекстов показывает upstream и downstream. Чужая неудобная модель — ACL, много потребителей — open host service. Core строй сам, generic покупай."},{"id":"team-domain-boundaries","title":"Границы команд и контекстов","sources":[{"level":"beginner","title":"Martin Fowler — Conway's Law","url":"https://martinfowler.com/bliki/ConwaysLaw.html","lang":"en"},{"level":"normal","title":"EventStorming","url":"https://www.eventstorming.com/","lang":"en"},{"level":"advanced","title":"Team Topologies — Key concepts","url":"https://teamtopologies.com/key-concepts","lang":"en"}],"rule":"Система копирует структуру общения команд (закон Конвея). Одна команда — один контекст целиком; границы ищи через EventStorming, а не по таблицам."},{"id":"html-forms","title":"HTML-формы","sources":[{"level":"beginner","title":"MDN — Sending form data","url":"https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Forms/Sending_and_retrieving_form_data","lang":"en"},{"level":"normal","title":"MDN — Client-side form validation","url":"https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Forms/Form_validation","lang":"en"},{"level":"advanced","title":"MDN — <label> element reference","url":"https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/label","lang":"en"}],"rule":"Каждому полю — label (for = id) и name, иначе подписи нет и значение не уйдёт. GET — в URL, POST — в теле. Проверка в браузере — удобство, защита — на сервере."},{"id":"css-units","title":"Единицы измерения в CSS","sources":[{"level":"beginner","title":"MDN — CSS values and units","url":"https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Values_and_units","lang":"en"},{"level":"normal","title":"web.dev — Learn CSS: Sizing units","url":"https://web.dev/learn/css/sizing","lang":"en"},{"level":"advanced","title":"W3C — CSS Values and Units Level 4","url":"https://www.w3.org/TR/css-values-4/","lang":"en"}],"rule":"rem — от корня, не накапливается; em — от родителя, накапливается; % — от другой величины (width — от родителя); vw/vh — 1% окна."},{"id":"css-selectors","title":"Селекторы, псевдоклассы и псевдоэлементы","sources":[{"level":"beginner","title":"MDN — Basic CSS selectors","url":"https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Basic_selectors","lang":"en"},{"level":"normal","title":"MDN — Pseudo-classes and pseudo-elements","url":"https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Pseudo_classes_and_elements","lang":"en"},{"level":"advanced","title":"W3C — Selectors Level 4","url":"https://www.w3.org/TR/selectors-4/","lang":"en"}],"rule":"Одно двоеточие — состояние (:hover), два — новый кусок разметки (::before, нужен content). nth-child считает всех детей, nth-of-type — только свой тег."},{"id":"browser-storage","title":"Cookie, localStorage и sessionStorage","sources":[{"level":"beginner","title":"MDN — Web Storage API","url":"https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API","lang":"en"},{"level":"normal","title":"MDN — Using HTTP cookies","url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies","lang":"en"},{"level":"advanced","title":"MDN — Storage quotas and eviction criteria","url":"https://developer.mozilla.org/en-US/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria","lang":"en"}],"rule":"Cookie летит на сервер с каждым запросом (≈4 КБ). localStorage живёт всегда, sessionStorage — пока открыта вкладка; оба только строки и не уходят на сервер."},{"id":"object-collections","title":"Перебор объектов и Map","sources":[{"level":"beginner","title":"learn.javascript.ru — Object.keys, values, entries","url":"https://learn.javascript.ru/keys-values-entries","lang":"ru"},{"level":"normal","title":"MDN — Map","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map","lang":"en"},{"level":"advanced","title":"MDN — Keyed collections","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Keyed_collections","lang":"en"}],"rule":"Свои ключи объекта — Object.keys/entries; for...in тянет и унаследованные. Ключи любого типа, порядок и частые удаления — Map."},{"id":"rendering-strategies","title":"CSR, SSR, SSG и гидратация","sources":[{"level":"beginner","title":"MDN Glossary — SSR","url":"https://developer.mozilla.org/en-US/docs/Glossary/SSR","lang":"en"},{"level":"normal","title":"web.dev — Rendering on the Web","url":"https://web.dev/articles/rendering-on-the-web","lang":"en"},{"level":"advanced","title":"Angular v21 — Server and hybrid rendering","url":"https://v21.angular.dev/guide/ssr","lang":"en"}],"rule":"Одинаково для всех — SSG, персонально и для SEO — SSR, за логином — CSR. Выбирают по маршруту, а гидратация стоит JS и INP."},{"id":"ng-error-handling","title":"Обработка ошибок в Angular","sources":[{"level":"beginner","title":"MDN — unhandledrejection event","url":"https://developer.mozilla.org/en-US/docs/Web/API/Window/unhandledrejection_event","lang":"en"},{"level":"normal","title":"Angular v21 — Unhandled errors in Angular","url":"https://v21.angular.dev/best-practices/error-handling","lang":"en"},{"level":"advanced","title":"Angular v21 API — ErrorHandler","url":"https://v21.angular.dev/api/core/ErrorHandler","lang":"en"}],"rule":"Ожидаемую ошибку ловят там, где она случилась. Неожиданная уходит в ErrorHandler — он сообщает в мониторинг, а не чинит состояние."},{"id":"ux-feedback","title":"Обратная связь интерфейса и ошибки форм","sources":[{"level":"beginner","title":"NN/g — Response Times: The 3 Important Limits","url":"https://www.nngroup.com/articles/response-times-3-important-limits/","lang":"en"},{"level":"normal","title":"NN/g — Progress Indicators","url":"https://www.nngroup.com/articles/progress-indicators/","lang":"en"},{"level":"advanced","title":"NN/g — Error messages in forms","url":"https://www.nngroup.com/articles/errors-forms-design-guidelines/","lang":"en"}],"rule":"0,1 с — мгновенно, 1 с — мысль не рвётся, 10 с — внимание уходит: дальше прогресс в % и отмена. Ошибку поля показывай рядом и после ввода."},{"id":"ts-narrowing","title":"Сужение типов, never и satisfies","sources":[{"level":"beginner","title":"TypeScript Handbook — Narrowing","url":"https://www.typescriptlang.org/docs/handbook/2/narrowing.html","lang":"en"},{"level":"normal","title":"TypeScript 4.9 — The satisfies operator","url":"https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html","lang":"en"},{"level":"advanced","title":"TypeScript Handbook — Creating Types from Types","url":"https://www.typescriptlang.org/docs/handbook/2/types-from-types.html","lang":"en"}],"rule":"Discriminated union + switch + never в default = компилятор найдёт забытый вариант. satisfies проверяет тип и не расширяет его, as — не проверяет."},{"id":"monorepo-strategy","title":"Монорепо и политика версий","sources":[{"level":"beginner","title":"monorepo.tools — Monorepo Explained","url":"https://monorepo.tools/","lang":"en"},{"level":"normal","title":"Nx — Dependency Management Strategies","url":"https://nx.dev/docs/kb/dependency-management","lang":"en"},{"level":"advanced","title":"Nx — Enforce Module Boundaries","url":"https://nx.dev/docs/features/enforce-module-boundaries","lang":"en"}],"rule":"Монорепо ≠ монолит: проекты с явными связями. Без кэша, affected и правил границ это просто большая медленная репа. Версии — одни на всех по умолчанию."},{"id":"design-system-governance","title":"Управление дизайн-системой","sources":[{"level":"beginner","title":"Atomic Design — Maintaining Design Systems","url":"https://atomicdesign.bradfrost.com/chapter-5/","lang":"en"},{"level":"normal","title":"Brad Frost — A design system governance process","url":"https://bradfrost.com/blog/post/a-design-system-governance-process/","lang":"en"},{"level":"advanced","title":"Design Tokens Community Group","url":"https://www.designtokens.org/","lang":"en"}],"rule":"Сначала спроси команду дизайн-системы, потом реши: снежинка (в продукт) или общий компонент (в систему). Токены — один источник, раздаются версией."},{"id":"rum-observability","title":"RUM и наблюдаемость фронтенда","sources":[{"level":"beginner","title":"GoogleChrome — web-vitals library","url":"https://github.com/GoogleChrome/web-vitals","lang":"en"},{"level":"normal","title":"web.dev — Find slow interactions in the field","url":"https://web.dev/articles/find-slow-interactions-in-the-field","lang":"en"},{"level":"advanced","title":"W3C — Trace Context","url":"https://www.w3.org/TR/trace-context/","lang":"en"}],"rule":"Метрики копи и шли через sendBeacon при visibilitychange, смотри p75 по маршруту и релизу. traceparent связывает запрос браузера с трассой бэкенда."},{"id":"performance-budgets","title":"Бюджеты производительности","sources":[{"level":"beginner","title":"web.dev — Performance budgets 101","url":"https://web.dev/articles/performance-budgets-101","lang":"en"},{"level":"normal","title":"Angular v21 — Configuring size budgets","url":"https://v21.angular.dev/tools/cli/build","lang":"en"},{"level":"advanced","title":"web.dev — Best practices for measuring Web Vitals in the field","url":"https://web.dev/articles/vitals-field-measurement-best-practices","lang":"en"}],"rule":"Бюджет — лимит с владельцем. Превысил — оптимизируй, убери старое или не добавляй новое; поднять лимит можно только явным решением."},{"id":"ng-version-upgrades","title":"Обновление версий Angular","sources":[{"level":"beginner","title":"Angular v21 — Update Guide","url":"https://v21.angular.dev/update-guide","lang":"en"},{"level":"normal","title":"Angular v21 — ng update","url":"https://v21.angular.dev/cli/update","lang":"en"},{"level":"advanced","title":"Angular v21 — Versioning and releases","url":"https://v21.angular.dev/reference/releases","lang":"en"}],"rule":"Мажор раз в 6 месяцев, поддержка 18 (6 активно + 12 LTS). ng update — по одной мажорной версии за раз, устаревшее живёт минимум два мажора."},{"id":"csp-trusted-types","title":"CSP, Trusted Types и SRI","sources":[{"level":"beginner","title":"Angular v21 — Security","url":"https://v21.angular.dev/best-practices/security","lang":"en"},{"level":"normal","title":"web.dev — Mitigate XSS with a strict CSP","url":"https://web.dev/articles/strict-csp","lang":"en"},{"level":"advanced","title":"web.dev — Prevent DOM-based XSS with Trusted Types","url":"https://web.dev/articles/trusted-types","lang":"en"}],"rule":"CSP — на nonce/hash, а не на список доменов. Trusted Types запрещают строку в innerHTML. SRI проверяет хэш чужого файла. Внедряй через report-only."},{"id":"library-semver","title":"Версии, deprecation и changelog библиотеки","sources":[{"level":"beginner","title":"Semantic Versioning 2.0.0","url":"https://semver.org/","lang":"en"},{"level":"normal","title":"Keep a Changelog","url":"https://keepachangelog.com/en/1.1.0/","lang":"en"},{"level":"advanced","title":"Conventional Commits 1.0.0","url":"https://www.conventionalcommits.org/en/v1.0.0/","lang":"en"}],"rule":"Сначала объяви публичный API. Сломал его — MAJOR, добавил — MINOR, починил — PATCH. Пометка deprecated — в минорной, удаление — только в мажорной."},{"id":"ng-library-api","title":"Angular-библиотека как SDK","sources":[{"level":"beginner","title":"Angular v21 — Creating libraries","url":"https://v21.angular.dev/tools/libraries/creating-libraries","lang":"en"},{"level":"normal","title":"Angular v21 — Schematics for libraries","url":"https://v21.angular.dev/tools/cli/schematics-for-libraries","lang":"en"},{"level":"advanced","title":"Angular CLI — Update command specification","url":"https://github.com/angular/angular-cli/blob/main/docs/specifications/update.md","lang":"en"}],"rule":"Публично только то, что экспортирует public-api.ts. @angular/* — в peerDependencies. Ломающие изменения сопровождай миграцией для ng update."},{"id":"contribution-model","title":"Модель вклада в общий код","sources":[{"level":"beginner","title":"GitHub Docs — About code owners","url":"https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners","lang":"en"},{"level":"normal","title":"React RFCs — process","url":"https://github.com/reactjs/rfcs","lang":"en"},{"level":"advanced","title":"InnerSource Commons — Introduction","url":"https://innersourcecommons.org/learn/learning-path/introduction/","lang":"en"}],"rule":"Мелочь — обычный PR, существенное — RFC. CODEOWNERS назначает ревьюеров-команды. Гость пишет код, хозяин ревьюит и поддерживает (InnerSource)."},{"id":"adoption-metrics","title":"Метрики внедрения и пользы платформы","sources":[{"level":"beginner","title":"The Design System Guide — Metrics","url":"https://thedesignsystem.guide/design-system-metrics","lang":"en"},{"level":"normal","title":"Brevo Engineering — Design system adoption tracker","url":"https://engineering.brevo.com/how-to-track-design-system-adoption/","lang":"en"},{"level":"advanced","title":"DORA — Software delivery performance metrics","url":"https://dora.dev/guides/dora-metrics/","lang":"en"}],"rule":"Использование меряй сканом кода, а не опросом; опрос объясняет «почему». Пользу платформы — метриками DORA до и после. Метрика без действия — тщеславие."}],"cards":[{"id":"a11y/aria-first-rule","topic":"a11y","level":"junior","concept":"aria-basics","q":"Когда нужна ARIA, а когда она вредит?","a":"Первое правило: если есть нативный элемент (button, a, input, dialog) — бери его. ARIA меняет только то, что объявляется скринридеру, поведение (фокус, клавиши) она не добавляет. Роль без поведения обманывает пользователя — это хуже, чем никакой ARIA.","origin":"authored"},{"id":"a11y/alt","topic":"a11y","level":"junior","concept":"aria-basics","q":"Какой alt писать у картинки? А если картинка декоративная?","a":"alt описывает смысл картинки в контексте, а не её вид: «График продаж: рост 20%», а не «картинка». Для декоративной — пустой alt=\"\", тогда скринридер её пропустит. Без атрибута alt скринридер может прочитать имя файла.","origin":"authored"},{"id":"a11y/contrast","topic":"a11y","level":"junior","concept":"aria-basics","q":"Какой контраст текста требует WCAG AA?","a":"Не меньше 4.5:1 для обычного текста и 3:1 для крупного (от 18pt или 14pt жирного). Проверяют в DevTools или Lighthouse. Серый текст на сером фоне — частая ошибка.","origin":"authored"},{"id":"a11y/modal-focus","topic":"a11y","level":"junior","concept":"focus-management","q":"Что должна делать доступная модалка с фокусом?","a":"При открытии — перевести фокус внутрь. Пока открыта — не выпускать Tab наружу, фон недоступен. Escape закрывает. При закрытии — вернуть фокус на кнопку-открыватель. Нативный <dialog> с showModal() даёт большую часть этого сам.","origin":"authored"},{"id":"a11y/a11y-owner","topic":"a11y","level":"senior","concept":"a11y-ownership","q":"Ты отвечаешь за доступность продукта. Как выстроить процесс?","a":"Цель — WCAG 2.2 AA. axe в юнит- и E2E-тестах и Lighthouse в CI ловят часть проблем; остальное — ручная проверка клавиатурой и скринридером по чек-листу для ключевых сценариев. Доступные компоненты в дизайн-системе: одна починка — везде. Критерии a11y в DoD, обучение команды, регулярный аудит.","origin":"authored"},{"id":"a11y/angular-aria","topic":"a11y","level":"senior","concept":"a11y-ownership","q":"Какие инструменты для доступности даёт экосистема Angular?","a":"CDK a11y: FocusTrap, FocusMonitor, LiveAnnouncer, ListKeyManager для клавиатурной навигации. Компоненты Angular Material уже доступны. Пакет @angular/aria с headless-паттернами — новый; перед использованием проверить его статус стабильности в документации своей версии.","origin":"authored"},{"id":"arch-patterns/solid","topic":"arch-patterns","level":"middle","concept":"design-principles","q":"Как принципы SOLID выглядят во фронтенде? Приведи примеры.","a":"S — компонент показывает, сервис загружает, стор хранит. O — расширяем через входы, слоты (ng-content), стратегии, а не правкой ядра. L — замена реализации (мок вместо API) не ломает потребителя. I — маленькие интерфейсы входов вместо одного «конфига на всё». D — зависим от абстракции через DI и InjectionToken.","origin":"authored"},{"id":"arch-patterns/patterns","topic":"arch-patterns","level":"middle","concept":"design-principles","q":"Где во фронтенде встречаются Singleton, Factory, Observer и Module?","a":"Singleton — сервис providedIn: 'root'. Factory — useFactory в провайдерах, создание объектов по типу. Observer — RxJS, сигналы, события DOM. Module — ES-модули: инкапсуляция через export. Паттерн берут под конкретную проблему, а не ради паттерна.","origin":"authored"},{"id":"arch-patterns/mvc-mvvm","topic":"arch-patterns","level":"middle","concept":"design-principles","q":"Чем MVC отличается от MVVM и где в этом Angular?","a":"Оба отделяют представление от модели. В MVC контроллер принимает ввод и обновляет модель и вид. В MVVM вид привязан к ViewModel через data binding и сам обновляется. Angular ближе к MVVM: шаблон = View, класс компонента = ViewModel, сервисы и стор = Model.","origin":"authored"},{"id":"arch-patterns/feature-structure","topic":"arch-patterns","level":"middle","concept":"app-structure","q":"Как разложить код Angular-приложения по папкам?","a":"По фичам, а не по типам файлов: orders/, profile/, каждая со своими страницами, компонентами, сервисами. Общий UI — в shared/ui, инфраструктура — в core. Зависимости идут в одну сторону: страницы → фичи → общие модули; фичи не импортируют друг друга напрямую. Правила проверяет линтер.","origin":"authored"},{"id":"arch-patterns/smart-dumb","topic":"arch-patterns","level":"middle","concept":"app-structure","q":"Что такое контейнерные и презентационные компоненты?","a":"Контейнер знает о данных: внедряет сервисы и стор, передаёт данные вниз, обрабатывает события. Презентационный компонент — только входы и выходы, без сервисов: его легко переиспользовать, тестировать и показывать в Storybook.","origin":"authored"},{"id":"arch-patterns/module-design","topic":"arch-patterns","level":"middle","concept":"app-structure","q":"Как защитить на ревью архитектуру новой фичи?","a":"Короткий документ: задача и ограничения, 2–3 варианта с плюсами и минусами, выбранный и почему, публичный API модуля, поток данных, состояния ошибок, что будет при росте. Решение записывают в ADR. Спорить о вариантах дешевле до кода, чем после.","origin":"authored"},{"id":"aspnet-basics/minimal-vs-controllers","topic":"aspnet-basics","level":"junior","concept":"aspnet-api-styles","q":"Чем Minimal API отличается от API на контроллерах, и что выбрать для нового проекта?","a":"Minimal API — endpoints объявляются прямо через app.MapGet/MapPost с лямбдами, мало кода и высокая скорость. Контроллеры — классы с action-методами, атрибутами и фильтрами MVC. Для новых проектов Microsoft рекомендует Minimal API; контроллеры остаются нормальным выбором для больших существующих кодовых баз.","code":"app.MapGet(\"/todos/{id:int}\", (int id, ITodoRepo repo) => repo.Find(id));","origin":"authored"},{"id":"aspnet-basics/api-controller","topic":"aspnet-basics","level":"junior","concept":"aspnet-api-styles","q":"Что даёт атрибут [ApiController]?","a":"Он включает поведение для API: обязательная атрибутная маршрутизация, автоматический ответ 400 при невалидной модели, вывод источника параметров (body, route, query), Problem Details для кодов ошибок.","origin":"authored"},{"id":"aspnet-basics/routing-template","topic":"aspnet-basics","level":"junior","concept":"aspnet-routing","q":"Как устроен шаблон маршрута, и зачем route constraints?","a":"Шаблон вида /users/{id:int} сопоставляет URL с endpoint, а значение сегмента попадает в параметр. Constraint (:int, :alpha, :guid) уточняет, какие значения подходят; иначе маршрут не совпадёт. Constraints не используют для валидации: при неверном вводе клиент получит 404, а не 400.","origin":"authored"},{"id":"aspnet-basics/attribute-routing","topic":"aspnet-basics","level":"junior","concept":"aspnet-routing","q":"Как задают маршруты в контроллерах Web API?","a":"Атрибутами: [Route(\"api/[controller]\")] на классе и [HttpGet(\"{id}\")], [HttpPost] на методах. Для Web API это стандарт, а conventional routing ({controller}/{action}/{id?}) обычно используют в MVC с представлениями.","code":"[ApiController]\n[Route(\"api/[controller]\")]\npublic class UsersController : ControllerBase\n{\n    [HttpGet(\"{id:int}\")]\n    public ActionResult<User> Get(int id) => Ok(new User(id));\n}","origin":"authored"},{"id":"aspnet-basics/binding-sources","topic":"aspnet-basics","level":"junior","concept":"aspnet-binding","q":"Откуда ASP.NET Core берёт значения параметров action или endpoint?","a":"Model binding читает route, query string, заголовки, форму и тело запроса. Источник можно указать явно: [FromRoute], [FromQuery], [FromHeader], [FromForm], [FromBody]. В [ApiController] сложный тип по умолчанию читается из тела, а если он зарегистрирован в DI — берётся из контейнера; простые типы — из route и query.","origin":"authored"},{"id":"aspnet-basics/typed-results","topic":"aspnet-basics","level":"junior","concept":"aspnet-binding","q":"Чем TypedResults отличается от Results в Minimal API?","a":"Оба создают IResult (Ok, NotFound, Created…). TypedResults возвращает конкретные типы, например Ok<User>, поэтому их проще тестировать, а OpenAPI сам видит типы ответов. Если веток несколько, тип ответа описывают как Results<Ok<User>, NotFound>.","origin":"authored"},{"id":"aspnet-basics/openapi-builtin","topic":"aspnet-basics","level":"junior","concept":"aspnet-openapi","q":"Как в ASP.NET Core 10 получить OpenAPI-документ, и есть ли там Swagger UI?","a":"Встроенный пакет Microsoft.AspNetCore.OpenApi: builder.Services.AddOpenApi() и app.MapOpenApi(), документ по адресу /openapi/v1.json. В .NET 10 по умолчанию генерируется OpenAPI 3.1, можно и YAML. UI в пакет не входит: Swagger UI или Scalar подключают отдельным пакетом.","code":"builder.Services.AddOpenApi();\nvar app = builder.Build();\napp.MapOpenApi();","origin":"authored"},{"id":"aspnet-basics/minimal-validation","topic":"aspnet-basics","level":"junior","concept":"aspnet-validation","q":"Что нового в валидации Minimal API в .NET 10?","a":"Появилась встроенная валидация: вызываешь builder.Services.AddValidation(), и параметры с атрибутами DataAnnotations ([Required], [Range]) проверяются до вызова обработчика. При ошибке клиент получает 400 Bad Request с деталями ошибок. Для одного endpoint её отключают через .DisableValidation().","code":"builder.Services.AddValidation();\napp.MapPost(\"/products\", ([Range(1, 1000)] int id, [Required] string name) => TypedResults.Ok(id));","origin":"authored"},{"id":"aspnet-basics/razor","topic":"aspnet-basics","level":"junior","concept":"aspnet-razor","q":"Что такое Razor и Razor Pages?","a":"Razor — синтаксис для вставки C# в HTML через @, файлы .cshtml (и .razor у Blazor). Razor Pages — модель серверных страниц: файл с директивой @page плюс класс PageModel. Их подключают через AddRazorPages() и MapRazorPages().","code":"@page\n<h1>Hello, @Model.Name</h1>","origin":"authored"},{"id":"aspnet-di-pipeline/lifetimes","topic":"aspnet-di-pipeline","level":"junior","concept":"aspnet-di-lifetimes","q":"Чем отличаются Singleton, Scoped и Transient?","a":"Transient — новый экземпляр при каждом запросе к контейнеру. Scoped — один экземпляр на scope, в веб-приложении это один HTTP-запрос (например, DbContext). Singleton — один экземпляр на всё приложение, он должен быть потокобезопасным.","code":"builder.Services.AddSingleton<IClock, SystemClock>();\nbuilder.Services.AddScoped<IOrderRepo, OrderRepo>();\nbuilder.Services.AddTransient<IEmailFormatter, EmailFormatter>();","origin":"authored"},{"id":"aspnet-di-pipeline/captive-dependency","topic":"aspnet-di-pipeline","level":"junior","concept":"aspnet-di-lifetimes","q":"Что такое captive dependency и почему это баг?","a":"Это когда сервис с длинным временем жизни держит сервис с коротким: например, Singleton получает Scoped в конструктор. Scoped-объект фактически становится синглтоном: один DbContext на все запросы и потоки. Правило: зависимость живёт не меньше того, кто её держит.","origin":"authored"},{"id":"aspnet-di-pipeline/scope-validation","topic":"aspnet-di-pipeline","level":"junior","concept":"aspnet-di-lifetimes","q":"Как контейнер помогает поймать неправильные lifetimes?","a":"В среде Development включена проверка scope: контейнер падает с ошибкой, если Scoped-сервис берут из корневого провайдера или внедряют в Singleton. Поэтому такие баги видны сразу при запуске, а не в продакшене.","origin":"authored"},{"id":"aspnet-di-pipeline/scoped-in-background","topic":"aspnet-di-pipeline","level":"middle","concept":"aspnet-di-lifetimes","q":"Как использовать Scoped-сервис (например, DbContext) в BackgroundService?","a":"BackgroundService — синглтон, Scoped в конструктор брать нельзя. Внедряют IServiceScopeFactory, на каждую единицу работы создают async scope через CreateAsyncScope и берут сервис из scope.ServiceProvider. Scope освобождают через await using.","code":"await using var scope = scopeFactory.CreateAsyncScope();\nvar db = scope.ServiceProvider.GetRequiredService<AppDbContext>();","origin":"authored"},{"id":"aspnet-di-pipeline/middleware-pipeline","topic":"aspnet-di-pipeline","level":"junior","concept":"aspnet-middleware","q":"Что такое middleware и почему важен их порядок?","a":"Middleware — звено конвейера запроса: может сделать работу до и после вызова следующего звена. Порядок добавления = порядок обработки запроса, ответ идёт в обратном порядке. Поэтому, например, UseCors ставят до UseAuthentication и UseAuthorization, а обработчик исключений — в самое начало.","code":"app.UseExceptionHandler(\"/error\");\napp.UseHttpsRedirection();\napp.UseCors();\napp.UseAuthentication();\napp.UseAuthorization();","origin":"authored"},{"id":"aspnet-di-pipeline/use-vs-run","topic":"aspnet-di-pipeline","level":"junior","concept":"aspnet-middleware","q":"Чем app.Use отличается от app.Run и что такое short-circuit?","a":"app.Use получает next и обычно вызывает await next(context), чтобы передать запрос дальше. app.Run — терминальный middleware: next нет, конвейер на нём заканчивается. Если middleware не вызывает next, он «замыкает» конвейер (short-circuit) — так делают, например, при отдаче статического файла.","code":"app.Use(async (context, next) =>\n{\n    // до\n    await next(context);\n    // после\n});","origin":"authored"},{"id":"aspnet-di-pipeline/configuration-order","topic":"aspnet-di-pipeline","level":"junior","concept":"aspnet-configuration","q":"Откуда ASP.NET Core читает конфигурацию и какой источник важнее?","a":"Источники по умолчанию от высшего приоритета к низшему: аргументы командной строки, переменные окружения, user secrets (в Development), appsettings.{Environment}.json, appsettings.json. Если ключ есть в нескольких источниках, побеждает более приоритетный.","origin":"authored"},{"id":"aspnet-di-pipeline/options-interfaces","topic":"aspnet-di-pipeline","level":"junior","concept":"aspnet-configuration","q":"Чем отличаются IOptions, IOptionsSnapshot и IOptionsMonitor?","a":"IOptions<T> — Singleton, значения читаются один раз, изменения файла не видит. IOptionsSnapshot<T> — Scoped, пересчитывается на каждый запрос, в Singleton его внедрять нельзя. IOptionsMonitor<T> — Singleton, всегда отдаёт текущее значение и умеет уведомлять об изменениях.","code":"builder.Services.Configure<SmtpOptions>(builder.Configuration.GetSection(\"Smtp\"));","origin":"authored"},{"id":"be-api-protocols/openapi-builtin","topic":"be-api-protocols","level":"middle","concept":"aspnet-openapi","q":"Как в ASP.NET Core 10 получить OpenAPI-документ для API?","a":"Встроенным пакетом Microsoft.AspNetCore.OpenApi: AddOpenApi() и MapOpenApi(), документ доступен по /openapi/v1.json. В .NET 10 по умолчанию генерируется OpenAPI 3.1, документ можно отдавать и в YAML. Пакет не содержит UI: Swagger UI или другой просмотрщик подключают отдельно, если он нужен.","code":"builder.Services.AddOpenApi();\nvar app = builder.Build();\nif (app.Environment.IsDevelopment())\n    app.MapOpenApi(); // /openapi/v1.json","origin":"authored"},{"id":"be-api-protocols/openapi-transformers","topic":"be-api-protocols","level":"middle","concept":"aspnet-openapi","q":"Как добавить в OpenAPI-документ схему безопасности или общие поля?","a":"Через трансформеры: AddDocumentTransformer, AddOperationTransformer и AddSchemaTransformer в options у AddOpenApi. Они получают сгенерированный документ и меняют его — например, добавляют Bearer security scheme. Описание конкретных эндпоинтов (WithSummary, Produces, атрибуты) лучше задавать прямо на эндпоинтах.","origin":"authored"},{"id":"be-api-protocols/grpc-basics","topic":"be-api-protocols","level":"middle","concept":"grpc-dotnet","q":"Что такое gRPC и чем он хорош для связи сервисов?","a":"Это RPC-фреймворк: контракт описывают в .proto, по нему генерируют клиент и сервер, данные идут в компактном бинарном Protobuf поверх HTTP/2. Отсюда строгий контракт, высокая скорость и стриминг в обе стороны. В ASP.NET Core сервис — класс, унаследованный от сгенерированной базы, и регистрация app.MapGrpcService<T>().","origin":"authored"},{"id":"be-api-protocols/grpc-vs-rest","topic":"be-api-protocols","level":"middle","concept":"grpc-dotnet","q":"Когда REST с JSON лучше gRPC?","a":"Для публичных API и браузеров: JSON читается человеком и работает везде, а браузер не может напрямую вызвать gRPC — нужен gRPC-Web или JSON transcoding. gRPC выигрывает внутри системы: связь микросервисов, низкая задержка, стриминг, клиенты на разных языках из одного .proto.","origin":"authored"},{"id":"be-api-protocols/grpc-deadlines","topic":"be-api-protocols","level":"senior","concept":"grpc-dotnet","q":"Зачем в gRPC задавать deadline?","a":"Deadline говорит, сколько клиент готов ждать; по истечении вызов завершается со статусом DeadlineExceeded. Без него зависший сервис держит ресурсы бесконечно. На сервере это ServerCallContext.CancellationToken — его передают дальше в БД и HttpClient, а в цепочке gRPC-вызовов deadline пробрасывают через EnableCallContextPropagation.","origin":"authored"},{"id":"be-api-protocols/graphql-basics","topic":"be-api-protocols","level":"middle","concept":"graphql-server","q":"Чем GraphQL отличается от REST?","a":"Один endpoint и типизированная схема; клиент в запросе сам выбирает нужные поля и вложенные связи, поэтому нет лишних или недостающих данных. Чтение — query, изменения — mutation, push-уведомления — subscription. В .NET GraphQL-сервер обычно делают на Hot Chocolate.","origin":"authored"},{"id":"be-api-protocols/graphql-performance","topic":"be-api-protocols","level":"senior","concept":"graphql-server","q":"Какие проблемы производительности и безопасности у GraphQL-сервера?","a":"N+1: резолвер поля вызывается для каждого объекта списка; DataLoader собирает ключи и делает один batch-запрос. Тяжёлые запросы: клиент может запросить глубокую вложенность, поэтому ограничивают глубину и сложность запроса и вводят пагинацию. Кеширование по URL, как в REST, тут работает хуже.","origin":"authored"},{"id":"be-api-protocols/api-versioning","topic":"be-api-protocols","level":"middle","concept":"api-versioning","q":"Когда API нужна новая версия и как её передавать?","a":"Новая версия нужна для ломающих изменений: удалили или переименовали поле, поменяли тип или смысл. Новое необязательное поле или новый эндпоинт обычно не ломают клиентов. Версию передают в URI (/v2/orders), query (?api-version=2.0), заголовке или media type; в ASP.NET Core для этого есть пакеты Asp.Versioning. Старую версию объявляют устаревшей и выключают по графику.","origin":"authored"},{"id":"be-appsec-pipeline/sast","topic":"be-appsec-pipeline","level":"senior","concept":"sast-dast","q":"Что умеет и чего не умеет SAST?","a":"SAST анализирует исходный код без запуска, хорошо масштабируется и находит типовые ошибки вроде SQL-инъекций прямо в pull request. Слабые места — много false positives и плохой поиск проблем аутентификации, контроля доступа и конфигурации, которых нет в коде. Поэтому SAST дополняют DAST, ревью и тестами авторизации.","origin":"authored"},{"id":"be-appsec-pipeline/dast","topic":"be-appsec-pipeline","level":"senior","concept":"sast-dast","q":"Что такое DAST и где его запускать в конвейере?","a":"DAST — black-box тестирование работающего приложения: сканер (например, OWASP ZAP) отправляет вредные запросы и ищет XSS, SQL-инъекции, проблемы аутентификации и ошибки конфигурации сервера. Исходный код ему не нужен, но нужно развёрнутое окружение. Поэтому его запускают на тестовом стенде после деплоя или по ночному расписанию.","origin":"authored"},{"id":"be-appsec-pipeline/quality-gate","topic":"be-appsec-pipeline","level":"senior","concept":"sast-dast","q":"Как встроить SonarQube quality gate в процесс, чтобы не утонуть в старом долге?","a":"Quality gate — набор условий (покрытие, новые баги и уязвимости, дубли), по которым анализ проходит или падает. Встроенный Sonar way смотрит на новый код: он не требует чинить весь legacy, но не даёт добавлять новые проблемы. Статус гейта показывают в pull request и используют, чтобы блокировать merge и ронять CI.","origin":"authored"},{"id":"be-appsec-pipeline/nuget-audit","topic":"be-appsec-pipeline","level":"senior","concept":"dependency-trust","q":"Как в .NET 10 проверять зависимости на известные уязвимости?","a":"NuGet Audit работает при каждом restore и выдаёт предупреждения NU1901–NU1904 по уровню серьёзности. Для проектов на net10.0 по умолчанию NuGetAuditMode=all, то есть проверяются и транзитивные пакеты. В CI high и critical делают ошибками через WarningsAsErrors, а для разбора есть dotnet package list --vulnerable --include-transitive.","code":"<PropertyGroup>\n  <WarningsAsErrors>$(WarningsAsErrors);NU1903;NU1904</WarningsAsErrors>\n</PropertyGroup>","origin":"authored"},{"id":"be-appsec-pipeline/user-secrets","topic":"be-appsec-pipeline","level":"senior","concept":"secrets-dotnet","q":"Для чего нужен dotnet user-secrets и почему он не для продакшна?","a":"Secret Manager хранит секреты разработчика вне репозитория — в JSON-файле в профиле пользователя, привязанном к UserSecretsId проекта. В окружении Development WebApplication.CreateBuilder подключает их автоматически. Файл не шифруется, это только защита от случайного коммита, поэтому в продакшне секреты берут из Key Vault или похожего хранилища.","origin":"authored"},{"id":"be-appsec-pipeline/key-vault","topic":"be-appsec-pipeline","level":"senior","concept":"secrets-dotnet","q":"Как приложение в Azure получает секреты из Key Vault без пароля в конфиге?","a":"Через конфигурационный провайдер AddAzureKeyVault и managed identity: приложение аутентифицируется через DefaultAzureCredential, и в коде нет ни ключей, ни сертификатов. Секреты попадают в IConfiguration, а иерархию задают двумя дефисами: ConnectionStrings--Db становится ConnectionStrings:Db. Доступ к хранилищу выдают с минимальными правами — только чтение секретов.","code":"builder.Configuration.AddAzureKeyVault(\n    new Uri($\"https://{vaultName}.vault.azure.net/\"),\n    new DefaultAzureCredential());","origin":"authored"},{"id":"be-appsec-pipeline/leaked-secret","topic":"be-appsec-pipeline","level":"senior","concept":"secrets-dotnet","q":"Секрет случайно попал в git. Что делать?","a":"Сначала сразу отозвать секрет и выпустить новый: удаление коммита не помогает, его уже могли скопировать. Потом убрать секрет из кода и логов и проверить по журналам, кто и когда им пользовался. Чтобы это не повторилось, включают поиск секретов в pre-commit hook и в конвейере.","origin":"authored"},{"id":"be-appsec-pipeline/non-root-container","topic":"be-appsec-pipeline","level":"senior","concept":"container-hardening","q":"Как запустить ASP.NET Core в контейнере не от root?","a":"С .NET 8 официальные Linux-образы содержат пользователя app (UID 1654), и в Dockerfile пишут USER $APP_UID. Обычные образы по умолчанию всё ещё работают от root, а chiseled-образы сразу non-root. Порт по умолчанию — 8080, потому что непривилегированный пользователь не может слушать порт 80.","code":"FROM mcr.microsoft.com/dotnet/aspnet:10.0\nWORKDIR /app\nCOPY --from=build /app/publish .\nUSER $APP_UID\nENTRYPOINT [\"dotnet\", \"Api.dll\"]","origin":"authored"},{"id":"be-appsec-pipeline/chiseled","topic":"be-appsec-pipeline","level":"senior","concept":"container-hardening","q":"Что такое chiseled-образы .NET и чем они безопаснее?","a":"Это distroless-образы на Ubuntu от Canonical: в них нет shell, пакетного менеджера, curl и wget, только библиотеки, нужные .NET. Меньше пакетов — меньше CVE в отчёте сканера и меньше инструментов у атакующего, а запускаются они от non-root. Минус: нет ICU и tzdata (кроме вариантов -extra), и отлаживать без shell сложнее.","origin":"authored"},{"id":"be-appsec-pipeline/threat-modeling-at-scale","topic":"be-appsec-pipeline","level":"lead","concept":"threat-modeling","q":"Как масштабировать threat modeling на все команды, а не держать его только в security-команде?","a":"Делаю его лёгким и частью обычного дизайна: четыре вопроса манифеста — что строим, что может пойти не так, что с этим делаем, хорошо ли справились — прямо в шаблоне design doc. Обязательный разбор запускают триггеры: новый внешний вход, изменение auth, новые персональные данные, крупное изменение инфраструктуры. В командах обучаю security champions, а security-команда приходит на сложные случаи. Меряю долю значимых изменений с моделью угроз и сколько найденных угроз закрыто до релиза.","origin":"authored"},{"id":"be-appsec-pipeline/vuln-remediation-at-scale","topic":"be-appsec-pipeline","level":"lead","concept":"vuln-management","q":"Сканер нашёл сотни уязвимостей в зависимостях 40 сервисов. Как организуешь исправление?","a":"Приоритет не только по CVSS: сначала то, что есть в каталоге CISA KEV (уже эксплуатируется), затем высокий EPSS — вероятность эксплуатации в ближайшие 30 дней — и проверка достижимости: вызывается ли уязвимый код и открыт ли сервис наружу. Для каждого уровня — SLA на исправление, автоматические PR с обновлениями (Dependabot или Renovate) и владелец у каждого сервиса. Исключения оформляются с причиной и сроком. Меряю время исправления по уровням и долю нарушений SLA.","origin":"authored"},{"id":"be-appsec-pipeline/supply-chain-provenance","topic":"be-appsec-pipeline","level":"lead","concept":"dependency-trust","q":"Как защитить supply chain — от зависимостей до образа в продакшене?","a":"Зависимости фиксирую lockfile и проверяю аудитом, на каждый артефакт генерирую SBOM. Собираю только на hosted CI, который создаёт и подписывает provenance — это уровень SLSA Build L2, а hardened-сборка с защитой от вмешательства — L3. Образы подписываю (например, Sigstore cosign), а admission policy в кластере пропускает только подписанные образы с provenance из нашего CI. Так подменённый артефакт или сборка с ноутбука до прода не дойдут.","origin":"authored"},{"id":"be-architecture/dbcontext-is-uow","topic":"be-architecture","level":"middle","concept":"repository-uow","q":"Нужны ли Repository и Unit of Work поверх EF Core?","a":"DbContext уже сочетает оба паттерна: DbSet<T> работает как репозиторий, а SaveChanges фиксирует все изменения одной единицей работы. Обобщённый IRepository<T> поверх EF часто только прячет его возможности — Include, проекции, AsNoTracking. Свой репозиторий оправдан, когда он говорит языком домена (GetActiveOrders(customerId)) и держит границу агрегата.","origin":"authored"},{"id":"be-architecture/repository-tradeoffs","topic":"be-architecture","level":"middle","concept":"repository-uow","q":"Какие плюсы и минусы у отдельного слоя репозиториев?","a":"Плюсы: домен не зависит от ORM, запросы к агрегату собраны в одном месте, в unit-тестах хранилище легко подменить. Минусы: ещё один слой, риск «протекающей» абстракции, если наружу отдают IQueryable, и generic-репозитории, которые ничего не добавляют. Логику запросов к EF Core всё равно лучше проверять интеграционным тестом на реальной БД.","origin":"authored"},{"id":"be-architecture/cqrs","topic":"be-architecture","level":"middle","concept":"cqrs","q":"Что такое CQRS и когда он оправдан?","a":"Это разделение модели на команды, которые меняют состояние, и запросы, которые только читают, — часто с разными моделями, а иногда и разными хранилищами. Оправдан, когда чтение и запись сильно отличаются по форме или нагрузке. Для обычного CRUD это лишняя сложность: Фаулер прямо предупреждает, что большинству систем CQRS добавляет рискованную сложность.","origin":"authored"},{"id":"be-architecture/cqrs-read-model-lag","topic":"be-architecture","level":"senior","concept":"cqrs","q":"Какие проблемы появляются, если у CQRS отдельная база для чтения?","a":"Модель чтения обновляется асинхронно, поэтому отстаёт — это eventual consistency: пользователь может не увидеть своё изменение сразу. Нужно надёжно доставлять изменения (outbox, брокер), уметь пересобирать проекции и следить за отставанием. UI часто показывает результат команды сам, не дожидаясь проекции.","origin":"authored"},{"id":"be-architecture/event-sourcing","topic":"be-architecture","level":"middle","concept":"event-sourcing","q":"Что такое Event Sourcing и чем он отличается от обычного хранения состояния?","a":"Вместо текущего состояния хранится append-only журнал событий (OrderCreated, ItemAdded, OrderPaid), а состояние получается их проигрыванием. Плюсы — полная история и аудит, можно восстановить состояние на любой момент и построить новые проекции. Минусы — запросы через проекции (часто вместе с CQRS), версионирование событий и eventual consistency.","origin":"authored"},{"id":"be-architecture/event-sourcing-snapshots","topic":"be-architecture","level":"senior","concept":"event-sourcing","q":"Агрегат в Event Sourcing накопил тысячи событий, и загрузка стала медленной. Что делать?","a":"Делают snapshot: сохраняют состояние на какой-то версии и при загрузке проигрывают только события после него. Snapshot — это оптимизация, источник истины по-прежнему журнал событий, поэтому снимок можно выбросить и пересчитать.","origin":"authored"},{"id":"be-architecture/monolith-vs-microservices","topic":"be-architecture","level":"middle","concept":"monolith-vs-microservices","q":"Монолит или микросервисы: как выбрать?","a":"Микросервисы дают независимый деплой, масштабирование и автономию команд, но платят распределённой системой: сеть, eventual consistency, наблюдаемость, сложный деплой. Монолит проще разрабатывать, тестировать и отлаживать. Для новой системы с неясными границами разумно начать с монолита (Monolith First) и выделять сервисы, когда границы и причины ясны.","origin":"authored"},{"id":"be-architecture/modular-monolith","topic":"be-architecture","level":"middle","concept":"monolith-vs-microservices","q":"Что такое модульный монолит?","a":"Одно развёртываемое приложение, разделённое на модули по бизнес-границам: у каждого модуля свой публичный API и свои данные, а лезть во внутренности соседа запрещено. Он сохраняет простоту одного деплоя, а чёткие границы позволяют позже вынести модуль в сервис, если это понадобится.","origin":"authored"},{"id":"be-architecture/database-per-service","topic":"be-architecture","level":"middle","concept":"monolith-vs-microservices","q":"Почему микросервисам не советуют общую базу данных?","a":"Общая БД связывает сервисы через схему: изменение таблицы ломает чужой сервис, и независимый деплой теряется. Паттерн Database per service: данные сервиса приватны и доступны только через его API. Цена — нет обычных транзакций между сервисами, согласованность держат саги, а запросы по нескольким сервисам собирают через API composition или CQRS.","origin":"authored"},{"id":"be-auth-basics/authn-vs-authz","topic":"be-auth-basics","level":"junior","concept":"authn-vs-authz","q":"Чем аутентификация отличается от авторизации?","a":"Аутентификация отвечает на вопрос «кто ты?»: проверяет личность и создаёт ClaimsPrincipal. Авторизация отвечает на вопрос «что тебе можно?» и проверяет claims пользователя по правилам. Это разные шаги, но авторизация опирается на результат аутентификации. Сама настройка аутентификации доступ к эндпоинтам не закрывает.","origin":"authored"},{"id":"be-auth-basics/401-vs-403","topic":"be-auth-basics","level":"junior","concept":"authn-vs-authz","q":"Когда API должен вернуть 401, а когда 403?","a":"401 Unauthorized — сервер не знает, кто ты: нет учётных данных или они неверные; ответ содержит заголовок WWW-Authenticate. 403 Forbidden — личность известна, но прав на действие нет. В ASP.NET Core 401 даёт challenge, а 403 — forbid.","origin":"authored"},{"id":"be-auth-basics/jwt-structure","topic":"be-auth-basics","level":"junior","concept":"jwt-bearer","q":"Из чего состоит JWT и можно ли хранить в нём секреты?","a":"JWT — три части через точку: header.payload.signature; header и payload закодированы в Base64Url. Подпись защищает от подмены, но не шифрует: payload может прочитать любой. Поэтому пароли и секреты в JWT не кладут.","origin":"authored"},{"id":"be-auth-basics/jwt-validation","topic":"be-auth-basics","level":"junior","concept":"jwt-bearer","q":"Что API проверяет в JWT access token?","a":"Подпись (токен выдал доверенный сервер и его не меняли), издателя iss, аудиторию aud (токен выпущен именно для этого API) и срок жизни exp. Если проверка не прошла, AddJwtBearer вернёт 401. Обычно указывают Authority и Audience, а ключи подписи берутся из metadata сервера.","code":"builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)\n    .AddJwtBearer(options =>\n    {\n        options.Authority = \"https://login.example.com\";\n        options.Audience = \"orders-api\";\n    });","origin":"authored"},{"id":"be-auth-basics/oauth-vs-oidc","topic":"be-auth-basics","level":"junior","concept":"oauth-oidc","q":"Чем OAuth 2.0 отличается от OpenID Connect?","a":"OAuth 2.0 — про авторизацию: клиент получает access token, чтобы вызывать API от имени пользователя. OpenID Connect — слой идентификации поверх OAuth 2.0: он добавляет ID token (JWT с данными о входе пользователя). Запрос OIDC обязательно содержит scope openid.","origin":"authored"},{"id":"be-auth-basics/auth-code-pkce","topic":"be-auth-basics","level":"middle","concept":"oauth-oidc","q":"Какой OAuth-поток выбрать для веб- или мобильного приложения пользователя?","a":"Authorization Code с PKCE: пользователь входит на сервере авторизации, приложение получает code и меняет его на токены через token endpoint. Для публичных клиентов (SPA, мобильные) PKCE обязателен, для конфиденциальных — рекомендуется. Implicit не рекомендуется, а Resource Owner Password использовать нельзя (RFC 9700).","origin":"authored"},{"id":"be-auth-basics/cookie-vs-bearer","topic":"be-auth-basics","level":"junior","concept":"cookie-vs-bearer","q":"Cookie или Bearer-токен: в чём разница?","a":"Cookie браузер отправляет сам с каждым запросом; с флагом HttpOnly JavaScript её не прочитает, но нужна защита от CSRF (SameSite, antiforgery). Bearer-токен клиент кладёт в заголовок Authorization сам — удобно для API и мобильных клиентов. Токены не хранят в localStorage: любой XSS их украдёт.","origin":"authored"},{"id":"be-auth-basics/cookie-api-401","topic":"be-auth-basics","level":"middle","concept":"cookie-vs-bearer","q":"Что изменилось в ASP.NET Core 10 для API с cookie-аутентификацией?","a":"Раньше неавторизованный запрос к API получал редирект 302 на страницу логина. В ASP.NET Core 10 известные API-эндпоинты ([ApiController], Minimal API с JSON, TypedResults, SignalR) получают 401 или 403. Старое поведение можно вернуть через события OnRedirectToLogin и OnRedirectToAccessDenied.","origin":"authored"},{"id":"be-auth-basics/authorize-policy","topic":"be-auth-basics","level":"junior","concept":"aspnet-authz-policies","q":"Как в ASP.NET Core ограничить доступ к эндпоинту по роли или политике?","a":"[Authorize] пускает только аутентифицированных, [AllowAnonymous] снимает ограничение. Roles = \"Admin,Manager\" значит «любая из ролей», а два атрибута [Authorize] — «обе». Сложные правила оформляют политикой (AddPolicy, RequireClaim) и подключают через [Authorize(Policy = ...)] или RequireAuthorization в Minimal API.","code":"builder.Services.AddAuthorizationBuilder()\n    .AddPolicy(\"CanEditOrders\", p => p.RequireClaim(\"permission\", \"orders:edit\"));\n\napp.MapPut(\"/orders/{id}\", UpdateOrder).RequireAuthorization(\"CanEditOrders\");","origin":"authored"},{"id":"be-clean-ddd/dependency-rule","topic":"be-clean-ddd","level":"senior","concept":"clean-architecture","q":"Какое главное правило Clean Architecture и как оно выглядит в .NET-решении?","a":"Правило зависимостей: зависимости в коде указывают только внутрь, к домену, и внутренний слой ничего не знает о внешних. Проект Domain не ссылается на EF Core, ASP.NET Core или брокер, а Infrastructure реализует интерфейсы, объявленные во внутренних слоях. Поэтому бизнес-правила можно тестировать и менять без базы и веба.","origin":"authored"},{"id":"be-clean-ddd/ports-adapters","topic":"be-clean-ddd","level":"senior","concept":"clean-architecture","q":"Что такое порты и адаптеры в Hexagonal Architecture?","a":"Порт — это API, через который ядро приложения общается с внешним миром, например IOrderRepository или IPaymentGateway. Адаптер — конкретная технология за портом: контроллер, репозиторий на EF Core, in-memory фейк для тестов. На один порт бывает несколько адаптеров, поэтому ядро можно запустить и проверить без UI и базы.","origin":"authored"},{"id":"be-clean-ddd/aggregate","topic":"be-clean-ddd","level":"senior","concept":"ddd-building-blocks","q":"Что такое агрегат в DDD и какие у него правила?","a":"Агрегат — кластер доменных объектов, который загружают и сохраняют целиком, например Order со строками заказа. Снаружи ссылаются только на корень агрегата, и корень следит за инвариантами всего кластера. Транзакция не должна пересекать границы агрегатов: другие агрегаты обновляют через доменные события и eventual consistency.","origin":"authored"},{"id":"be-clean-ddd/value-object","topic":"be-clean-ddd","level":"senior","concept":"ddd-building-blocks","q":"Чем value object отличается от entity и как сделать его в C#?","a":"У entity есть идентичность (Id), у value object её нет: два адреса с одинаковыми полями равны. Value object неизменяемый, поэтому его безопасно передавать и переиспользовать. В C# удобно взять record с равенством по значению, а в EF Core маппить его как owned или complex type.","code":"public sealed record Money(decimal Amount, string Currency)\n{\n    public Money Add(Money other) =>\n        other.Currency == Currency\n            ? this with { Amount = Amount + other.Amount }\n            : throw new InvalidOperationException(\"Currency mismatch\");\n}","origin":"authored"},{"id":"be-clean-ddd/domain-vs-integration-events","topic":"be-clean-ddd","level":"senior","concept":"ddd-building-blocks","q":"Чем доменное событие отличается от интеграционного?","a":"Доменное событие обрабатывают внутри того же домена и процесса, часто в той же транзакции, чтобы обновить другие агрегаты. Интеграционное событие уходит в другие сервисы через брокер и публикуется только после успешного коммита. Поэтому доменные события обычно раздаёт in-memory диспетчер (например, MediatR), а интеграционные идут через outbox.","origin":"authored"},{"id":"be-clean-ddd/bounded-context","topic":"be-clean-ddd","level":"senior","concept":"bounded-contexts","q":"Что такое bounded context и как он помогает делить систему на сервисы?","a":"Bounded context — граница, внутри которой у модели и терминов одно значение. «Customer» в продажах и в поддержке — разные модели, и это нормально. Между контекстами делают явный перевод и рисуют context map, а границы контекстов — хорошие кандидаты на границы сервисов.","origin":"authored"},{"id":"be-clean-ddd/anemic-model","topic":"be-clean-ddd","level":"senior","concept":"ddd-building-blocks","q":"Что такое анемичная доменная модель и почему её считают антипаттерном?","a":"Это сущности — «мешки» с геттерами и сеттерами, а вся логика лежит в сервисах. Ты платишь за доменную модель и ORM-маппинг, но получаешь процедурный Transaction Script. Лечат переносом правил в методы сущностей (order.AddItem(...) вместо service.AddItem(order, ...)), а сервисы приложения оставляют тонкими.","origin":"authored"},{"id":"be-clean-ddd/yagni-vs-refactoring","topic":"be-clean-ddd","level":"senior","concept":"simple-design-tradeoffs","q":"Как YAGNI уживается с SOLID и DRY, если они тянут в разные стороны?","a":"YAGNI запрещает строить возможности «на будущее», но не запрещает держать код простым для изменений: рефакторинг — не нарушение YAGNI. Интерфейс или общий модуль вводят, когда появился второй реальный случай, а не заранее. DRY применяют к одному и тому же знанию, а не к коду, который просто похож в двух разных контекстах.","origin":"authored"},{"id":"be-clean-ddd/when-not-ddd","topic":"be-clean-ddd","level":"lead","concept":"ddd-building-blocks","q":"Как бы ты решил, нужны ли DDD и Clean Architecture новому сервису?","a":"Смотрю на сложность домена: простому CRUD-сервису хватает тонкого слоя над EF Core, полные DDD-паттерны там лишние. Если много бизнес-правил, инвариантов и терминов, которые обсуждают с экспертами, агрегаты и слоистая архитектура окупаются. Решение фиксирую в ADR и принимаю отдельно для каждого сервиса, а не для всей системы сразу.","origin":"authored"},{"id":"be-clean-ddd/context-map","topic":"be-clean-ddd","level":"principal","concept":"context-mapping","q":"Как бы ты описал отношения между bounded contexts в большой системе?","a":"Рисую context map: какие контексты есть и кто от кого зависит. Для каждой связи выбираю паттерн: customer-supplier, когда upstream учитывает нужды downstream; open host service с published language, когда потребителей много; anticorruption layer, когда чужая модель неудобна или это legacy; separate ways, когда интеграция не окупается. Карта сразу показывает, где нужны договорённости между командами и где изменения будут дорогими.","origin":"authored"},{"id":"be-clean-ddd/anticorruption-layer","topic":"be-clean-ddd","level":"principal","concept":"context-mapping","q":"Новый сервис должен работать с legacy-системой со странной моделью данных. Как не испортить свой домен?","a":"Ставлю anti-corruption layer: отдельный слой или сервис переводит запросы и данные между моделью legacy и нашей, и домен видит только свои понятия. Цена — дополнительная задержка, ещё один компонент для масштабирования и мониторинга и правила перевода, которые надо поддерживать. Если идёт миграция, ACL временный и уходит вместе с legacy. Успех: изменения в legacy затрагивают только ACL, а не доменный код.","origin":"authored"},{"id":"be-clean-ddd/subdomain-investment","topic":"be-clean-ddd","level":"principal","concept":"context-mapping","q":"Как решаешь, куда вкладывать сильных инженеров и полноценный DDD, а где взять готовое решение?","a":"Делю домен на поддомены. Core — то, что отличает бизнес от конкурентов: сюда лучших людей, богатую модель и тактический DDD. Supporting — нужно бизнесу, но преимущества не даёт: делаем проще, CRUD допустим. Generic — задачи, которые индустрия давно решила, например учёт пользователей или выставление счетов: покупаем или берём готовое. Частая ошибка — тратить силы на generic и экономить на core.","origin":"authored"},{"id":"be-clean-ddd/finding-boundaries","topic":"be-clean-ddd","level":"principal","concept":"team-domain-boundaries","q":"Как бы ты искал границы сервисов при декомпозиции сложного домена?","a":"Начинаю не с таблиц, а с бизнес-процесса: провожу EventStorming с экспертами домена, аналитиками и инженерами и прохожу процесс как поток событий. Места, где меняются язык, владелец или ритм процесса, — кандидаты в границы контекстов. Кандидата проверяю: мало ли синхронных вызовов через границу и может ли одна команда владеть контекстом целиком. Результат — гипотеза, которую подтверждаю на первых выносах, а не окончательная схема.","origin":"authored"},{"id":"be-clean-ddd/conway-team-boundaries","topic":"be-clean-ddd","level":"principal","concept":"team-domain-boundaries","q":"Как связаны границы контекстов и структура команд?","a":"Закон Конвея: система повторяет структуру коммуникаций организации. Поэтому границы контекстов и команд проектирую вместе: одна команда владеет контекстом целиком, и обычное изменение не требует согласования трёх команд. Inverse Conway maneuver — сознательно менять структуру команд под нужную архитектуру, но одна реорганизация без работы с кодом создаёт только трение. Контекст должен помещаться в голову одной команды, иначе её когнитивная нагрузка тормозит поставку.","origin":"authored"},{"id":"be-clean-ddd/fitness-functions","topic":"be-clean-ddd","level":"principal","concept":"architecture-governance","q":"Как не дать Clean Architecture «расползтись» в большой кодовой базе, где работают десять команд?","a":"Правила, которые держатся только на ревью, со временем нарушаются, поэтому превращаю их в architectural fitness functions — автоматические проверки в CI. Например, тесты зависимостей через NetArchTest или ArchUnitNET: проект Domain не ссылается на Infrastructure и EF Core; плюс проверки производительности ключевых сценариев. Нарушение валит сборку, а исключение оформляется через ADR. Меряю число нарушений и сколько модулей приходится менять ради типовой фичи.","origin":"authored"},{"id":"be-crypto/symmetric-vs-asymmetric","topic":"be-crypto","level":"middle","concept":"crypto-algorithms","q":"Чем симметричное шифрование (AES) отличается от асимметричного (RSA, ECC) и как их совмещают?","a":"В AES один секретный ключ и шифрует, и расшифровывает — это быстро и подходит для больших данных. В RSA и ECC пара ключей: публичным шифруют или проверяют подпись, приватным расшифровывают или подписывают; это заметно медленнее. На практике их совмещают: асимметрией договариваются о сессионном ключе, а сами данные шифруют AES — так работает TLS.","origin":"authored"},{"id":"be-crypto/ecc-vs-rsa","topic":"be-crypto","level":"middle","concept":"crypto-algorithms","q":"Почему ECC вытесняет RSA?","a":"ECC даёт ту же стойкость при гораздо меньшем ключе: по таблице NIST 128 бит безопасности — это ECC 256 бит против RSA 3072 бит. Отсюда быстрее операции с приватным ключом, короче подписи и сертификаты. В .NET это ECDsa для подписи и ECDiffieHellman для согласования ключа; RSA остаётся ради совместимости.","origin":"authored"},{"id":"be-crypto/aes-gcm","topic":"be-crypto","level":"senior","concept":"crypto-algorithms","q":"Какой режим AES выбрать и что важно про IV/nonce?","a":"Authenticated-режим, например GCM (в .NET — AesGcm): он шифрует и считает tag, поэтому подмена шифротекста обнаруживается при расшифровке. Nonce должен быть уникальным для каждого шифрования одним ключом, повтор ломает защиту. ECB не используют: одинаковые блоки дают одинаковый шифротекст.","origin":"authored"},{"id":"be-crypto/hash-vs-encryption","topic":"be-crypto","level":"middle","concept":"password-hashing","q":"Чем хеширование отличается от шифрования и почему MD5 и SHA-1 больше не используют?","a":"Шифрование обратимо, если есть ключ; хеш — односторонняя функция, из него исходные данные не получить. MD5 и SHA-1 признаны небезопасными, для целостности и подписей берут SHA-2 (SHA-256 и выше). Но для паролей не годится и SHA-256: он слишком быстрый для перебора.","origin":"authored"},{"id":"be-crypto/password-storage","topic":"be-crypto","level":"middle","concept":"password-hashing","q":"Как правильно хранить пароли?","a":"Только медленным адаптивным хешем с уникальной солью: Argon2id, scrypt, bcrypt или PBKDF2 с большим числом итераций. Соль не секретна, она хранится рядом с хешем и делает заранее посчитанные таблицы бесполезными. Параметры стоимости повышают со временем и при входе пользователя перехешируют пароль по новым параметрам.","origin":"authored"},{"id":"be-crypto/aspnet-password-hasher","topic":"be-crypto","level":"middle","concept":"password-hashing","q":"Как хеширует пароли PasswordHasher<TUser> в ASP.NET Core Identity?","a":"Через PBKDF2 с солью; в формате V3 по умолчанию 100 000 итераций (PasswordHasherOptions.IterationCount). VerifyHashedPassword может вернуть SuccessRehashNeeded — пароль верный, но хеш устарел, и его стоит пересчитать. Без Identity есть низкоуровневый KeyDerivation.Pbkdf2.","origin":"authored"},{"id":"be-crypto/data-protection","topic":"be-crypto","level":"middle","concept":"aspnet-data-protection","q":"Для чего в ASP.NET Core Data Protection API и для чего он не подходит?","a":"Он шифрует и подписывает данные, которые возвращаются к нам же: auth-куки, antiforgery-токены, токены сброса пароля. Берут IDataProtectionProvider.CreateProtector(\"purpose\") и вызывают Protect/Unprotect; разные purpose изолируют данные друг от друга. Ключи по умолчанию живут 90 дней, поэтому API не предназначен для бессрочного хранения секретов в БД.","code":"var protector = provider.CreateProtector(\"Orders.ShareLink.v1\");\nstring token = protector.Protect(orderId.ToString());\nstring back = protector.Unprotect(token); // бросит исключение при подмене","origin":"authored"},{"id":"be-crypto/data-protection-keys","topic":"be-crypto","level":"middle","concept":"aspnet-data-protection","q":"После деплоя на несколько инстансов пользователей «разлогинивает». Почему и как исправить?","a":"Каждый инстанс создал свой key ring, и куку, зашифрованную одним сервером, другой расшифровать не может; то же бывает после рестарта контейнера без постоянного хранилища ключей. Ключи хранят в общем месте (PersistKeysToAzureBlobStorage, PersistKeysToDbContext и т. п.), защищают их at rest (ProtectKeysWithAzureKeyVault) и задают общее SetApplicationName.","origin":"authored"},{"id":"be-debugging/debugger-tools","topic":"be-debugging","level":"junior","concept":"dotnet-debugger","q":"Какими возможностями отладчика ты пользуешься, кроме обычного breakpoint?","a":"Step Over (F10) выполняет строку целиком, Step Into (F11) заходит в вызываемый метод, Step Out (Shift+F11) дорабатывает текущий метод и возвращается к вызывающему. Условный breakpoint останавливает выполнение только при условии или числе срабатываний, а logpoint (tracepoint в Visual Studio) пишет сообщение без остановки. Окна Call Stack и Locals показывают, как мы сюда попали и какие значения у переменных.","origin":"authored"},{"id":"be-debugging/stack-trace","topic":"be-debugging","level":"junior","concept":"dotnet-exceptions","q":"Как читать stack trace исключения?","a":"Сначала тип и сообщение исключения. Верхний кадр — место, где исключение брошено; ниже идут методы, которые его вызвали, до места, где его поймали. Ищи первый кадр из своего кода с файлом и номером строки, а потом смотри InnerException — там часто настоящая причина.","origin":"authored"},{"id":"be-debugging/null-reference","topic":"be-debugging","level":"junior","concept":"dotnet-exceptions","q":"Откуда берётся NullReferenceException и как с ним бороться?","a":"Это обращение к члену объекта, который равен null: забыли создать объект, метод вернул null, элемент массива не заполнен. Его не ловят в catch, а исправляют код: проверка на null, операторы ?. и ??. Включённые nullable reference types (<Nullable>enable</Nullable>) предупреждают о таких местах ещё при компиляции.","origin":"authored"},{"id":"be-debugging/argument-vs-invalid-operation","topic":"be-debugging","level":"junior","concept":"dotnet-exceptions","q":"Когда бросать ArgumentException, а когда InvalidOperationException?","a":"ArgumentException (и наследники ArgumentNullException, ArgumentOutOfRangeException) — когда плохой входной аргумент. InvalidOperationException — когда аргументы нормальные, но вызов неуместен в текущем состоянии объекта: например, First() на пустой коллекции или изменение коллекции во время foreach. Для проверок есть помощники ArgumentNullException.ThrowIfNull и ArgumentException.ThrowIfNullOrEmpty.","code":"public void Ship(Order order)\n{\n    ArgumentNullException.ThrowIfNull(order);\n    if (order.Status != OrderStatus.Paid)\n        throw new InvalidOperationException(\"Only paid orders can be shipped.\");\n}","origin":"authored"},{"id":"be-debugging/throw-vs-throw-ex","topic":"be-debugging","level":"junior","concept":"dotnet-exceptions","q":"Чем throw; отличается от throw ex; в блоке catch?","a":"throw; пробрасывает то же исключение и сохраняет исходный stack trace. throw ex; начинает stack trace заново с текущего места, и строка, где ошибка случилась на самом деле, теряется. Если нужно добавить контекст, бросай новое исключение и передавай исходное как innerException.","origin":"authored"},{"id":"be-debugging/exception-handler","topic":"be-debugging","level":"junior","concept":"aspnet-error-handling","q":"Как в ASP.NET Core API централизованно обработать необработанные исключения?","a":"Подключить AddProblemDetails и UseExceptionHandler: middleware ловит исключение и возвращает 500 в формате ProblemDetails (RFC 9457) без stack trace. UseStatusCodePages добавляет такое же тело к пустым ответам 4xx и 5xx. Developer Exception Page с деталями ошибки показывают только в Development.","code":"builder.Services.AddProblemDetails();\nvar app = builder.Build();\n\napp.UseExceptionHandler();\napp.UseStatusCodePages();","origin":"authored"},{"id":"be-debugging/iexceptionhandler","topic":"be-debugging","level":"middle","concept":"aspnet-error-handling","q":"Как работает IExceptionHandler и что изменилось в .NET 10?","a":"Реализуешь TryHandleAsync и регистрируешь AddExceptionHandler<T> (Singleton); обработчики вызываются по порядку, пока один не вернёт true. Без UseExceptionHandler они не вызываются. В .NET 10 для исключений, которые обработчик пометил как обработанные, middleware по умолчанию больше не пишет логи и метрики; старое поведение возвращает SuppressDiagnosticsCallback.","origin":"authored"},{"id":"be-debugging/log-levels","topic":"be-debugging","level":"junior","concept":"dotnet-logging","q":"Какие уровни логирования есть в ILogger и как ими управлять?","a":"По возрастанию важности: Trace, Debug, Information, Warning, Error, Critical; None выключает логи. Минимальный уровень задают в appsettings в секции Logging:LogLevel — отдельно по умолчанию и для категорий вроде Microsoft.AspNetCore. На проде обычно Information или Warning: Trace и Debug дают много шума и могут содержать чувствительные данные.","origin":"authored"},{"id":"be-debugging/structured-logging","topic":"be-debugging","level":"junior","concept":"dotnet-logging","q":"Почему в логах пишут шаблон {OrderId}, а не интерполяцию $\"...{orderId}\"?","a":"Шаблон сообщения сохраняет OrderId как отдельное поле, и в Seq, Elastic или Application Insights можно искать по нему. Интерполяция склеивает строку заранее: поле теряется, и строка создаётся даже при выключенном уровне (анализатор CA2254). Для горячих путей есть [LoggerMessage] — генерация кода на этапе компиляции.","code":"logger.LogInformation(\"Order {OrderId} paid by {UserId}\", order.Id, userId);","origin":"authored"},{"id":"be-delivery/ci-vs-cd","topic":"be-delivery","level":"middle","concept":"ci-cd-pipelines","q":"Чем отличаются Continuous Integration, Continuous Delivery и Continuous Deployment?","a":"CI — каждое изменение в общей ветке автоматически собирается и проходит тесты. Continuous Delivery — код в любой момент готов к выкладке в прод, а саму выкладку запускает человек, когда бизнес решит. Continuous Deployment — каждое изменение, прошедшее pipeline, уезжает в прод автоматически.","origin":"authored"},{"id":"be-delivery/github-actions","topic":"be-delivery","level":"middle","concept":"ci-cd-pipelines","q":"Из чего состоит pipeline в GitHub Actions?","a":"Workflow — YAML-файл в .github/workflows, который запускается событием: push, pull_request, расписание. В нём jobs, которые по умолчанию идут параллельно, каждая на своём runner; job состоит из steps — shell-команд или готовых actions. Секреты берут из secrets.*, а не пишут в YAML; в Azure DevOps те же идеи называются pipeline, stage, job и task.","code":"on: [push, pull_request]\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: '10.0.x'\n      - run: dotnet test --configuration Release","origin":"authored"},{"id":"be-delivery/compose-depends-on","topic":"be-delivery","level":"middle","concept":"docker-compose","q":"API в Docker Compose падает при старте, хотя стоит depends_on: db. Почему?","a":"Короткая форма depends_on ждёт только запуска контейнера БД, а не её готовности принимать подключения. Нужно описать healthcheck у сервиса db и указать condition: service_healthy. И всё равно приложение должно уметь повторять подключение, потому что в проде такого порядка запуска нет.","code":"services:\n  api:\n    build: .\n    depends_on:\n      db:\n        condition: service_healthy\n  db:\n    image: postgres:17\n    environment:\n      POSTGRES_PASSWORD: example\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n      interval: 5s\n      retries: 10","origin":"authored"},{"id":"be-delivery/multi-stage-dockerfile","topic":"be-delivery","level":"middle","concept":"dotnet-containers","q":"Зачем multi-stage Dockerfile для .NET-приложения?","a":"В первой стадии на образе SDK делают restore и publish, во второй копируют только результат в небольшой образ ASP.NET runtime. Итоговый образ меньше и безопаснее: в нём нет SDK, исходников и инструментов сборки. В .NET есть и вариант без Dockerfile — dotnet publish /t:PublishContainer.","code":"FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build\nWORKDIR /src\nCOPY . .\nRUN dotnet publish -c Release -o /app\n\nFROM mcr.microsoft.com/dotnet/aspnet:10.0\nWORKDIR /app\nCOPY --from=build /app .\nENTRYPOINT [\"dotnet\", \"Api.dll\"]","origin":"authored"},{"id":"be-delivery/k8s-core-objects","topic":"be-delivery","level":"middle","concept":"k8s-basics","q":"Что такое Pod, Deployment и Service в Kubernetes?","a":"Pod — минимальная единица запуска: один или несколько контейнеров с общей сетью и хранилищем; Pod смертен и при пересоздании получает новый IP. Deployment описывает желаемое состояние — образ и число реплик — и через ReplicaSet поддерживает и обновляет Pod'ы. Service даёт стабильное имя и адрес и балансирует трафик между Pod'ами, выбранными по label selector.","origin":"authored"},{"id":"be-delivery/configmap-vs-secret","topic":"be-delivery","level":"middle","concept":"k8s-basics","q":"Чем ConfigMap отличается от Secret и защищён ли Secret?","a":"ConfigMap — для несекретной конфигурации, Secret — для паролей, токенов и ключей; оба передают в Pod как переменные окружения или файлы. Но Secret по умолчанию хранится в etcd незашифрованным, а base64 — это кодирование, не шифрование. Поэтому включают шифрование at rest, ограничивают доступ через RBAC и часто берут секреты из внешнего хранилища через CSI-драйвер.","origin":"authored"},{"id":"be-delivery/liveness-readiness","topic":"be-delivery","level":"middle","concept":"health-checks","q":"Чем liveness отличается от readiness и как их сделать в ASP.NET Core?","a":"Liveness отвечает на вопрос «процесс жив?» — при провале Kubernetes перезапускает контейнер. Readiness — «готов принимать трафик?» — при провале на Pod перестают слать трафик, но не перезапускают его. В ASP.NET Core это AddHealthChecks() и два MapHealthChecks с фильтром по тегам (Predicate); проверки БД и других зависимостей кладут только в readiness, иначе сбой БД перезапустит все Pod'ы.","origin":"authored"},{"id":"be-delivery/deployment-strategies","topic":"be-delivery","level":"middle","concept":"deployment-strategies","q":"Чем отличаются rolling update, blue-green и canary?","a":"Rolling update постепенно заменяет старые Pod'ы новыми — это стратегия Deployment по умолчанию: без простоя, но какое-то время работают обе версии. Blue-green держит две полные среды и переключает трафик целиком: откат мгновенный, но ресурсов нужно вдвое больше. Canary отдаёт новой версии небольшую долю пользователей и расширяет её, пока метрики в норме. При всех трёх схема БД должна подходить и старой, и новой версии.","origin":"authored"},{"id":"be-delivery/managed-identity","topic":"be-delivery","level":"middle","concept":"cloud-basics","q":"Как приложение в Azure получает доступ к другим ресурсам без пароля в конфиге?","a":"Через Managed Identity: Azure выдаёт приложению идентичность в Microsoft Entra ID и сам управляет её учётными данными. Приложение получает Entra-токен и с ним обращается к Key Vault, Storage или базе, а доступ выдают ролями на конкретный ресурс. Секретов, которые можно забыть ротировать или слить, просто нет.","origin":"authored"},{"id":"be-distributed/outbox","topic":"be-distributed","level":"senior","concept":"outbox-inbox","q":"Какую проблему решает transactional outbox?","a":"Проблему dual write: без 2PC нельзя атомарно записать в БД и отправить сообщение в брокер. Сервис пишет событие в таблицу outbox в той же транзакции, что и бизнес-данные, а отдельный relay (polling или чтение лога транзакций) публикует его в брокер. Сообщение уходит тогда и только тогда, когда транзакция закоммичена, но может уйти дважды — поэтому потребители должны быть идемпотентными.","code":"db.Orders.Add(order);\ndb.OutboxMessages.Add(OutboxMessage.From(new OrderPlaced(order.Id)));\nawait db.SaveChangesAsync(ct); // одна транзакция: и заказ, и событие","origin":"authored"},{"id":"be-distributed/inbox","topic":"be-distributed","level":"senior","concept":"outbox-inbox","q":"Как сделать идемпотентного потребителя сообщений (inbox)?","a":"Брокер с доставкой at-least-once может прислать одно сообщение несколько раз. Потребитель в одной транзакции записывает MessageId в таблицу обработанных сообщений с уникальным ключом и делает бизнес-изменение. Если вставка ключа падает на дубликате, транзакцию откатывают, а сообщение просто подтверждают.","origin":"authored"},{"id":"be-distributed/exactly-once","topic":"be-distributed","level":"senior","concept":"idempotent-processing","q":"Можно ли получить exactly-once доставку между сервисами?","a":"Через сеть с возможными сбоями гарантированную доставку ровно один раз не получить: либо рискуем потерять сообщение (at-most-once), либо повторяем его (at-least-once). На практике выбирают at-least-once и делают обработку идемпотентной, чтобы повтор давал тот же результат, что и одна обработка. Это и называют «effectively once».","origin":"authored"},{"id":"be-distributed/idempotency-key","topic":"be-distributed","level":"senior","concept":"idempotent-processing","q":"Как сделать POST-запрос безопасным для повтора?","a":"Клиент отправляет заголовок Idempotency-Key с уникальным значением, а сервер сохраняет ключ и результат первой обработки. Повтор с тем же ключом получает сохранённый ответ; тот же ключ с другим телом — 422, а повтор во время обработки первого запроса — 409. Это IETF draft, а не RFC, поэтому правила стоит явно описать в документации своего API.","origin":"authored"},{"id":"be-distributed/saga","topic":"be-distributed","level":"senior","concept":"saga-compensation","q":"Что такое сага и чем хореография отличается от оркестрации?","a":"Сага — цепочка локальных транзакций в разных сервисах вместо одной распределённой транзакции. При хореографии сервисы реагируют на события друг друга без центра, при оркестрации оркестратор отправляет команды и ждёт ответы. Хореография проще на коротких сценариях, а оркестрация лучше видна и управляется на длинных.","origin":"authored"},{"id":"be-distributed/compensation","topic":"be-distributed","level":"senior","concept":"saga-compensation","q":"Что такое компенсирующая транзакция и почему это не rollback?","a":"Компенсация — отдельная бизнес-операция, которая отменяет эффект уже закоммиченного шага: вернуть деньги, снять бронь. Она не возвращает систему точно в исходное состояние, потому что параллельно могли пройти другие изменения. Шаги компенсации делают идемпотентными, потому что при сбое их могут повторить.","origin":"authored"},{"id":"be-distributed/saga-isolation","topic":"be-distributed","level":"senior","concept":"saga-compensation","q":"Какую гарантию ACID теряет сага и как с этим живут?","a":"Сага теряет изоляцию (I): другие запросы видят промежуточное состояние, и возможны аномалии данных. С этим работают через контрмеры на уровне дизайна, например статус PENDING как семантическую блокировку или перечитывание значения перед изменением. Контрмеры выбирают для каждой саги отдельно, исходя из бизнес-рисков.","origin":"authored"},{"id":"be-distributed/event-styles","topic":"be-distributed","level":"senior","concept":"event-driven-styles","q":"Какие бывают виды event-driven взаимодействия?","a":"Event notification только сообщает, что что-то изменилось, а за деталями получатель идёт к источнику. Event-carried state transfer несёт данные в событии, и получатель хранит свою копию без запросов к источнику. Event sourcing хранит все изменения как события и восстанавливает состояние их проигрыванием; CQRS разделяет модели чтения и записи и часто идёт рядом.","origin":"authored"},{"id":"be-distributed/service-mesh","topic":"be-distributed","level":"senior","concept":"service-mesh-traffic","q":"Что даёт service mesh и когда он оправдан?","a":"Service mesh (Istio, Linkerd) выносит в инфраструктуру mTLS между сервисами, управление трафиком (canary, разделение по процентам, восстановление после сбоев) и телеметрию — без изменений кода. Работает через прокси: sidecar рядом с каждым подом или ambient-режим в Istio. Он оправдан, когда сервисов много и нужен zero-trust; для нескольких сервисов это часто лишняя операционная сложность.","origin":"authored"},{"id":"be-distributed/own-k8s-operator","topic":"be-distributed","level":"lead","concept":"k8s-operators","q":"Команда хочет написать свой Kubernetes operator для PostgreSQL. Что посоветуешь?","a":"Operator — это custom resource плюс контроллер, который в цикле приводит реальное состояние к описанному и автоматизирует day-2 операции: бэкапы, восстановление, обновления, выбор лидера. Свой operator — это сложное ПО, которым команда будет владеть всегда, поэтому сначала смотрю зрелые готовые операторы и managed-базу у облака. Писать свой оправдано, если это наш собственный продукт с уникальной логикой эксплуатации. Для stateless-сервисов хватает Helm и обычных Deployment.","origin":"authored"},{"id":"be-distributed/multi-region-replication","topic":"be-distributed","level":"lead","concept":"multi-region-data","q":"Вы выходите во второй регион. Как решишь, какие данные реплицировать синхронно, а какие асинхронно?","a":"Расстояние между регионами — это задержка: синхронная репликация даёт согласованность, но каждая запись ждёт другой регион и зависит от его доступности. Асинхронная оставляет запись быстрой, но при отказе региона теряются данные в пределах лага (RPO больше нуля), и после failover нужна сверка. Решаю по типу данных: деньги и остатки — один регион-владелец на запись или синхронная репликация, каталог и профили — асинхронно с eventual consistency. Меряю лаг репликации как фактический RPO и проверяю его на учениях.","origin":"authored"},{"id":"be-distributed/event-schema-evolution","topic":"be-distributed","level":"lead","concept":"event-contracts","q":"Как вести эволюцию схем событий, если их читают десять команд?","a":"Схема события — публичный контракт: храню её в schema registry, а проверка совместимости идёт в CI продюсера. Режим по умолчанию BACKWARD: можно удалять поля и добавлять поля со значением по умолчанию, а потребители обновляются первыми. Потребители читают как tolerant reader — берут только нужные поля и игнорируют новые. Ломающее изменение — это новая версия события с периодом параллельной публикации и consumer-driven contract tests.","origin":"authored"},{"id":"be-distributed/cell-based-blast-radius","topic":"be-distributed","level":"lead","concept":"high-availability","q":"Плохой релиз или «отравленный» запрос кладёт сервис сразу для всех клиентов. Как ограничить радиус поражения?","a":"Cell-based архитектура: делю нагрузку по ключу (например, customer ID) на независимые ячейки со своей копией сервиса и данных, а тонкий cell router только направляет запросы. Отказ или плохой деплой задевает одну ячейку: при 10 ячейках 90% запросов не страдают, а релизы выкатываются по ячейкам волнами. Цена — control plane для создания ячеек и переноса клиентов, поэтому ключ выбираю так, чтобы запросов между ячейками почти не было. Успех меряю долей клиентов, задетых типичным инцидентом.","origin":"authored"},{"id":"be-enterprise/hundred-percent","topic":"be-enterprise","level":"principal","concept":"slo-error-budget","q":"Бизнес просит «100% доступности». Как ты ответишь?","a":"100% — неправильная цель: её нельзя достичь, и пользователи не заметят разницы между четырьмя и пятью девятками. Каждая следующая девятка стоит на порядок дороже, а 99.99% — это около 52 минут простоя в год. Договариваюсь о SLO по реальной потребности, а разница до 100% становится error budget на релизы и эксперименты.","origin":"authored"},{"id":"be-enterprise/ha-ft-dr","topic":"be-enterprise","level":"principal","concept":"high-availability","q":"Чем high availability отличается от fault tolerance и disaster recovery?","a":"High availability — система быстро восстанавливается после отказа компонента, короткий простой допустим. Fault tolerance — работа продолжается без заметного сбоя за счёт полного резервирования, и это дороже. Disaster recovery — план на потерю целого региона: backup and restore, pilot light, warm standby или multi-site active/active, от дешёвого с большим RTO до дорогого с почти нулевым.","origin":"authored"},{"id":"be-enterprise/policy-as-code","topic":"be-enterprise","level":"principal","concept":"policy-as-code","q":"Что такое policy as code и как бы ты применил OPA в компании?","a":"Политики — что разрешено, кому и где — пишут как код на Rego, хранят в git, ревьюят и тестируют. OPA отделяет принятие решения от исполнения: сервис или инструмент отправляет JSON input и получает решение. Типичные точки: admission controller в Kubernetes (например, запрет контейнеров от root), проверка Terraform plan в CI и авторизация в API-шлюзе.","origin":"authored"},{"id":"be-enterprise/iac-at-scale","topic":"be-enterprise","level":"principal","concept":"iac-provisioning","q":"Как бы ты организовал провижининг облака для десятков команд?","a":"Вся инфраструктура описана декларативно (Terraform, Bicep) и лежит в git; изменения идут через pull request с выводом plan на ревью, а apply делает конвейер, а не человек с ноутбука. State хранится удалённо с блокировкой, общие части оформлены в версионируемые модули. Политики проверяют plan до apply, а ручные правки в консоли запрещены, чтобы не было drift.","origin":"authored"},{"id":"be-enterprise/oltp-vs-olap","topic":"be-enterprise","level":"principal","concept":"oltp-olap","q":"Почему отчёты не стоит строить прямо по OLTP-базе и что делать вместо этого?","a":"OLTP-база нормализована и настроена на много коротких транзакций, а тяжёлые агрегаты по большим объёмам требуют много JOIN и тормозят рабочие операции. Данные переносят в OLAP-хранилище (хранилище данных, колоночные индексы, звёздная схема) через ETL/ELT или CDC. Так аналитика не мешает продакшн-нагрузке, а модель данных удобна для отчётов.","origin":"authored"},{"id":"be-enterprise/pci-scope","topic":"be-enterprise","level":"principal","concept":"compliance-scope","q":"Как бы ты уменьшил PCI DSS scope для сервиса, который принимает оплату картой?","a":"В scope входят все системы, которые хранят, обрабатывают или передают данные карт, и всё, что с ними связано. Лучший путь — не касаться номера карты вообще: платёжная страница провайдера или токенизация у сертифицированного провайдера, а у себя храним только токены. Одного шифрования мало: по позиции PCI SSC оно само по себе не выводит данные карт из scope.","origin":"authored"},{"id":"be-enterprise/pdpa-breach","topic":"be-enterprise","level":"principal","concept":"compliance-scope","q":"Что PDPA (Сингапур) требует при утечке персональных данных и как это влияет на архитектуру?","a":"Организация должна быстро оценить утечку (не дольше 30 календарных дней) и, если она подлежит уведомлению — значительный вред или 500 и больше человек, — сообщить регулятору не позже 3 календарных дней после этого решения. Значит, журналы доступа, карта персональных данных и runbook для инцидентов нужны заранее. Минимизация данных и сроки хранения уменьшают масштаб возможной утечки.","origin":"authored"},{"id":"be-enterprise/build-vs-buy","topic":"be-enterprise","level":"principal","concept":"build-vs-buy","q":"Как бы ты решал build vs buy: свой сервис, SaaS, PaaS или open source?","a":"Сначала спрашиваю, отличает ли эта функция наш бизнес от конкурентов. Utility — биллинг, HR, почта — покупаем SaaS/PaaS или берём зрелый open source и подстраиваем процесс под инструмент; strategic, то есть источник преимущества, строим сами. Дальше сравниваю полную стоимость владения, lock-in, требования compliance к данным и то, кто будет поддерживать решение через несколько лет.","origin":"authored"},{"id":"be-enterprise/cicd-platform","topic":"be-enterprise","level":"principal","concept":"platform-thinking","q":"Как бы ты строил CI/CD и своё tooling на уровне всей компании?","a":"Как внутренний продукт с владельцем и roadmap: self-service шаблоны конвейеров, готовый путь для нового сервиса и общие шаги безопасности (SAST, NuGet Audit, сканирование образов). Свой инструмент пишу только там, где готовые решения не закрывают потребность, и измеряю эффект: lead time, частоту деплоев, долю сервисов на стандартном пути. Команды могут отойти от стандарта, но тогда сами поддерживают своё решение.","origin":"authored"},{"id":"be-enterprise/strangler-fig-modernization","topic":"be-enterprise","level":"principal","concept":"strangler-fig","q":"Как бы ты модернизировал 15-летний монолит, если бизнес не готов ждать два года полного переписывания?","a":"Полное переписывание обычно проваливается: оно идёт годами, а пользователи не могут ждать новых функций. Выбираю strangler fig: перед монолитом ставлю фасад-маршрутизатор и по частям переношу функциональность в новый код, начиная с мест с наибольшей ценностью или болью. Временная архитектура — фасад, синхронизация данных, адаптеры — осознанная цена за меньший риск, в конце её удаляют. Успех меряю долей трафика на новом коде, частотой релизов и тем, что старые модули реально выключаются.","origin":"authored"},{"id":"be-enterprise/tenant-isolation-model","topic":"be-enterprise","level":"principal","concept":"multi-tenancy","q":"Как выбрать модель изоляции для multi-tenant SaaS: общая база для всех или отдельная на клиента?","a":"Это шкала от дешёвого к изолированному. Общая база с tenant_id даёт максимальную плотность и простые обновления, но риск утечки между клиентами и noisy neighbor. Отдельная база или целый stamp на клиента — сильная изоляция и проще требования регуляторов, но дороже и сложнее в эксплуатации. Чаще выбираю смешанную модель: мелкие клиенты в общей инфраструктуре, крупные и регулируемые — на выделенной, всё с автоматическим провижинингом. Изоляцию обязательно проверяю тестами: запрос одного тенанта никогда не видит данные другого.","origin":"authored"},{"id":"be-enterprise/noisy-neighbor","topic":"be-enterprise","level":"principal","concept":"multi-tenancy","q":"Один крупный клиент своей нагрузкой замедляет всех остальных. Что сделаешь?","a":"Сначала видимость: метрики и потребление ресурсов в разрезе тенанта, иначе виновника не найти. Затем защита общих ресурсов: квоты и rate limiting на тенанта, throttling, тяжёлые фоновые задачи — в очередь и на непиковое время. Если клиент стабильно большой, переношу его на выделенный stamp или более изолированный тариф — это решение вместе с бизнесом, а не только техническое. Успех: задержка у остальных клиентов не зависит от пиков одного.","origin":"authored"},{"id":"be-enterprise/finops-governance","topic":"be-enterprise","level":"principal","concept":"finops-governance","q":"Облачный счёт растёт быстрее выручки. Как бы ты выстроил cost governance?","a":"По фазам FinOps. Inform — теги и распределение затрат по командам и продуктам, showback, чтобы каждая команда видела свои деньги. Optimize — rightsizing, выключение лишнего, commitments для стабильной базовой нагрузки. Operate — бюджеты, алерты на аномалии и стоимость как требование в design review. Главная метрика — не общий счёт, а unit cost, например стоимость одного заказа: при росте бизнеса она должна падать.","origin":"authored"},{"id":"be-enterprise/advice-process","topic":"be-enterprise","level":"principal","concept":"architecture-governance","q":"Как управлять архитектурными решениями в 30 командах, чтобы архитектор не стал узким местом?","a":"Использую advice process: решение может принять любой инженер, но сначала обязан спросить совета у тех, кого оно затронет, и у экспертов, и записать всё в ADR рядом с кодом. Раз в неделю — открытый architecture advisory forum, где решения обсуждают публично, плюс свой tech radar как рекомендация, а не запрет. Ключевые правила проверяю автоматически fitness functions в CI. Меряю время от вопроса до решения и сколько решений пришлось пересмотреть, потому что не спросили нужных людей.","origin":"authored"},{"id":"be-enterprise/data-mesh-or-not","topic":"be-enterprise","level":"principal","concept":"data-platform-strategy","q":"Центральная data-команда стала узким местом для аналитики. Переходить на data mesh?","a":"Зависит от масштаба. Data mesh — это четыре принципа вместе: владение данными у доменов, данные как продукт, self-serve платформа и федеративное computational governance. Он окупается, когда доменов, источников и потребителей много и центральная команда реально тормозит изменения; при двух-трёх доменах хватает хорошо организованного lake или warehouse. Без платформы и владельцев данных в доменах mesh превращается в хаос, поэтому начинаю с одного-двух доменов как пилота и меряю время от вопроса до готового датасета.","origin":"authored"},{"id":"be-enterprise/right-to-erasure","topic":"be-enterprise","level":"principal","concept":"privacy-erasure","q":"Как выполнить право на удаление (GDPR) в системе с бэкапами, событиями и аналитикой?","a":"Нужна карта данных: где лежат персональные данные клиента — базы, логи, топики, аналитика, бэкапы, внешние получатели. Удалять надо без неоправданной задержки (на ответ — максимум месяц) и уведомить получателей, но есть исключения, например обязанность хранить данные по закону. Бэкапы сразу не переписать — их выводят из использования до плановой ротации, а в неизменяемых журналах событий помогает crypto-shredding: шифровать данные ключом на пользователя и удалять ключ. Процесс автоматизирую и логирую, чтобы можно было доказать выполнение.","origin":"authored"},{"id":"be-enterprise/cdc-to-analytics","topic":"be-enterprise","level":"principal","concept":"oltp-olap","q":"Как передавать данные из OLTP-базы в аналитику, не нагружая продакшен?","a":"Не запросами к рабочей базе по расписанию: они нагружают её и теряют удаления. Выбираю log-based CDC (например, Debezium): изменения читаются из журнала транзакций, ловятся все insert, update и delete с задержкой в миллисекунды, а схему таблиц менять не нужно. События идут через Kafka в lake или warehouse. Цена — ещё одна инфраструктура и привязка аналитики к схеме БД, поэтому для событий между сервисами предпочитаю outbox с явным контрактом.","origin":"authored"},{"id":"be-enterprise/engineering-strategy","topic":"be-enterprise","level":"principal","concept":"architecture-governance","q":"Как бы ты написал техническую стратегию инженерии на ближайшие два года?","a":"Стратегия — не список желаний, а диагноз текущей ситуации, guiding policies с явными компромиссами и согласованные действия. Практичный путь — взять несколько недавних design docs, найти повторяющиеся решения и записать их как правила; vision — это уже проекция таких стратегий на два-три года вперёд. Пишу конкретно и с обоснованием, чтобы по документу можно было решать споры. Успех — одни и те же споры перестают повторяться, а новые design docs ссылаются на стратегию.","origin":"authored"},{"id":"be-git-docker/merge-vs-rebase","topic":"be-git-docker","level":"junior","concept":"git-history","q":"Чем merge отличается от rebase?","a":"merge соединяет две ветки новым merge-коммитом и сохраняет историю как была. rebase переносит твои коммиты поверх другой ветки и создаёт их заново — история получается линейной. Итоговый код одинаковый, разница только в истории.","origin":"authored"},{"id":"be-git-docker/rebase-rule","topic":"be-git-docker","level":"junior","concept":"git-history","q":"Когда rebase делать нельзя?","a":"Нельзя перебазировать коммиты, которые уже опубликованы и на которых другие могли строить работу: rebase меняет их хеши, и у коллег история разойдётся. Своя локальная ветка до push — нормальный случай для rebase. Общую ветку обновляют через merge, а ошибку в ней откатывают через revert.","origin":"authored"},{"id":"be-git-docker/pull-request","topic":"be-git-docker","level":"junior","concept":"pull-requests","q":"Зачем нужен pull request?","a":"Pull request — предложение влить изменения из твоей ветки в основную. В нём коллеги обсуждают и ревьюят код, а CI запускает сборку и тесты до слияния. Незаконченную работу открывают как draft: его нельзя смёржить, и ревьюеров не зовут автоматически.","origin":"authored"},{"id":"be-git-docker/image-vs-container","topic":"be-git-docker","level":"junior","concept":"docker-image-container","q":"Чем Docker-образ отличается от контейнера?","a":"Образ — неизменяемый шаблон из слоёв: файлы, зависимости и настройки для запуска приложения. Контейнер — изолированный процесс, запущенный из образа; из одного образа можно запустить много контейнеров. Образ — как класс, контейнер — как его экземпляр.","origin":"authored"},{"id":"be-git-docker/container-vs-vm","topic":"be-git-docker","level":"junior","concept":"docker-image-container","q":"Чем контейнер отличается от виртуальной машины?","a":"Виртуальная машина содержит целую ОС со своим ядром, поэтому она тяжёлая и долго стартует. Контейнер — изолированный процесс со своими файлами, а ядро общее с хостом. Поэтому контейнеры лёгкие, и на одной машине их помещается гораздо больше.","origin":"authored"},{"id":"be-git-docker/multi-stage","topic":"be-git-docker","level":"junior","concept":"dotnet-containers","q":"Зачем в Dockerfile для .NET делают multi-stage сборку?","a":"В первой стадии на большом образе sdk собирают и публикуют приложение. Во вторую стадию на маленьком образе aspnet копируют только результат публикации через COPY --from. В итоговом образе нет SDK и исходников: он меньше и безопаснее.","code":"FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build\nWORKDIR /src\nCOPY *.csproj ./\nRUN dotnet restore\nCOPY . ./\nRUN dotnet publish -c Release -o /app/publish\n\nFROM mcr.microsoft.com/dotnet/aspnet:10.0\nWORKDIR /app\nCOPY --from=build /app/publish .\nENTRYPOINT [\"dotnet\", \"Orders.Api.dll\"]","origin":"authored"},{"id":"be-git-docker/layer-cache","topic":"be-git-docker","level":"middle","concept":"docker-image-container","q":"Почему в Dockerfile сначала копируют .csproj и делают restore, а потом весь код?","a":"Если слой изменился, Docker пересобирает его и все слои после него. .csproj меняется редко, поэтому слой с dotnet restore берётся из кэша, а при правке кода пересобирается только сборка. Файл .dockerignore убирает из контекста bin, obj и .git, чтобы они не ломали кэш.","origin":"authored"},{"id":"be-git-docker/publish-container","topic":"be-git-docker","level":"junior","concept":"dotnet-containers","q":"Можно ли собрать образ .NET-приложения без Dockerfile?","a":"Да: .NET SDK сам собирает образ командой dotnet publish с целью PublishContainer. Имя образа по умолчанию — AssemblyName, его меняют свойством ContainerRepository. Образ можно отправить в локальный Docker или Podman, сохранить в tar.gz (ContainerArchiveOutputPath) или сразу загрузить в registry (ContainerRegistry).","code":"dotnet publish --os linux --arch x64 /t:PublishContainer","origin":"authored"},{"id":"be-git-docker/port-8080","topic":"be-git-docker","level":"middle","concept":"dotnet-containers","q":"Почему ASP.NET Core в контейнере слушает порт 8080, а не 80?","a":"С .NET 8 образы ASP.NET Core по умолчанию слушают 8080. Причина — запуск от не-root пользователя: такой пользователь не может открыть привилегированный порт 80. Порт меняют переменной ASPNETCORE_HTTP_PORTS, и не забудь поправить маппинг портов в docker run или Kubernetes.","origin":"authored"},{"id":"be-http-rest/status-classes","topic":"be-http-rest","level":"junior","concept":"http-status-codes","q":"Какие есть классы HTTP-статусов и какие коды чаще всего возвращает API?","a":"1xx — информационные, 2xx — успех, 3xx — перенаправление, 4xx — ошибка клиента, 5xx — ошибка сервера. Типичный набор API: 200 OK, 201 Created, 204 No Content, 400 Bad Request, 401, 403, 404 Not Found, 409 Conflict, 500.","origin":"authored"},{"id":"be-http-rest/401-vs-403","topic":"be-http-rest","level":"junior","concept":"http-status-codes","q":"Чем 401 Unauthorized отличается от 403 Forbidden?","a":"401 — нет валидных учётных данных: клиент не аутентифицирован, сервер шлёт заголовок WWW-Authenticate. 403 — сервер знает, кто ты, но прав нет; повторный логин не поможет. 401 — «кто ты?», 403 — «тебе нельзя».","origin":"authored"},{"id":"be-http-rest/problem-details","topic":"be-http-rest","level":"junior","concept":"http-status-codes","q":"Как API должен возвращать ошибки, и что такое Problem Details?","a":"Стандарт RFC 9457: JSON-объект с полями type, title, status, detail, instance и media type application/problem+json. В ASP.NET Core его включает builder.Services.AddProblemDetails(), а UseExceptionHandler превращает исключения в такой ответ.","origin":"authored"},{"id":"be-http-rest/put-vs-patch","topic":"be-http-rest","level":"junior","concept":"http-rest-design","q":"Чем PUT отличается от PATCH, и что значит идемпотентность?","a":"Идемпотентный запрос при повторе оставляет ресурс в том же состоянии. PUT полностью заменяет ресурс и всегда идемпотентен. PATCH — набор изменений, он не обязательно идемпотентен (например, «увеличить счётчик»). POST тоже не идемпотентен.","origin":"authored"},{"id":"be-http-rest/rest-maturity","topic":"be-http-rest","level":"junior","concept":"http-rest-design","q":"Что такое REST и модель зрелости Richardson?","a":"REST — стиль API вокруг ресурсов с URL и стандартных методов HTTP. Модель Richardson: уровень 0 — один endpoint для всего, 1 — отдельные ресурсы, 2 — HTTP-методы и статусы по назначению, 3 — гипермедиа (ссылки в ответах). Большинство API на практике — уровень 2.","origin":"authored"},{"id":"be-http-rest/content-negotiation","topic":"be-http-rest","level":"junior","concept":"http-content-negotiation","q":"Как клиент и сервер договариваются о формате данных (JSON или XML)?","a":"Клиент шлёт заголовок Accept (например, application/json), а формат тела запроса указывает в Content-Type. Сервер выбирает формат ответа. Если подходящего нет — 406 Not Acceptable, если формат тела не поддерживается — 415 Unsupported Media Type. В ASP.NET Core 406 отдаётся только при MvcOptions.ReturnHttpNotAcceptable = true, иначе берётся первый подходящий форматтер (обычно JSON).","origin":"authored"},{"id":"be-http-rest/json-aspnet","topic":"be-http-rest","level":"junior","concept":"http-content-negotiation","q":"Какой формат ответа у ASP.NET Core API по умолчанию, и как добавить XML?","a":"По умолчанию JSON через System.Text.Json, имена свойств в camelCase. XML для контроллеров включают через AddXmlSerializerFormatters(), после этого работает выбор по заголовку Accept. JSON по RFC 8259 между системами передают в UTF-8.","code":"builder.Services.AddControllers().AddXmlSerializerFormatters();","origin":"authored"},{"id":"be-http-rest/https-hsts","topic":"be-http-rest","level":"junior","concept":"https-tls","q":"Что такое HTTPS и как его включают в ASP.NET Core?","a":"HTTPS — это HTTP поверх TLS: трафик зашифрован, сервер подтверждает себя сертификатом. В ASP.NET Core UseHttpsRedirection перенаправляет HTTP на HTTPS, а UseHsts (не в Development) говорит браузеру ходить только по HTTPS.","code":"if (!app.Environment.IsDevelopment()) app.UseHsts();\napp.UseHttpsRedirection();","origin":"authored"},{"id":"be-messaging-cache/cache-aside","topic":"be-messaging-cache","level":"middle","concept":"cache-aside","q":"Как работает паттерн cache-aside?","a":"Приложение сначала ищет данные в кеше; при промахе читает их из БД и кладёт в кеш со временем жизни. При изменении данных приложение пишет в БД и удаляет (инвалидирует) ключ в кеше. Кеш ничего не знает о БД — всю логику держит приложение.","origin":"authored"},{"id":"be-messaging-cache/cache-problems","topic":"be-messaging-cache","level":"middle","concept":"cache-aside","q":"Какие типичные проблемы у кеша и как с ними справляются?","a":"Устаревшие данные — задают TTL и удаляют ключ при записи в БД. Cache stampede — когда истекает популярный ключ, сотни запросов разом идут в БД; спасает одна загрузка на ключ, а HybridCache делает это сам. И кеш — не источник истины: приложение должно работать, пусть медленнее, если Redis недоступен.","origin":"authored"},{"id":"be-messaging-cache/memory-vs-distributed","topic":"be-messaging-cache","level":"middle","concept":"aspnet-caching-abstractions","q":"Чем IMemoryCache отличается от IDistributedCache?","a":"IMemoryCache живёт в памяти процесса: очень быстрый и хранит объекты, но у каждого инстанса свой кеш, и он пропадает при рестарте. IDistributedCache — общий внешний кеш (Redis, SQL Server) для всех инстансов, он переживает рестарт и деплой, но работает с byte[] и требует сериализации и сетевого вызова. Для нескольких инстансов за балансировщиком нужен распределённый кеш или HybridCache.","origin":"authored"},{"id":"be-messaging-cache/hybrid-cache","topic":"be-messaging-cache","level":"middle","concept":"aspnet-caching-abstractions","q":"Что такое HybridCache и чем он удобнее IDistributedCache?","a":"HybridCache (пакет Microsoft.Extensions.Caching.Hybrid) объединяет два уровня: кеш в памяти процесса и вторичный IDistributedCache, например Redis. GetOrCreateAsync сам делает cache-aside и сериализацию и защищает от stampede: для одного ключа фабрика выполняется один раз, остальные ждут результат. Ещё есть теги для группового удаления — RemoveByTagAsync.","code":"builder.Services.AddHybridCache();\n\nvar product = await cache.GetOrCreateAsync(\n    $\"product:{id}\",\n    async token => await db.Products.FindAsync([id], token),\n    tags: [\"products\"],\n    cancellationToken: ct);","origin":"authored"},{"id":"be-messaging-cache/rabbitmq-vs-kafka","topic":"be-messaging-cache","level":"middle","concept":"message-brokers","q":"Чем RabbitMQ отличается от Kafka и когда что брать?","a":"RabbitMQ — брокер очередей: exchange раскладывает сообщения по очередям, после ack сообщение удаляется; хорош для задач и команд со сложной маршрутизацией. Kafka — распределённый лог: события не удаляются после чтения, а хранятся заданное время, консьюмеры читают по offset и могут перечитать историю. Kafka берут для потоков событий, большой пропускной способности и многих независимых читателей.","origin":"authored"},{"id":"be-messaging-cache/kafka-ordering","topic":"be-messaging-cache","level":"middle","concept":"message-brokers","q":"Как в Kafka сохранить порядок событий одного заказа?","a":"Порядок гарантирован только внутри партиции. Поэтому события одного заказа публикуют с одним ключом (OrderId): события с одинаковым ключом попадают в одну партицию и читаются в порядке записи. Внутри consumer group партицию читает один консьюмер, так что параллелизм ограничен числом партиций.","origin":"authored"},{"id":"be-messaging-cache/at-least-once","topic":"be-messaging-cache","level":"middle","concept":"idempotent-processing","q":"Что значит at-least-once и почему обработчик должен быть идемпотентным?","a":"Брокер гарантирует доставку, но возможно не один раз: если консьюмер обработал сообщение и упал до ack, сообщение придёт снова. Поэтому ack отправляют после обработки, а обработчик делают идемпотентным — например, сохраняют id обработанных сообщений в той же транзакции, что и изменения. Повторная обработка тогда ничего не ломает.","origin":"authored"},{"id":"be-messaging-cache/transactional-outbox","topic":"be-messaging-cache","level":"senior","concept":"outbox-inbox","q":"Как надёжно сохранить данные в БД и опубликовать событие в брокер?","a":"Атомарно записать в БД и отправить в брокер нельзя: одна из операций может упасть. Transactional outbox: событие пишут в таблицу outbox в той же транзакции, что и бизнес-данные, а отдельный процесс читает outbox и публикует в брокер. Доставка получается at-least-once, поэтому консьюмеры должны быть идемпотентными.","origin":"authored"},{"id":"be-messaging-cache/dead-letter","topic":"be-messaging-cache","level":"middle","concept":"message-brokers","q":"Что делать с сообщением, которое обработчик не может обработать?","a":"Не крутить его бесконечно: несколько повторов с задержкой, потом — в dead letter queue. В RabbitMQ это dead letter exchange: сообщение уходит туда при reject или nack без requeue, по истечении TTL или при переполнении очереди. DLQ мониторят, разбирают и после исправления переотправляют.","origin":"authored"},{"id":"be-observability/three-signals","topic":"be-observability","level":"senior","concept":"otel-dotnet","q":"Какие API в .NET отвечают за логи, метрики и трейсы, и при чём тут OpenTelemetry?","a":"В .NET три сигнала пишут через встроенные API: ILogger для логов, Meter для метрик, ActivitySource и Activity для трейсов. OpenTelemetry SDK собирает эти данные и экспортирует их, обычно по OTLP, в выбранный бэкенд: Prometheus, Jaeger, Azure Monitor и другие. Код инструментации не зависит от вендора — меняется только экспортёр.","origin":"authored"},{"id":"be-observability/activity-span","topic":"be-observability","level":"senior","concept":"otel-dotnet","q":"Как добавить свой span в .NET и почему StartActivity может вернуть null?","a":"Создают один ActivitySource на компонент, хранят его в статическом поле и вызывают StartActivity в using; Activity — это span в терминах OpenTelemetry. Если этот source никто не слушает (его не добавили через AddSource), StartActivity возвращает null, чтобы не тратить ресурсы. Поэтому теги ставят через activity?.SetTag(...).","code":"private static readonly ActivitySource Source = new(\"Shop.Orders\");\n\nusing var activity = Source.StartActivity(\"ReserveStock\");\nactivity?.SetTag(\"order.id\", orderId);","origin":"authored"},{"id":"be-observability/meter-instruments","topic":"be-observability","level":"senior","concept":"otel-dotnet","q":"Чем Counter отличается от Histogram и как правильно получить Meter в ASP.NET Core?","a":"Counter считает только растущее значение (число заказов), UpDownCounter — значение, которое растёт и падает, Histogram — распределение (время обработки), из него строят перцентили. В приложении с DI Meter создают через IMeterFactory, а не в статическом поле: так метрики изолированы между DI-контейнерами и их удобно проверять в тестах.","origin":"authored"},{"id":"be-observability/tag-cardinality","topic":"be-observability","level":"senior","concept":"otel-dotnet","q":"Почему нельзя класть userId или orderId в теги метрики?","a":"Каждая уникальная комбинация тегов — отдельный временной ряд, который хранят и инструмент, и бэкенд. Неограниченные значения (userId, URL с параметрами) раздувают число рядов, память и стоимость хранения. Такие данные кладут в теги span'ов или в логи, а в метриках оставляют ограниченные наборы: статус, метод, шаблон маршрута.","origin":"authored"},{"id":"be-observability/trace-context","topic":"be-observability","level":"senior","concept":"otel-dotnet","q":"Как trace переходит из одного сервиса в другой?","a":"Через HTTP-заголовок W3C traceparent: версия, trace-id (16 байт), parent-id — id span вызывающей стороны (8 байт) и флаги с битом sampled. Данные вендоров идут в заголовке tracestate. В .NET 5+ формат W3C — формат id по умолчанию, а через брокер сообщений контекст передают в заголовках самого сообщения.","origin":"authored"},{"id":"be-observability/log-templates","topic":"be-observability","level":"senior","concept":"otel-dotnet","q":"Почему в логах используют шаблоны сообщений, а не интерполяцию, и что даёт [LoggerMessage]?","a":"Шаблон «Order {OrderId} paid» сохраняет OrderId отдельным полем, по нему можно искать и фильтровать; интерполяция склеивает всё в одну строку и строит её даже при выключенном уровне логирования. Атрибут [LoggerMessage] генерирует метод логирования при компиляции: шаблон разбирается один раз, нет лишних аллокаций и боксинга, а ошибки в шаблоне показывает компилятор.","code":"public static partial class Log\n{\n    [LoggerMessage(Level = LogLevel.Information, Message = \"Order {OrderId} paid\")]\n    public static partial void OrderPaid(ILogger logger, Guid orderId);\n}","origin":"authored"},{"id":"be-observability/sli-slo-sla","topic":"be-observability","level":"senior","concept":"slo-error-budget","q":"Чем SLI, SLO и SLA отличаются друг от друга?","a":"SLI — измеряемый показатель сервиса, например доля успешных запросов или p99 задержки. SLO — целевое значение этого показателя, например 99.9% успешных запросов за 30 дней. SLA — договор с пользователями с последствиями за нарушение, а допустимая доля неудач по SLO — это error budget.","origin":"authored"},{"id":"be-observability/burn-rate-alerts","topic":"be-observability","level":"lead","concept":"slo-error-budget","q":"Как бы ты настроил алерты по SLO, чтобы не будить дежурного зря?","a":"Алерчу не на каждую ошибку, а на скорость сжигания error budget (burn rate). Беру несколько окон: например, вызов дежурного при burn rate 14.4 за 1 час с подтверждением в 5-минутном окне и тикет при burn rate 1 за 3 дня. Короткое окно подтверждает, что проблема ещё идёт, и убирает ложные срабатывания.","origin":"authored"},{"id":"be-observability/aspire-dashboard","topic":"be-observability","level":"senior","concept":"otel-dotnet","q":"Как быстро посмотреть трейсы, метрики и логи локально без своего стека мониторинга?","a":"Запустить standalone Aspire dashboard в контейнере (mcr.microsoft.com/dotnet/aspire-dashboard) и направить на него OTLP-экспорт приложения; AppHost и остальной Aspire для этого не нужны. UI открывается на порту 18888. Это инструмент для разработки и короткой диагностики: по умолчанию данные теряются при остановке, для продакшна нужен полноценный бэкенд.","origin":"authored"},{"id":"be-observability/strategy-many-teams","topic":"be-observability","level":"lead","concept":"observability-strategy","q":"Как бы ты выстроил единую observability-стратегию для 15 команд?","a":"Стандарт — OpenTelemetry: общий пакет с настройкой SDK, атрибутами ресурса (service.name, версия, окружение) и semantic conventions, чтобы метрики и трейсы разных команд назывались одинаково. Телеметрия идёт через общий Collector, поэтому бэкенд можно сменить без правок в коде сервисов. От каждой команды требую SLO, базовый дашборд и runbook для сервиса. Меряю время обнаружения и диагностики инцидентов и долю сервисов на стандарте.","origin":"authored"},{"id":"be-observability/production-readiness","topic":"be-observability","level":"lead","concept":"observability-strategy","q":"Какой минимум мониторинга ты требуешь от сервиса перед выходом в прод?","a":"Дашборд по четырём golden signals: latency, traffic, errors, saturation. Алерты, которые будят человека, — только по симптомам, видимым пользователю, и каждый с runbook; если реакция механическая, это автоматизация или тикет, а не page. Плюс сквозные трейсы с передачей контекста и структурные логи с trace id. Проверяю на game day: дежурный, который не писал сервис, находит причину сбоя по этим данным.","origin":"authored"},{"id":"be-observability/telemetry-cost","topic":"be-observability","level":"lead","concept":"observability-strategy","q":"Счёт за observability за год вырос втрое. Что будешь делать?","a":"Сначала ищу, откуда объём: обычно это debug-логи в проде, метрики с высокой кардинальностью и 100% трейсов. Трейсы сэмплирую: head sampling в сервисах и tail sampling на gateway Collector, чтобы сохранить все ошибки и медленные запросы; для tail sampling все спаны одного трейса должны попадать в один экземпляр Collector. Для логов — уровни и сроки хранения по ценности, лишние атрибуты отрезаю в Collector. Цель — дешевле без потери диагностики: проверяю, что по последним инцидентам нужные данные остались бы.","origin":"authored"},{"id":"be-performance/span","topic":"be-performance","level":"senior","concept":"dotnet-memory-perf","q":"Зачем нужен Span<T> и какие у него ограничения?","a":"Span<T> — окно на непрерывную память (массив, stackalloc, неуправляемый буфер) без копирования и аллокаций: Slice и AsSpan только меняют ссылку и длину. Span обязан жить на стеке, поэтому его нельзя хранить в поле класса и использовать в асинхронных методах; для этого есть Memory<T>. В C# 14 появились неявные преобразования в Span и ReadOnlySpan, и код со span стал короче.","code":"ReadOnlySpan<char> line = \"2026-10-02;42\".AsSpan();\nint separator = line.IndexOf(';');\nint quantity = int.Parse(line[(separator + 1)..]); // без Substring и новой строки","origin":"authored"},{"id":"be-performance/array-pool","topic":"be-performance","level":"senior","concept":"dotnet-memory-perf","q":"Когда использовать ArrayPool<T> и какие у него подводные камни?","a":"Когда буферы часто создаются и выбрасываются и это нагружает GC: Rent берёт массив из пула, Return возвращает его. Rent может вернуть массив длиннее запрошенного, поэтому работают с нужной длиной, а не с buffer.Length. После Return массив использовать нельзя, а для чувствительных данных вызывают Return(buffer, clearArray: true).","code":"byte[] buffer = ArrayPool<byte>.Shared.Rent(minimumLength);\ntry\n{\n    int read = await stream.ReadAsync(buffer.AsMemory(0, minimumLength), ct);\n}\nfinally\n{\n    ArrayPool<byte>.Shared.Return(buffer);\n}","origin":"authored"},{"id":"be-performance/gc-modes","topic":"be-performance","level":"senior","concept":"dotnet-memory-perf","q":"Чем Server GC отличается от Workstation GC и какой режим у ASP.NET Core?","a":"Workstation GC рассчитан на клиентские приложения, а Server GC даёт отдельную кучу и поток сборки на каждый логический CPU — выше пропускная способность, но больше памяти. ASP.NET Core по умолчанию использует Server GC. С .NET 9 для Server GC по умолчанию включён DATAS: размер кучи подстраивается под объём живых данных, что особенно заметно в контейнерах.","origin":"authored"},{"id":"be-performance/valuetask","topic":"be-performance","level":"senior","concept":"dotnet-memory-perf","q":"Когда возвращать ValueTask вместо Task?","a":"Когда метод часто завершается синхронно, например отдаёт ответ из кеша: ValueTask не создаёт объект Task на этом пути. Цена — ограничения: ValueTask ждут через await только один раз, нельзя ждать его параллельно и брать результат до завершения; если нужно больше — вызывают AsTask(). По умолчанию выбирают Task, а ValueTask — когда профилирование показало выигрыш.","origin":"authored"},{"id":"be-performance/benchmarkdotnet","topic":"be-performance","level":"senior","concept":"benchmarkdotnet","q":"Как доказать, что новая версия метода быстрее и меньше аллоцирует?","a":"Через BenchmarkDotNet: метод помечают [Benchmark], класс — [MemoryDiagnoser] и запускают сборку Release без отладчика. Библиотека делает много итераций и выдаёт Mean, Error, StdDev и колонку Allocated, а с базовой версией — Ratio. Stopwatch в цикле и Debug-сборка дают шум и неверные выводы.","code":"[MemoryDiagnoser]\npublic class ParseBenchmarks\n{\n    [Benchmark(Baseline = true)] public int WithSubstring() => ParseOld(Line);\n    [Benchmark] public int WithSpan() => ParseNew(Line);\n}","origin":"authored"},{"id":"be-performance/split-query","topic":"be-performance","level":"senior","concept":"ef-core-tuning","q":"Что такое cartesian explosion в EF Core и как его лечить?","a":"Когда в одном запросе несколько Include коллекций одного уровня, JOIN перемножает строки, и по сети едет много дубликатов. AsSplitQuery() разбивает загрузку на отдельные SQL-запросы для каждой коллекции. Цена — несколько round trip и возможная несогласованность данных между запросами без serializable или snapshot-транзакции.","code":"var blogs = await db.Blogs\n    .Include(b => b.Posts)\n    .Include(b => b.Contributors)\n    .AsSplitQuery()\n    .ToListAsync(ct);","origin":"authored"},{"id":"be-performance/execute-update","topic":"be-performance","level":"senior","concept":"ef-core-tuning","q":"Когда использовать ExecuteUpdate и ExecuteDelete и что важно помнить?","a":"Для массовых изменений: EF Core переводит LINQ в один UPDATE или DELETE и не загружает сущности в память. Change tracker об этом не знает, поэтому уже загруженные сущности остаются со старыми значениями. Каждый вызов сразу идёт в БД, поэтому несколько вызовов вместе с SaveChanges оборачивают в явную транзакцию; в EF Core 10 сеттеры можно задавать обычной лямбдой, в том числе с условиями.","code":"await db.Orders\n    .Where(o => o.Status == OrderStatus.Draft && o.CreatedAt < cutoff)\n    .ExecuteUpdateAsync(s => s.SetProperty(o => o.Status, OrderStatus.Expired), ct);","origin":"authored"},{"id":"be-performance/raw-sql","topic":"be-performance","level":"senior","concept":"ef-core-tuning","q":"Как безопасно выполнить сырой SQL в EF Core?","a":"FromSql и SqlQuery с интерполированной строкой безопасны: каждое значение в {…} EF Core превращает в DbParameter. FromSqlRaw и ExecuteSqlRaw со склейкой строк открывают дорогу SQL-инъекции. Имена колонок и таблиц параметром не передать — их проверяют по белому списку, а поверх компонуемого FromSql можно продолжить LINQ.","code":"var blogs = await db.Blogs\n    .FromSql($\"SELECT * FROM dbo.SearchBlogs({searchTerm})\")\n    .Where(b => b.Rating > minRating)\n    .ToListAsync(ct);","origin":"authored"},{"id":"be-performance/compiled-query","topic":"be-performance","level":"senior","concept":"ef-core-tuning","q":"Что дают compiled queries в EF Core и когда они нужны?","a":"EF.CompileAsyncQuery один раз переводит LINQ-выражение и при каждом вызове пропускает поиск запроса во внутреннем кеше. Выигрыш небольшой на один запрос, поэтому это имеет смысл в горячих путях, где профилирование показало накладные расходы самого EF. Рядом с этим помогают DbContext pooling и параметры вместо констант в динамически собранных выражениях.","origin":"authored"},{"id":"be-resilience/standard-handler","topic":"be-resilience","level":"lead","concept":"resilience-pipeline","q":"Что делает AddStandardResilienceHandler для HttpClient?","a":"Добавляет пять стратегий от внешней к внутренней: rate limiter, общий таймаут (30 с), retry (3 повтора, экспоненциальная задержка с jitter), circuit breaker и таймаут на одну попытку (10 с). Retry и circuit breaker реагируют на 5xx, 408, 429, HttpRequestException и таймауты. Порядок важен: таймаут попытки стоит внутри retry, а общий таймаут ограничивает все попытки вместе.","code":"builder.Services.AddHttpClient<PaymentsClient>(c => c.BaseAddress = paymentsUri)\n    .AddStandardResilienceHandler();","origin":"authored"},{"id":"be-resilience/retry-unsafe-methods","topic":"be-resilience","level":"lead","concept":"resilience-pipeline","q":"Чем опасен retry для POST и как это настроить?","a":"Стандартный обработчик повторяет запросы любых HTTP-методов, и повтор POST может создать дубликат заказа или платежа. Retry отключают для небезопасных методов через DisableForUnsafeHttpMethods() или делают операцию идемпотентной через Idempotency-Key. Ещё правило: повторы держат на одном уровне, иначе вложенные retry умножают нагрузку на сервис, которому и так плохо.","code":"builder.Services.AddHttpClient<PaymentsClient>()\n    .AddStandardResilienceHandler(o => o.Retry.DisableForUnsafeHttpMethods());","origin":"authored"},{"id":"be-resilience/circuit-breaker","topic":"be-resilience","level":"lead","concept":"resilience-pipeline","q":"Как работает circuit breaker и чем он отличается от retry?","a":"В состоянии Closed запросы идут, а ошибки считаются; при превышении порога он переходит в Open и сразу отказывает, не нагружая упавший сервис. После паузы — Half-Open: пропускает несколько пробных запросов и по их результату закрывается или снова открывается. Retry надеется, что следующая попытка пройдёт, а circuit breaker защищает от попыток, которые почти наверняка упадут.","origin":"authored"},{"id":"be-resilience/hedging","topic":"be-resilience","level":"lead","concept":"resilience-pipeline","q":"Что такое hedging и когда он лучше retry?","a":"Hedging не ждёт ошибки: если ответ медленный, он параллельно отправляет ещё один запрос, в тот же или в другой endpoint, и берёт первый успешный. Это снижает хвостовую задержку для чтений, но увеличивает нагрузку и подходит только для идемпотентных операций. В .NET это AddStandardHedgingHandler, у которого свой circuit breaker на каждый endpoint.","origin":"authored"},{"id":"be-resilience/multi-region","topic":"be-resilience","level":"lead","concept":"high-availability","q":"Как бы ты выбирал между active-active и active-passive для multi-region?","a":"Active-active даёт самое быстрое восстановление и обслуживает пользователей ближе к ним, но стоит дороже всего и требует решать согласованность и конфликты записи между регионами. Active-passive (warm или cold standby) дешевле, но переключение дольше, и в резерве могут быть не самые свежие данные. Выбираю от требований бизнеса к RTO/RPO и бюджета, а failover регулярно тренирую.","origin":"authored"},{"id":"be-resilience/chaos-experiment","topic":"be-resilience","level":"lead","concept":"chaos-engineering","q":"Как бы ты начал внедрять chaos engineering в команде?","a":"Сначала определяю steady state — измеримый признак нормальной работы, например SLI успешных заказов. Формулирую гипотезу «если упадёт один узел кеша, SLI не изменится», вношу реальный сбой и пытаюсь гипотезу опровергнуть. Начинаю с малого blast radius и быстрой кнопки остановки, а потом двигаюсь к экспериментам на продакшн-трафике и их автоматическому регулярному запуску.","origin":"authored"},{"id":"be-resilience/threat-modeling","topic":"be-resilience","level":"lead","concept":"threat-modeling","q":"Как бы ты провёл threat modeling новой фичи?","a":"Отвечаю на четыре вопроса: что мы строим, что может пойти не так, что с этим делаем, достаточно ли хорошо сделали. Рисую DFD с границами доверия и по каждому потоку прохожу STRIDE: Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege. Угрозы ранжирую по вероятности и ущербу и по каждой фиксирую решение: устранить, снизить, передать или принять.","origin":"authored"},{"id":"be-resilience/incident-roles","topic":"be-resilience","level":"lead","concept":"oncall-postmortem","q":"Что ты делаешь в первые минуты крупного инцидента как лид?","a":"Объявляю инцидент рано и раздаю роли: incident commander, operations lead (только его группа меняет систему) и communications lead для регулярных апдейтов стейкхолдерам. Веду живой документ инцидента с таймлайном и решениями. Сначала восстанавливаем сервис — откат, переключение трафика, — а причину ищем потом, в постмортеме без поиска виноватых.","origin":"authored"},{"id":"be-resilience/capacity-planning-peak","topic":"be-resilience","level":"lead","concept":"capacity-overload","q":"Через два месяца ожидается сезонный пик x5 трафика. Как бы ты провёл capacity planning?","a":"Начинаю с прогноза спроса по реальным метрикам и планам бизнеса. Затем нагружаю каждый компонент до отказа, чтобы знать точку слома и узкое место — часто это БД, лимиты облака или внешний API. Ёмкость закладываю по правилу N+2: пик выдерживается даже без двух экземпляров, а квоты и пределы автоскейлинга поднимаю заранее, не в день пика. Успех — на пике SLO выполнен, а фактическая нагрузка уложилась в прогноз с запасом.","origin":"authored"},{"id":"be-resilience/cascading-overload","topic":"be-resilience","level":"lead","concept":"capacity-overload","q":"На пике один сервис перегружается, и отказ каскадом идёт по всей системе. Что сделаешь?","a":"Сначала убираю усиление нагрузки: ретраи с экспоненциальной задержкой и jitter, лимит попыток и retry budget — иначе три слоя по 4 попытки дают 64 запроса в базу. Дальше load shedding: перегруженный сервис сразу отвечает 503 на часть запросов, а не копит очередь, плюс graceful degradation с упрощённым ответом. Дедлайны передаю по цепочке, чтобы не делать работу, которую клиент уже не ждёт. Проверяю нагрузочным тестом до отказа: деградация плавная, восстановление без ручных действий.","origin":"authored"},{"id":"be-resilience/failover-drills","topic":"be-resilience","level":"lead","concept":"high-availability","q":"Второй регион для DR есть, но переключение на него ни разу не делали. Как убедиться, что оно сработает?","a":"Непроверенный failover считаю неработающим: надёжно работает только тот путь восстановления, который часто проверяют. Ввожу регулярные учения — сначала в staging, потом плановое переключение в проде в спокойное время — по runbook с автоматизацией вместо ручных шагов. Отдельно слежу за дрейфом конфигурации и квотами во втором регионе: он должен выдержать полную нагрузку. На каждом учении меряю фактические RTO и RPO и сравниваю с целями.","origin":"authored"},{"id":"be-resilience/cost-of-a-nine","topic":"be-resilience","level":"lead","concept":"slo-error-budget","q":"Команда просит квартал на переход с 99.9% на 99.99% доступности. Как ты решишь, стоит ли?","a":"Это решение про деньги: 99.9% — около 8,8 часа простоя в год, 99.99% — около 53 минут, и каждая следующая девятка обычно стоит намного дороже предыдущей. Сравниваю выгоду (сколько денег теряет бизнес за эти часы простоя) с ценой работы и инфраструктуры. Проверяю, не ограничивают ли нас зависимости с более низким SLO и заметят ли разницу пользователи. Если error budget сейчас не тратится полностью, вложение, скорее всего, не окупится — лучше потратить квартал на фичи.","origin":"authored"},{"id":"be-resilience/incident-process-at-scale","topic":"be-resilience","level":"lead","concept":"oncall-postmortem","q":"Как бы ты выстроил incident response для платформы из 20 команд?","a":"Единые правила для всех: заранее записанные критерии, когда объявлять инцидент, уровни серьёзности и принцип «объявляй рано». Роли по Incident Command System: Incident Commander руководит, Ops Lead чинит, Comms Lead информирует стейкхолдеров; сначала смягчаем последствия, причину ищем потом. Регулярные учения и blameless postmortem с задачами, у которых есть владелец и срок. Меряю время обнаружения и восстановления, долю закрытых задач из постмортемов и повторы похожих инцидентов.","origin":"authored"},{"id":"be-security-owasp/top10-2025","topic":"be-security-owasp","level":"middle","concept":"owasp-top10","q":"Что такое OWASP Top 10 и что в актуальной редакции?","a":"Это рейтинг самых критичных рисков веб-приложений; актуальная редакция — OWASP Top 10:2025. На первом месте A01 Broken Access Control, дальше Security Misconfiguration и новая категория A03 Software Supply Chain Failures; Injection теперь A05, а новая A10 — Mishandling of Exceptional Conditions. Это документ для осознания рисков, а не полный стандарт проверки.","origin":"authored"},{"id":"be-security-owasp/broken-access-control","topic":"be-security-owasp","level":"middle","concept":"owasp-top10","q":"Что такое IDOR и как от него защититься в ASP.NET Core?","a":"Пользователь меняет id в запросе (/orders/42 → /orders/43) и получает чужие данные, потому что сервер проверил только, что он вошёл. Защита — проверять права на каждый объект на сервере: фильтровать запрос по текущему пользователю или использовать resource-based authorization (IAuthorizationService.AuthorizeAsync). Доступ по умолчанию запрещён — deny by default.","origin":"authored"},{"id":"be-security-owasp/sql-injection-ef","topic":"be-security-owasp","level":"middle","concept":"sql-injection-params","q":"Где в EF Core можно получить SQL-инъекцию и как её избежать?","a":"LINQ-запросы EF Core параметризует сам. Риск — в сыром SQL: FromSqlRaw и ExecuteSqlRaw со склейкой строк. FromSql и ExecuteSql с интерполированной строкой безопасны: каждое значение в {} становится DbParameter. Имена таблиц и колонок параметризовать нельзя — их берут только из белого списка.","code":"// Уязвимо: строка склеена до вызова\ndb.Users.FromSqlRaw(\"SELECT * FROM Users WHERE Name = '\" + name + \"'\");\n// Безопасно: {name} станет параметром\ndb.Users.FromSql($\"SELECT * FROM Users WHERE Name = {name}\");","origin":"authored"},{"id":"be-security-owasp/xss-aspnet","topic":"be-security-owasp","level":"middle","concept":"xss-csrf","q":"Как ASP.NET Core защищает от XSS и где защита ломается?","a":"Razor по умолчанию HTML-кодирует всё, что выводится через @. Защита ломается, когда недоверенные данные выводят без кодирования (Html.Raw, HtmlString), вставляют в JavaScript без JavaScriptEncoder или собирают HTML строками. Правило: данные кодируют под тот контекст, куда они попадают — HTML, атрибут, JS, URL.","origin":"authored"},{"id":"be-security-owasp/csrf-antiforgery","topic":"be-security-owasp","level":"middle","concept":"xss-csrf","q":"Когда бэкенду на ASP.NET Core нужна защита от CSRF?","a":"Когда аутентификация на куках: браузер сам прикладывает их к запросу с чужого сайта. Тогда нужны antiforgery-токены — в MVC и Razor Pages они встроены, для Minimal API с формами есть AddAntiforgery/UseAntiforgery — плюс проверка Origin. Если токен идёт в заголовке Authorization (Bearer), браузер не добавляет его сам, и классический CSRF не работает.","origin":"authored"},{"id":"be-security-owasp/secrets","topic":"be-security-owasp","level":"middle","concept":"secrets-dotnet","q":"Где хранить секреты (строки подключения, ключи API) в ASP.NET Core?","a":"Не в коде и не в appsettings.json в git. В разработке — Secret Manager (dotnet user-secrets): он хранит значения вне репозитория, но не шифрует их и подходит только для dev. В проде — хранилище секретов (Azure Key Vault, AWS Secrets Manager) или переменные окружения от оркестратора. Утёкший секрет ротируют, а не просто удаляют из истории git.","origin":"authored"},{"id":"be-security-owasp/least-privilege","topic":"be-security-owasp","level":"middle","concept":"secure-sdlc","q":"Как принцип least privilege выглядит в бэкенд-приложении?","a":"Каждый компонент получает минимум прав для своей задачи: учётка приложения в БД без db_owner и без прав менять схему, отдельная учётка для миграций, токены и роли с узкими scope. Это не предотвращает взлом, но сильно ограничивает ущерб от него. Права регулярно пересматривают, потому что они имеют свойство накапливаться (privilege creep).","origin":"authored"},{"id":"be-security-owasp/secure-sdlc","topic":"be-security-owasp","level":"middle","concept":"secure-sdlc","q":"Что такое Secure SDLC на практике для команды?","a":"Безопасность встроена в каждый этап, а не проверяется аудитом перед релизом: требования и threat modeling на дизайне, стандарты криптографии и безопасные компоненты при разработке, защита цепочки поставок, security-тестирование, мониторинг и реакция на инциденты в эксплуатации. Чем раньше найдена уязвимость, тем дешевле её исправить.","origin":"authored"},{"id":"be-testing-advanced/webapplicationfactory","topic":"be-testing-advanced","level":"middle","concept":"aspnet-integration-tests","q":"Как написать интеграционный тест для ASP.NET Core API?","a":"Через WebApplicationFactory<Program> из пакета Microsoft.AspNetCore.Mvc.Testing: она поднимает приложение на TestServer в памяти, а CreateClient() даёт HttpClient к нему. Запрос проходит весь реальный pipeline — middleware, маршрутизацию, DI, сериализацию — без сетевого порта. В .NET 10 public partial class Program больше не нужно писать вручную: его генерирует source generator.","code":"public class OrdersApiTests(WebApplicationFactory<Program> factory)\n    : IClassFixture<WebApplicationFactory<Program>>\n{\n    [Fact]\n    public async Task Get_order_returns_success()\n    {\n        HttpClient client = factory.CreateClient();\n        HttpResponseMessage response = await client.GetAsync(\"/orders/1\");\n        response.EnsureSuccessStatusCode();\n    }\n}","origin":"authored"},{"id":"be-testing-advanced/configure-test-services","topic":"be-testing-advanced","level":"middle","concept":"aspnet-integration-tests","q":"Как подменить зависимость в тесте с WebApplicationFactory?","a":"Через ConfigureTestServices на host builder — например, factory.WithWebHostBuilder(b => b.ConfigureTestServices(...)) для одного теста. Он выполняется после регистраций приложения, поэтому там можно заменить реальную реализацию фейком: внешний платёжный клиент, отправку почты, TimeProvider. Базу данных при этом лучше не подменять, а поднимать настоящую.","origin":"authored"},{"id":"be-testing-advanced/testcontainers-vs-inmemory","topic":"be-testing-advanced","level":"middle","concept":"testcontainers","q":"Почему в интеграционных тестах с EF Core берут Testcontainers, а не InMemory-провайдер?","a":"InMemory — не реляционная БД: нет SQL, транзакций и ограничений, часть запросов ведёт себя иначе, и тест зелёный, а прод падает. Документация EF Core не советует его как замену БД в тестах. Testcontainers поднимает настоящий SQL Server или PostgreSQL в Docker на время тестов, и проверяются реальные запросы и миграции.","origin":"authored"},{"id":"be-testing-advanced/testcontainers-setup","topic":"be-testing-advanced","level":"middle","concept":"testcontainers","q":"Как организовать тесты с Testcontainers, чтобы они были быстрыми и изолированными?","a":"Контейнер дорогой, поэтому его поднимают один раз на класс или коллекцию тестов — в fixture с IAsyncLifetime — и передают GetConnectionString() в приложение. Изоляцию между тестами дают откат транзакции, отдельная база на тест или очистка таблиц, например библиотекой Respawn. Оставшиеся контейнеры после прогона удаляет Ryuk.","origin":"authored"},{"id":"be-testing-advanced/e2e-tests","topic":"be-testing-advanced","level":"middle","concept":"test-pyramid","q":"Чем e2e-тесты отличаются от интеграционных и сколько их нужно?","a":"E2E проверяют систему целиком, как пользователь: развёрнутый фронт, API, БД — например, через Playwright for .NET. Они самые медленные, дорогие и чаще всего нестабильные, поэтому их немного и только на ключевые сценарии: вход, оплата, оформление заказа. Основную логику покрывают unit- и интеграционные тесты.","origin":"authored"},{"id":"be-testing-advanced/load-test-types","topic":"be-testing-advanced","level":"middle","concept":"load-testing","q":"Какие виды нагрузочных тестов бывают?","a":"Smoke — минимальная нагрузка, проверить, что скрипт и система вообще работают. Average-load — обычная ожидаемая нагрузка. Stress — нагрузка выше обычной, spike — резкий короткий всплеск, soak — долгий прогон, который ловит утечки и деградацию, breakpoint — нагрузка растёт до отказа, чтобы найти предел.","origin":"authored"},{"id":"be-testing-advanced/k6-thresholds","topic":"be-testing-advanced","level":"middle","concept":"load-testing","q":"Как сделать нагрузочный тест в k6 проверкой, которая может уронить pipeline?","a":"Задать thresholds — критерии успеха по метрикам, например p(95) времени ответа меньше 500 мс и доля ошибок меньше 1%. Если порог нарушен, k6 завершается с ненулевым кодом выхода, и CI-шаг падает. Смотрят на перцентили, а не на среднее: среднее прячет медленный хвост.","code":"export const options = {\n  vus: 50,\n  duration: '5m',\n  thresholds: {\n    http_req_duration: ['p(95)<500'],\n    http_req_failed: ['rate<0.01'],\n  },\n};","origin":"authored"},{"id":"be-testing-advanced/mutation-testing","topic":"be-testing-advanced","level":"middle","concept":"mutation-testing","q":"Что такое мутационное тестирование и что показывает Stryker.NET?","a":"Stryker вносит в код маленькие изменения — мутанты: меняет > на >=, && на ||, удаляет вызов. Если тесты упали, мутант убит (Killed); если прошли — выжил (Survived), значит тесты эту логику не проверяют. Mutation score честнее процента покрытия, но прогон дорогой, поэтому его часто запускают только на изменённом коде (опция since).","origin":"authored"},{"id":"be-testing-basics/fact-vs-theory","topic":"be-testing-basics","level":"junior","concept":"xunit-basics","q":"Чем [Fact] отличается от [Theory] в xUnit? Какие аналоги в NUnit?","a":"[Fact] — тест без параметров, который всегда должен проходить. [Theory] — тест с параметрами: данные приходят из [InlineData], [MemberData] или [ClassData], и каждый набор запускается как отдельный случай. В NUnit это [Test] и [TestCase(...)].","code":"[Theory]\n[InlineData(-1)]\n[InlineData(0)]\n[InlineData(1)]\npublic void IsPrime_ValuesLessThan2_ReturnFalse(int value)\n{\n    var service = new PrimeService();\n    Assert.False(service.IsPrime(value));\n}","origin":"authored"},{"id":"be-testing-basics/arrange-act-assert","topic":"be-testing-basics","level":"junior","concept":"unit-tests","q":"Что такое Arrange-Act-Assert и как назвать тест?","a":"Тест делится на три части: Arrange готовит объекты, Act выполняет одно действие, Assert проверяет результат. Одно действие на тест: если тест упал, сразу ясно почему. Имя говорит, что проверяем, при каком условии и что ожидаем: Add_EmptyString_ReturnsZero.","origin":"authored"},{"id":"be-testing-basics/what-to-unit-test","topic":"be-testing-basics","level":"junior","concept":"unit-tests","q":"Что стоит покрывать юнит-тестами, а что нет?","a":"Тестируй публичное поведение и бизнес-логику: ветки, граничные значения, ошибки. Приватные методы проверяются через публичный метод, который их вызывает. База, файлы и сеть делают тест медленным и хрупким — их заменяют дублёрами в юнит-тестах и проверяют отдельно интеграционными тестами.","origin":"authored"},{"id":"be-testing-basics/stub-vs-mock","topic":"be-testing-basics","level":"junior","concept":"test-doubles","q":"Чем stub отличается от mock?","a":"Stub отдаёт заготовленные ответы, а тест проверяет состояние результата. Mock заранее знает, какие вызовы должен получить, и тест проверяет поведение — был ли нужный вызов. Moq и NSubstitute умеют и то и другое; называют их по роли в конкретном тесте.","origin":"authored"},{"id":"be-testing-basics/moq-nsubstitute","topic":"be-testing-basics","level":"junior","concept":"dotnet-mocking","q":"Как подменить зависимость через Moq и NSubstitute?","a":"В Moq создаёшь new Mock<IFoo>(), настраиваешь Setup(...).Returns(...), передаёшь в класс mock.Object и в конце проверяешь Verify. В NSubstitute то же короче: Substitute.For<IFoo>(), foo.Method().Returns(...), проверка — foo.Received().Method(). Подменяют интерфейсы, поэтому код получает зависимости через DI.","code":"// Moq\nvar repo = new Mock<IOrderRepository>();\nrepo.Setup(r => r.Get(It.IsAny<int>())).Returns(new Order());\nvar service = new OrderService(repo.Object);\n\n// NSubstitute\nvar repo2 = Substitute.For<IOrderRepository>();\nrepo2.Get(Arg.Any<int>()).Returns(new Order());","origin":"authored"},{"id":"be-testing-basics/time-provider","topic":"be-testing-basics","level":"junior","concept":"dotnet-mocking","q":"Как протестировать код, который использует DateTime.Now?","a":"Прямой вызов DateTime.Now нельзя подменить, и тест зависит от дня запуска. Время передают как зависимость: в .NET 8+ есть абстракция TimeProvider (в проде TimeProvider.System). В тестах используют FakeTimeProvider из пакета Microsoft.Extensions.TimeProvider.Testing и задают нужное время.","origin":"authored"},{"id":"be-testing-basics/xunit-lifecycle","topic":"be-testing-basics","level":"middle","concept":"xunit-basics","q":"Как в xUnit сделать setup и teardown и поделиться дорогим ресурсом между тестами?","a":"xUnit создаёт новый экземпляр тестового класса для каждого теста: setup — в конструкторе, teardown — в Dispose (или IAsyncLifetime для async). Дорогой ресурс на весь класс даёт IClassFixture<T>, на несколько классов — ICollectionFixture<T>. Атрибутов [SetUp] и [TearDown], как в NUnit, в xUnit нет.","origin":"authored"},{"id":"be-testing-basics/webapplicationfactory","topic":"be-testing-basics","level":"middle","concept":"aspnet-integration-tests","q":"Как написать интеграционный тест для ASP.NET Core API?","a":"Пакет Microsoft.AspNetCore.Mvc.Testing даёт WebApplicationFactory<Program>: он поднимает приложение в памяти на TestServer. factory.CreateClient() возвращает HttpClient, через него тест вызывает эндпоинты по-настоящему, через весь pipeline. Сервисы подменяют в ConfigureWebHost или через WithWebHostBuilder и ConfigureTestServices.","origin":"authored"},{"id":"be-testing-basics/dont-mock-dbcontext","topic":"be-testing-basics","level":"middle","concept":"aspnet-integration-tests","q":"Почему для тестов с EF Core не советуют InMemory-провайдер и мок DbSet?","a":"InMemory-провайдер ведёт себя не как реальная база: нет транзакций и сырого SQL, иначе работает регистр строк. Мок DbSet сложен и имеет те же минусы. Команда EF советует тестировать на реальной СУБД (например, в контейнере); SQLite in-memory ближе к ней, но тоже отличается.","origin":"authored"},{"id":"cs-async/async-await-basics","topic":"cs-async","level":"junior","concept":"cs-async-await","q":"Зачем async/await в бэкенде и что происходит на await?","a":"async/await нужен для I/O: запросы к БД, сети, файлам. На await незавершённой Task метод отдаёт поток обратно, а продолжение выполнится, когда операция закончится. Поток не простаивает, поэтому сервер обслуживает больше запросов теми же потоками.","origin":"authored"},{"id":"cs-async/when-all","topic":"cs-async","level":"junior","concept":"cs-async-await","q":"Как выполнить несколько независимых асинхронных операций параллельно?","a":"Запустить все Task сразу и ждать их вместе через await Task.WhenAll(...): общее время ≈ самая долгая операция, а не сумма. Task.WhenAny ждёт первую завершённую. Последовательные await подряд выполняют операции одну за другой.","code":"var userTask = users.GetAsync(id, ct);\nvar ordersTask = orders.ListAsync(id, ct);\nawait Task.WhenAll(userTask, ordersTask);","origin":"authored"},{"id":"cs-async/async-void","topic":"cs-async","level":"junior","concept":"cs-async-pitfalls","q":"Почему async void опасен и где он допустим?","a":"Исключение из async void нельзя поймать try/catch у вызывающего кода, его нельзя дождаться через await, и его сложно тестировать. Возвращай Task или Task<T>. async void допустим только для обработчиков событий.","origin":"authored"},{"id":"cs-async/sync-over-async","topic":"cs-async","level":"junior","concept":"cs-async-pitfalls","q":"Что плохого в .Result и .Wait() в ASP.NET Core?","a":"Они блокируют поток, пока Task не закончится. Под нагрузкой это ведёт к thread pool starvation: потоков не хватает, время ответа растёт. В классическом ASP.NET и UI с SynchronizationContext это ещё и deadlock. Правило: async all the way — await до самого верха.","code":"// плохо\nvar user = repo.GetAsync(id).Result;\n// хорошо\nvar user = await repo.GetAsync(id);","origin":"authored"},{"id":"cs-async/configureawait","topic":"cs-async","level":"junior","concept":"cs-async-pitfalls","q":"Нужен ли ConfigureAwait(false) в коде ASP.NET Core?","a":"В ASP.NET Core по умолчанию нет SynchronizationContext, поэтому в коде приложения ConfigureAwait(false) обычно не нужен. В универсальных библиотеках его по-прежнему пишут: они могут работать там, где контекст есть (UI, классический ASP.NET).","origin":"authored"},{"id":"cs-async/task-run-in-aspnet","topic":"cs-async","level":"junior","concept":"cs-async-pitfalls","q":"Стоит ли оборачивать код в Task.Run в обработчике запроса?","a":"Нет. ASP.NET Core и так выполняет запросы на потоках пула; Task.Run и сразу await только добавляют переключение и лишнюю работу планировщика. Для I/O используют настоящие async API, а долгую фоновую работу выносят в BackgroundService или очередь.","origin":"authored"},{"id":"cs-async/cancellation-token","topic":"cs-async","level":"junior","concept":"cs-cancellation","q":"Как отменить работу, если клиент закрыл HTTP-запрос?","a":"Добавить параметр CancellationToken в endpoint или action — ASP.NET Core привяжет его к отмене запроса. Передавать токен дальше во все async-вызовы (EF Core, HttpClient). Отмена кооперативная: код сам проверяет токен или передаёт его методам, которые это делают.","code":"app.MapGet(\"/report\", async (IReportService svc, CancellationToken ct) =>\n    await svc.BuildAsync(ct));","origin":"authored"},{"id":"cs-async/cancellation-source","topic":"cs-async","level":"middle","concept":"cs-cancellation","q":"Как отменить операцию по таймауту, и как правильно завершить задачу при отмене?","a":"CancellationTokenSource создаёт токен; CancelAfter(TimeSpan) отменяет по таймауту, CreateLinkedTokenSource объединяет токен запроса и таймаут. Внутри работы вызывают ct.ThrowIfCancellationRequested(): OperationCanceledException переводит Task в состояние Canceled.","code":"using var cts = CancellationTokenSource.CreateLinkedTokenSource(requestToken);\ncts.CancelAfter(TimeSpan.FromSeconds(5));\nawait client.GetAsync(url, cts.Token);","origin":"authored"},{"id":"cs-basics/value-vs-reference","topic":"cs-basics","level":"intern","concept":"cs-value-reference-types","q":"Чем value type отличается от reference type в C#?","a":"Переменная value type (int, struct, enum) хранит само значение: при присваивании копируются данные. Переменная reference type (class, string, массив, record) хранит ссылку на объект: при присваивании копируется ссылка, и обе переменные видят один объект.","code":"struct P { public int X; }\nvar a = new P { X = 1 };\nvar b = a;   // копия данных\nb.X = 2;     // a.X не меняется","origin":"authored"},{"id":"cs-basics/boxing","topic":"cs-basics","level":"intern","concept":"cs-value-reference-types","q":"Что такое boxing и unboxing и почему их избегают?","a":"Boxing — упаковка value type в object (или интерфейс): CLR создаёт объект в управляемой куче и копирует туда значение. Unboxing — явное обратное приведение. Это лишние аллокации и работа для GC, поэтому в горячем коде берут generic-коллекции (List<int>), а не ArrayList.","code":"int n = 42;\nobject o = n;      // boxing\nint m = (int)o;    // unboxing","origin":"authored"},{"id":"cs-basics/string-immutable","topic":"cs-basics","level":"intern","concept":"cs-strings","q":"Что значит «строки в C# неизменяемы»?","a":"Объект string нельзя изменить после создания. Методы вроде Replace, ToUpper и оператор + возвращают новую строку, старая остаётся прежней. Поэтому результат надо присвоить: s = s.Trim();","origin":"authored"},{"id":"cs-basics/stringbuilder","topic":"cs-basics","level":"intern","concept":"cs-strings","q":"Когда нужен StringBuilder вместо конкатенации строк?","a":"Когда строку много раз меняют, например в цикле. Каждая операция со string создаёт новый объект и новую аллокацию. StringBuilder меняет свой внутренний буфер, а итоговую строку отдаёт один раз через ToString().","code":"var sb = new StringBuilder();\nforeach (var item in items) sb.Append(item).Append(';');\nvar result = sb.ToString();","origin":"authored"},{"id":"cs-basics/string-comparison","topic":"cs-basics","level":"junior","concept":"cs-strings","q":"Как правильно сравнивать строки, например ключи, пути или заголовки?","a":"Явно указывать StringComparison. Для технических значений — Ordinal или OrdinalIgnoreCase: быстро и не зависит от культуры. Сравнение с учётом культуры (CurrentCulture) — только для текста, который видит пользователь.","code":"string.Equals(a, b, StringComparison.OrdinalIgnoreCase);","origin":"authored"},{"id":"cs-basics/nullable-reference-types","topic":"cs-basics","level":"intern","concept":"cs-nullable","q":"Что дают nullable reference types (string vs string?)?","a":"Это статический анализ компилятора, а не новый тип в runtime. При включённом <Nullable>enable</Nullable> string значит «не null», string? — «может быть null». Компилятор выдаёт предупреждения, если ты разыменовываешь возможный null или присваиваешь null в не-nullable.","origin":"authored"},{"id":"cs-basics/nullable-value-types","topic":"cs-basics","level":"intern","concept":"cs-nullable","q":"Чем int? отличается от string? ?","a":"int? — это настоящий тип Nullable<int> со свойствами HasValue и Value. string? — только аннотация для анализа: в runtime это тот же string. Для value types null возможен только через Nullable<T>.","origin":"authored"},{"id":"cs-basics/null-operators","topic":"cs-basics","level":"junior","concept":"cs-nullable","q":"Какие операторы помогают работать с null, и что нового в C# 14?","a":"?. и ?[] — вызвать, только если не null. ?? — значение по умолчанию. ??= — присвоить, если null. В C# 14 ?. и ?[] можно ставить слева от присваивания: правая часть вычисляется, только если объект не null.","code":"customer?.Order = GetCurrentOrder(); // C# 14","origin":"authored"},{"id":"cs-basics/ref-out-in","topic":"cs-basics","level":"intern","concept":"cs-method-params","q":"Чем отличаются параметры ref, out и in?","a":"По умолчанию аргумент передаётся по значению (для класса копируется ссылка). ref — передать переменную по ссылке, она должна быть инициализирована. out — метод обязан присвоить значение (TryParse). in — по ссылке только для чтения, полезно для больших struct.","code":"if (int.TryParse(text, out var n)) Use(n);","origin":"authored"},{"id":"cs-basics/decimal-vs-double","topic":"cs-basics","level":"intern","concept":"cs-numbers","q":"Когда использовать decimal, а когда double?","a":"double — двоичное число с плавающей точкой: 8 байт, около 15–17 значащих цифр, быстрый, но 0.1 в нём точно не хранится. decimal — десятичный тип: 16 байт, 28–29 цифр, медленнее, зато точно хранит десятичные дроби. Деньги и бухгалтерию считают в decimal, физику и графику — в double. Смешать decimal и double в одном выражении без явного приведения нельзя — будет ошибка компиляции.","origin":"authored"},{"id":"cs-basics/var-not-dynamic","topic":"cs-basics","level":"intern","concept":"cs-declarations","q":"Что делает var? Становится ли переменная динамической?","a":"Нет. var просит компилятор вывести тип из выражения справа, и дальше переменная строго типизирована, как будто тип написали явно. Поэтому var требует инициализацию в той же строке и работает только для локальных переменных, не для полей. Проверка типов во время выполнения — это dynamic, совсем другой механизм.","origin":"authored"},{"id":"cs-basics/const-vs-readonly","topic":"cs-basics","level":"intern","concept":"cs-declarations","q":"Чем const отличается от readonly?","a":"const — константа времени компиляции: значение задаётся только в объявлении, подходят встроенные типы, string и null, и она неявно static. readonly-поле можно присвоить в объявлении или в конструкторе, поэтому значение может вычисляться при запуске и быть разным у разных объектов. Подвох const: значение копируется в сборки, которые её используют, и после изменения константы их нужно пересобрать.","origin":"authored"},{"id":"cs-basics/array-vs-list","topic":"cs-basics","level":"intern","concept":"dotnet-collections","q":"Чем массив отличается от List<T>?","a":"У массива T[] длина фиксирована: её задают при создании и потом не меняют. List<T> хранит элементы во внутреннем массиве и сам увеличивает его, когда места не хватает, поэтому у него есть Add, Insert и Remove. Размер известен и не меняется — массив, элементы добавляются и удаляются — List<T>. Оба ссылочные типы, доступ по индексу у обоих O(1).","origin":"authored"},{"id":"cs-basics/dictionary-trygetvalue","topic":"cs-basics","level":"intern","concept":"dotnet-collections","q":"Как получить значение из Dictionary, если ключа может не быть?","a":"Индексатор dict[key] для отсутствующего ключа бросает KeyNotFoundException. Используй TryGetValue: он возвращает bool и отдаёт значение через out-параметр. Это лучше, чем ContainsKey плюс индексатор, потому что поиск по ключу идёт один раз, а не два.","code":"if (prices.TryGetValue(\"apple\", out var price))\n    Console.WriteLine(price);","origin":"authored"},{"id":"cs-basics/foreach-modify","topic":"cs-basics","level":"intern","concept":"cs-loops","q":"Чем for отличается от foreach и почему нельзя удалять элементы из List внутри foreach?","a":"for идёт по индексу и нужен, когда важен индекс или особый шаг. foreach перебирает любую коллекцию с GetEnumerator и читается проще. Если изменить List во время foreach (Add или Remove), перечислитель становится недействительным, и следующий шаг бросает InvalidOperationException. Решение — RemoveAll с условием или цикл for с конца списка.","code":"orders.RemoveAll(order => order.IsExpired);","origin":"authored"},{"id":"cs-basics/cast-is-as","topic":"cs-basics","level":"intern","concept":"cs-casting","q":"Чем отличаются приведение (T)x, оператор as и оператор is?","a":"Приведение (T)x при неудаче бросает InvalidCastException. as не бросает исключение, а возвращает null, поэтому работает только со ссылочными и nullable-типами. is проверяет тип, а форма `is T t` сразу и проверяет, и объявляет переменную — сейчас это самый удобный вариант.","code":"if (shape is Circle circle)\n    Console.WriteLine(circle.Radius);","origin":"authored"},{"id":"cs-linq-generics/deferred-execution","topic":"cs-linq-generics","level":"middle","concept":"linq-deferred-execution","q":"Что такое отложенное выполнение в LINQ?","a":"Where, Select и похожие операторы не выполняются в момент объявления: запрос хранит «рецепт» и выполняется только при переборе — foreach, ToList, First, Count. Поэтому изменения источника до перебора попадут в результат. Сразу выполняются операторы, которые возвращают одно значение или материализуют коллекцию: ToList, ToArray, Count, Sum.","origin":"authored"},{"id":"cs-linq-generics/multiple-enumeration","topic":"cs-linq-generics","level":"middle","concept":"linq-deferred-execution","q":"Что не так с этим кодом и как его исправить?","a":"Переменная big — это запрос, а не данные. Any() и Count() перебирают его дважды: дважды считается Where, а если источник — БД или файл, то дважды идёт чтение, и результаты могут различаться. Если данные нужны несколько раз, материализуй их один раз через ToList() или ToArray().","code":"IEnumerable<Order> big = orders.Where(o => o.Total > 1000);\nif (big.Any())              // первый перебор\n    Notify(big.Count());    // второй перебор\n// Исправление: var big = orders.Where(o => o.Total > 1000).ToList();","origin":"authored"},{"id":"cs-linq-generics/ienumerable-vs-iqueryable","topic":"cs-linq-generics","level":"middle","concept":"linq-iqueryable","q":"Чем IQueryable<T> отличается от IEnumerable<T>?","a":"IEnumerable<T> выполняет операторы в памяти через делегаты (LINQ to Objects). IQueryable<T> собирает дерево выражений, а провайдер — например EF Core — переводит его в SQL и выполняет в БД. Если рано перейти к IEnumerable (AsEnumerable, ToList), следующие Where и OrderBy выполнятся уже в памяти, после загрузки всех строк.","origin":"authored"},{"id":"cs-linq-generics/client-evaluation","topic":"cs-linq-generics","level":"middle","concept":"linq-iqueryable","q":"EF Core бросил исключение: выражение нельзя перевести в SQL. Почему и что делать?","a":"В Where или OrderBy стоит то, что EF Core не умеет перевести в SQL, например вызов своего C#-метода. С EF Core 3.0 клиентская оценка разрешена только в финальном Select, иначе EF бросает исключение, а не тянет тихо всю таблицу. Перепиши условие переводимыми выражениями или явно перейди в память (AsEnumerable) уже после фильтрации в БД.","origin":"authored"},{"id":"cs-linq-generics/collections-complexity","topic":"cs-linq-generics","level":"middle","concept":"dotnet-collections","q":"Как выбрать между List<T>, Dictionary<TKey,TValue> и HashSet<T>?","a":"List<T> — упорядоченный список: доступ по индексу и Add за O(1), но Contains и поиск — O(n). Dictionary ищет по ключу в среднем за O(1), HashSet — то же для набора уникальных значений. Если в цикле вызывается list.Contains, это кандидат на HashSet.","origin":"authored"},{"id":"cs-linq-generics/generic-constraints","topic":"cs-linq-generics","level":"middle","concept":"cs-generic-constraints","q":"Зачем нужны ограничения where у дженериков?","a":"Без ограничений компилятор знает про T только то, что это object. where T : IComparable<T> разрешает вызвать CompareTo, where T : new() — создать экземпляр, class/struct задают вид типа, notnull запрещает nullable. Ограничение — это контракт: неподходящий аргумент типа не скомпилируется.","code":"public static T Max<T>(IEnumerable<T> items) where T : IComparable<T>\n{\n    T best = items.First();\n    foreach (T item in items)\n        if (item.CompareTo(best) > 0) best = item;\n    return best;\n}","origin":"authored"},{"id":"cs-linq-generics/generics-vs-object","topic":"cs-linq-generics","level":"middle","concept":"cs-generic-constraints","q":"Что дают дженерики по сравнению с коллекциями object вроде ArrayList?","a":"Типобезопасность: ошибка типа видна при компиляции, а не при приведении в рантайме. Производительность: List<int> хранит int без boxing, а ArrayList упаковывает каждое значение. И код пишется один раз для любого T.","origin":"authored"},{"id":"cs-linq-generics/variance","topic":"cs-linq-generics","level":"middle","concept":"cs-variance","q":"Почему IEnumerable<string> можно присвоить в IEnumerable<object>, а List<string> в List<object> — нельзя?","a":"IEnumerable<out T> ковариантен: T только отдаётся наружу, поэтому подстановка безопасна. List<T> инвариантен: в List<object> можно было бы добавить int и сломать список строк. Контравариантность (in T) работает в обратную сторону: Action<object> подходит как Action<string>. Вариантность есть только у интерфейсов и делегатов и только для ссылочных типов: IEnumerable<int> в IEnumerable<object> не превратится.","origin":"authored"},{"id":"cs-linq-generics/extension-members","topic":"cs-linq-generics","level":"middle","concept":"linq-extension-members","q":"Как операторы LINQ «появляются» у IEnumerable<T> и что тут изменил C# 14?","a":"Операторы LINQ — это методы расширения в статических классах Enumerable и Queryable, поэтому они видны у любого IEnumerable<T> или IQueryable<T> после using System.Linq. C# 14 добавил блоки extension: кроме методов теперь можно объявлять свойства-расширения и статические члены расширяемого типа. Старый синтаксис с this в параметре продолжает работать.","code":"public static class SequenceExtensions\n{\n    extension<T>(IEnumerable<T> source)\n    {\n        public bool IsEmpty => !source.Any();\n    }\n}","origin":"authored"},{"id":"cs-oop/four-principles","topic":"cs-oop","level":"intern","concept":"cs-oop-principles","q":"Назови принципы ООП и коротко объясни каждый.","a":"Абстракция — показываем важное, прячем детали. Инкапсуляция — данные закрыты, менять их можно только через методы и свойства. Наследование — новый класс берёт поведение базового. Полиморфизм — один вызов работает по-разному для разных типов.","origin":"authored"},{"id":"cs-oop/polymorphism","topic":"cs-oop","level":"intern","concept":"cs-oop-principles","q":"Как работает полиморфизм через virtual и override?","a":"Базовый класс помечает метод virtual, наследник переопределяет его через override. При вызове через ссылку базового типа CLR смотрит на реальный тип объекта и вызывает его версию метода.","code":"class Shape { public virtual double Area() => 0; }\nclass Circle(double r) : Shape { public override double Area() => Math.PI * r * r; }\nShape s = new Circle(2);\ns.Area(); // вызовется Circle.Area","origin":"authored"},{"id":"cs-oop/interface-vs-abstract","topic":"cs-oop","level":"intern","concept":"cs-abstraction","q":"Чем интерфейс отличается от абстрактного класса?","a":"Абстрактный класс — общий базовый класс: может хранить состояние (поля), конструктор и общую реализацию; наследовать можно только один класс. Интерфейс — контракт без состояния экземпляра; класс может реализовать много интерфейсов. Интерфейс — «что умеет», абстрактный класс — «кем является».","origin":"authored"},{"id":"cs-oop/access-modifiers","topic":"cs-oop","level":"intern","concept":"cs-abstraction","q":"Какие модификаторы доступа есть в C#?","a":"public — всем. private — только внутри типа. protected — тип и наследники. internal — только своя сборка. protected internal — своя сборка или наследники. private protected — наследники внутри своей сборки.","origin":"authored"},{"id":"cs-oop/record-vs-class","topic":"cs-oop","level":"intern","concept":"cs-records","q":"Чем record отличается от class?","a":"record — тип для данных: компилятор генерирует равенство по значениям (Equals, ==), удобный ToString и with для копирования. У class равенство по ссылке. record class — ссылочный тип, record struct — значимый.","code":"public record User(string Name, int Age);\nnew User(\"Ann\", 30) == new User(\"Ann\", 30); // true","origin":"authored"},{"id":"cs-oop/with-expression","topic":"cs-oop","level":"junior","concept":"cs-records","q":"Что делает выражение with и меняет ли оно исходный объект?","a":"with создаёт копию с изменёнными свойствами (nondestructive mutation). Исходный объект не меняется. Копия поверхностная: вложенные ссылочные объекты общие для оригинала и копии.","code":"var older = user with { Age = 31 };","origin":"authored"},{"id":"cs-oop/field-keyword","topic":"cs-oop","level":"junior","concept":"cs-csharp14","q":"Что делает ключевое слово field в C# 14?","a":"field внутри аксессора свойства — это поле, которое создаёт компилятор. Можно написать логику только в set (например, проверку), а get оставить автоматическим. Явное приватное поле больше не нужно.","code":"public string Message\n{\n    get;\n    set => field = value ?? throw new ArgumentNullException(nameof(value));\n}","origin":"authored"},{"id":"cs-oop/extension-members","topic":"cs-oop","level":"junior","concept":"cs-csharp14","q":"Что такое extension members в C# 14 и чем они лучше старых extension methods?","a":"Раньше были только методы с this у первого параметра. С C# 14 в статическом классе пишут блок extension(...), и в нём можно объявить не только методы, но и свойства, а также статические члены расширяемого типа. Старый синтаксис тоже работает.","code":"public static class SeqExt\n{\n    extension<T>(IEnumerable<T> source)\n    {\n        public bool IsEmpty => !source.Any();\n    }\n}","origin":"authored"},{"id":"cs-oop/class-vs-object","topic":"cs-oop","level":"intern","concept":"cs-classes-objects","q":"Чем класс отличается от объекта и зачем нужен конструктор?","a":"Класс — описание типа: какие у него данные и методы. Объект — конкретный экземпляр класса в памяти, его создают через new. Конструктор вызывается при создании и приводит объект в правильное начальное состояние. Если в классе не объявлен ни один конструктор, C# сам добавляет конструктор без параметров.","origin":"authored"},{"id":"cs-oop/property-vs-field","topic":"cs-oop","level":"intern","concept":"cs-properties","q":"Чем свойство отличается от поля?","a":"Поле — переменная внутри объекта. Свойство снаружи выглядит как поле, но на деле это методы доступа get и set, поэтому в них можно добавить проверку, вычисление или сделать set приватным. Наружу данные открывают через свойства, а поля держат private. Auto-property `{ get; set; }` — короткая запись, скрытое поле создаёт компилятор, а `init` разрешает задать значение только при создании объекта.","origin":"authored"},{"id":"cs-oop/static-members","topic":"cs-oop","level":"intern","concept":"cs-classes-objects","q":"Что значит static у члена класса и у самого класса?","a":"static-член принадлежит типу, а не объекту: у static-поля одна копия на всё приложение, обращаются к нему через имя класса. static-метод не видит поля экземпляра, пока ему не передали объект. static class нельзя создать через new и нельзя унаследовать, в нём только статические члены — как в Math. Изменяемое static-поле — общее состояние для всех, поэтому с ним нужно быть осторожным.","origin":"authored"},{"id":"cs-oop/overloading-vs-overriding","topic":"cs-oop","level":"intern","concept":"cs-oop-principles","q":"Чем перегрузка метода отличается от переопределения?","a":"Перегрузка (overloading) — несколько методов с одним именем и разными параметрами в одном типе; какой вызвать, решает компилятор по аргументам. Переопределение (override) — наследник заменяет реализацию virtual- или abstract-метода базового класса с той же сигнатурой; какую реализацию вызвать, решается во время выполнения по реальному типу объекта. Методы, которые отличаются только типом результата, перегрузкой не считаются.","origin":"authored"},{"id":"cs-oop/single-inheritance-sealed","topic":"cs-oop","level":"intern","concept":"cs-inheritance","q":"Сколько базовых классов может быть у класса в C# и что делает sealed?","a":"Базовый класс только один, а интерфейсов можно реализовать сколько угодно. sealed у класса запрещает от него наследоваться, а `sealed override` у метода запрещает переопределять его дальше по иерархии. struct неявно sealed. Запечатывают классы, которые не проектировали для наследования, чтобы наследник не сломал их поведение.","origin":"authored"},{"id":"cs-oop/constructor-chaining","topic":"cs-oop","level":"intern","concept":"cs-inheritance","q":"Как вызвать конструктор базового класса или другой конструктор этого же класса?","a":"После сигнатуры конструктора пишут `: base(...)`, чтобы вызвать конструктор базового класса, или `: this(...)`, чтобы вызвать другой конструктор того же класса. Базовый конструктор выполняется раньше тела конструктора наследника. Цепочка через this убирает дублирование: короткий конструктор передаёт значения по умолчанию в полный.","code":"public class Coords\n{\n    public Coords() : this(0, 0) { }\n    public Coords(int x, int y) { X = x; Y = y; }\n    public int X { get; }\n    public int Y { get; }\n}","origin":"authored"},{"id":"cs-oop/equals-gethashcode","topic":"cs-oop","level":"intern","concept":"cs-equality","q":"Чем == отличается от Equals и зачем переопределять GetHashCode вместе с Equals?","a":"У обычного класса по умолчанию и ==, и Equals сравнивают ссылки: один и тот же это объект или нет. string и record сравнивают по значению, потому что переопределяют это поведение. Если переопределил Equals, переопредели и GetHashCode: равные объекты обязаны давать одинаковый хеш, иначе Dictionary и HashSet не найдут такой ключ.","origin":"authored"},{"id":"css-basics/specificity","topic":"css-basics","level":"intern","concept":"css-cascade","q":"Два правила задают color одному элементу. Как браузер выбирает победителя?","a":"Сначала важность (!important) и происхождение, затем cascade layer, затем специфичность селектора (id > класс/атрибут/псевдокласс > тег), при равенстве — то правило, что ниже в коде. Inline-стиль сильнее любого селектора без !important.","origin":"authored"},{"id":"css-basics/box-sizing","topic":"css-basics","level":"intern","concept":"box-model","q":"Блоку задали width: 200px, padding: 20px и border: 1px. Какая у него итоговая ширина и как это изменить?","a":"При box-sizing: content-box (по умолчанию) — 200 + 2×20 + 2×1 = 242px. С box-sizing: border-box padding и border входят в width, итог — 200px. Поэтому многие проекты ставят border-box всем элементам.","origin":"authored"},{"id":"css-basics/position","topic":"css-basics","level":"intern","concept":"box-model","q":"Чем отличаются position: relative, absolute, fixed и sticky?","a":"relative сдвигает элемент от его обычного места и не выкидывает из потока. absolute выкидывает из потока и считает координаты от ближайшего позиционированного предка. fixed — от окна, при прокрутке стоит на месте. sticky ведёт себя как relative, пока не дойдёт до порога, потом прилипает внутри своего контейнера.","origin":"authored"},{"id":"css-basics/units","topic":"css-basics","level":"intern","concept":"css-units","q":"Чем отличаются px, em, rem, % и vw/vh?","a":"px — абсолютная единица. em в font-size берёт размер шрифта родителя, в остальных свойствах — свой шрифт, поэтому вложенные em накапливаются. rem всегда от шрифта корня (html) и не накапливается — удобно для текста и отступов. % считается от другой величины, для width — от ширины родителя; 1vw и 1vh — 1% ширины и высоты окна.","origin":"authored"},{"id":"css-basics/display-block-inline","topic":"css-basics","level":"intern","concept":"box-model","q":"Чем отличаются display: block, inline и inline-block?","a":"block начинается с новой строки, тянется на всю ширину и слушается width и height (p, h1, div). inline стоит в строке текста: width, height и вертикальные margin на него не действуют (a, span). inline-block стоит в строке, но width, height и все отступы работают — так делают крупную кликабельную ссылку.","origin":"authored"},{"id":"css-basics/margin-collapsing","topic":"css-basics","level":"intern","concept":"box-model","q":"Что такое схлопывание margin и когда его не бывает?","a":"Вертикальные margin соседних блоков не складываются: остаётся больший (20px и 10px дают 20px). Так же схлопывается margin родителя с первым или последним ребёнком, если между ними нет border, padding или текста. Горизонтальные margin не схлопываются никогда, как и margin внутри flex- и grid-контейнера.","origin":"authored"},{"id":"css-basics/pseudo-class-vs-element","topic":"css-basics","level":"intern","concept":"css-selectors","q":"Чем псевдокласс отличается от псевдоэлемента? Приведи примеры.","a":"Псевдокласс (одно двоеточие) выбирает элемент в состоянии: :hover, :focus, :first-child, :invalid. Псевдоэлемент (два двоеточия) ведёт себя как новый кусок разметки: ::before, ::after, ::first-line, ::placeholder. ::before и ::after без свойства content не появятся.","origin":"authored"},{"id":"css-basics/nth-child-vs-nth-of-type","topic":"css-basics","level":"intern","concept":"css-selectors","q":"Чем :nth-child() отличается от :nth-of-type()?","a":":nth-child(n) смотрит на номер среди всех детей родителя, какого бы тега они ни были. :nth-of-type(n) считает только соседей того же тега. Если первым ребёнком стоит div, то p:nth-child(1) ничего не найдёт, а p:nth-of-type(1) выберет первый абзац.","origin":"authored"},{"id":"css-layout/layout-from-mockup","topic":"css-layout","level":"intern","concept":"css-layout-modern","q":"Тебе дали макет страницы. С чего начать вёрстку?","a":"Найти крупные блоки и сетку страницы (обычно Grid), внутри блоков — строки и колонки (Flexbox). Найти повторяющиеся элементы — это будущие компоненты. Выписать отступы, цвета, шрифты в переменные/токены. Проверить, как макет ведёт себя на узком и широком экране.","origin":"authored"},{"id":"css-layout/flex-vs-grid","topic":"css-layout","level":"junior","concept":"css-layout-modern","q":"Когда брать Flexbox, а когда Grid?","a":"Flexbox — раскладка по одной оси, размеры идут от содержимого: панель кнопок, строка карточки. Grid — по двум осям, размеры задаёт сетка: каркас страницы, галерея. Их часто комбинируют: Grid снаружи, Flex внутри.","origin":"authored"},{"id":"css-layout/center","topic":"css-layout","level":"junior","concept":"css-layout-modern","q":"Как отцентровать блок по горизонтали и вертикали?","a":"Родителю: display: grid; place-items: center. Или display: flex; justify-content: center; align-items: center. Для блока известной ширины по горизонтали хватает margin-inline: auto.","origin":"authored"},{"id":"css-layout/mobile-first","topic":"css-layout","level":"junior","concept":"responsive-design","q":"Что такое mobile-first и зачем meta viewport?","a":"Базовые стили пишут для узкого экрана, а для широких добавляют media-запросы с min-width. Так мобильный получает минимум CSS. Без <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> телефон рисует страницу как десктоп шириной ~980px и уменьшает её.","origin":"authored"},{"id":"css-layout/container-queries","topic":"css-layout","level":"junior","concept":"css-layout-modern","q":"Чем container queries лучше media queries для компонента?","a":"Media query смотрит на ширину окна, container query — на ширину контейнера (container-type: inline-size). Одна и та же карточка в узкой колонке и в широком блоке выглядит правильно, не зная о странице.","origin":"authored"},{"id":"css-layout/cascade-layers","topic":"css-layout","level":"junior","concept":"css-cascade","q":"Какую проблему решают cascade layers (@layer)?","a":"Они задают порядок групп стилей: например reset, base, components, utilities. Правило из более позднего слоя побеждает правило из раннего, даже с меньшей специфичностью. Это заменяет гонку специфичности и !important.","origin":"authored"},{"id":"css-layout/scss-vs-vars","topic":"css-layout","level":"junior","concept":"scss-vs-custom-properties","q":"Чем переменная SCSS ($color) отличается от CSS-переменной (--color)?","a":"$color подставляется при сборке и исчезает из итогового CSS. --color живёт в браузере: наследуется, меняется через JS или media query в рантайме. Темы и токены делают на CSS-переменных, а SCSS оставляют для миксинов и циклов при сборке.","origin":"authored"},{"id":"css-layout/mixins","topic":"css-layout","level":"junior","concept":"scss-vs-custom-properties","q":"Что такое миксин в SCSS и когда он лучше, чем @extend?","a":"Миксин (@mixin / @include) — блок стилей с параметрами, его код копируется в каждое место вызова. @extend объединяет селекторы и может неожиданно раздуть и запутать CSS. Для повторяемых наборов с параметрами берут миксин.","origin":"authored"},{"id":"css-layout/flex-axes","topic":"css-layout","level":"junior","concept":"css-layout-modern","q":"Что такое главная и поперечная ось во Flexbox и какие свойства по ним выравнивают?","a":"Главную ось задаёт flex-direction: row — горизонталь, column — вертикаль. justify-content раскладывает элементы вдоль главной оси, align-items — вдоль поперечной (по умолчанию stretch). Эти свойства ставят на контейнер, а не на детей. flex-wrap: wrap разрешает перенос на новые строки.","origin":"authored"},{"id":"css-layout/flex-shorthand","topic":"css-layout","level":"junior","concept":"css-layout-modern","q":"Что значит flex: 1 у элемента и чем он отличается от flex: auto и flex: none?","a":"flex: 1 — это 1 1 0%: элемент растёт и сжимается, а стартовый размер 0, поэтому место делится поровну. flex: auto — 1 1 auto: делится только свободное место, после размера контента. flex: none — 0 0 auto: элемент не растёт и не сжимается. По умолчанию стоит 0 1 auto.","origin":"authored"},{"id":"css-layout/grid-fr-minmax","topic":"css-layout","level":"junior","concept":"css-layout-modern","q":"Что такое единица fr и функции repeat() и minmax() в Grid?","a":"fr — доля свободного места в grid-контейнере: 2fr 1fr делит его как 2:1, фиксированные колонки и gap вычитаются раньше. repeat(3, 1fr) — короткая запись трёх одинаковых колонок. minmax(100px, auto) задаёт трек, который не меньше 100px, но растёт по контенту.","code":".cards {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  grid-auto-rows: minmax(100px, auto);\n  gap: 1rem;\n}","origin":"authored"},{"id":"db-design-perf/one-to-many","topic":"db-design-perf","level":"middle","concept":"db-relationships","q":"Как устроена связь «один-ко-многим» в БД и в EF Core?","a":"Внешний ключ лежит в таблице на стороне «многих»: у Order есть CustomerId, который ссылается на Customer. В EF Core это коллекция Customer.Orders и ссылка Order.Customer; связь находится по соглашениям, если есть свойство CustomerId. Обязательная связь или нет — решает nullable ли FK.","origin":"authored"},{"id":"db-design-perf/one-to-one","topic":"db-design-perf","level":"middle","concept":"db-relationships","q":"Где ставить внешний ключ в связи «один-к-одному» и зачем её вообще выделять?","a":"FK ставят в зависимой (dependent) таблице, и он должен быть уникальным — часто FK и есть её первичный ключ. В EF Core важно явно указать, кто principal, а кто dependent: HasOne(...).WithOne(...).HasForeignKey<TDependent>(...). Связь 1:1 выделяют, чтобы отделить редко нужные или тяжёлые данные либо данные с другими правами доступа.","origin":"authored"},{"id":"db-design-perf/many-to-many","topic":"db-design-perf","level":"middle","concept":"db-relationships","q":"Как EF Core моделирует связь «многие-ко-многим»?","a":"В БД это всегда таблица связей (join table) с двумя FK, каждая строка — одна пара. В EF Core достаточно двух коллекций навигаций, например Post.Tags и Tag.Posts: join entity и таблицу EF создаст сам. Если у связи есть свои данные (дата, роль), делают явную сущность связи.","origin":"authored"},{"id":"db-design-perf/isolation-levels","topic":"db-design-perf","level":"middle","concept":"db-isolation-levels","q":"Какие уровни изоляции есть в SQL Server и от каких аномалий они защищают?","a":"READ UNCOMMITTED допускает грязное чтение. READ COMMITTED (по умолчанию) его запрещает, но повторное чтение может вернуть другие данные. REPEATABLE READ держит блокировки на прочитанных строках до конца транзакции, но допускает фантомы — новые строки. SERIALIZABLE закрывает и фантомы, а SNAPSHOT читает версию данных на начало транзакции без блокировок чтения.","origin":"authored"},{"id":"db-design-perf/ef-transactions","topic":"db-design-perf","level":"middle","concept":"db-isolation-levels","q":"Нужна ли явная транзакция вокруг SaveChanges в EF Core?","a":"Нет: если провайдер поддерживает транзакции, все изменения одного вызова SaveChanges применяются в одной транзакции — либо все, либо ни одно. Явная транзакция (Database.BeginTransaction) нужна, когда несколько SaveChanges или EF вместе с сырым SQL должны быть атомарными.","origin":"authored"},{"id":"db-design-perf/composite-covering-index","topic":"db-design-perf","level":"middle","concept":"db-indexes","q":"Как выбрать порядок колонок в составном индексе и что такое покрывающий индекс?","a":"Составной индекс отсортирован сначала по первой колонке, потом по второй, поэтому он помогает, только когда запрос фильтрует по его левой части. Правило: сначала колонки с равенством, потом колонка диапазона. Покрывающий индекс содержит все колонки, нужные запросу (часть — через INCLUDE), и SQL Server не ходит за остальными в таблицу — нет Key Lookup.","code":"-- WHERE CustomerId = @id AND CreatedAt >= @from; SELECT Total\nCREATE INDEX IX_Orders_Customer_Created\n    ON Orders (CustomerId, CreatedAt)\n    INCLUDE (Total);","origin":"authored"},{"id":"db-design-perf/execution-plan","topic":"db-design-perf","level":"middle","concept":"db-query-tuning","q":"Что ищешь в плане выполнения медленного запроса?","a":"Беру actual-план и смотрю: Scan вместо Seek на большой таблице, Key Lookup на много строк, большой разрыв между оценкой и фактическим числом строк (часто устаревшая статистика), дорогие Sort и Hash Match, предупреждения, например о неявном приведении типов. Потом проверяю, SARGable ли условие: функция над колонкой, как YEAR(CreatedAt) = 2025, не даёт использовать индекс.","origin":"authored"},{"id":"db-design-perf/n-plus-one","topic":"db-design-perf","level":"middle","concept":"efcore-loading","q":"Что такое проблема N+1 в EF Core и как её исправить?","a":"Один запрос загружает список, а потом на каждый элемент уходит ещё запрос за связанными данными — обычно из-за lazy loading в цикле. Это видно в логе SQL как много одинаковых запросов. Лечится eager loading (Include), проекцией через Select в DTO или split query; для чтения ещё помогает AsNoTracking.","origin":"authored"},{"id":"db-design-perf/migrations-prod","topic":"db-design-perf","level":"middle","concept":"efcore-migrations","q":"Как применять миграции EF Core в продакшене?","a":"Для ревью и контроля DBA — SQL-скрипт (dotnet ef migrations script --idempotent), для автоматического деплоя — migration bundle шагом в CI/CD. Migrate() на старте приложения — компромисс: с EF Core 9 есть блокировка миграций, но приложению нужны права менять схему. Лучше отдельная учётка для деплоя схемы, а приложению — только права на чтение и запись данных; сгенерированную миграцию всегда проверяют глазами.","origin":"authored"},{"id":"design-system/atomic-levels","topic":"design-system","level":"middle","concept":"atomic-design","q":"Что такое Atomic Design и как по нему разрезать экран?","a":"Атомы — базовые элементы (кнопка, поле). Молекулы — небольшие группы (поле + подпись + ошибка). Организмы — крупные блоки (шапка, карточка заказа). Шаблоны — раскладка страницы без данных. Страницы — шаблон с реальными данными. Режут сверху вниз и ищут, что уже есть в каталоге.","origin":"authored"},{"id":"design-system/when-to-extract","topic":"design-system","level":"middle","concept":"atomic-design","q":"Когда выносить компонент в общий каталог, а когда оставить в фиче?","a":"Выносить, когда он уже повторился (обычно 2–3 раза) и у него понятный стабильный API. Локальный компонент дешевле, чем плохой «атом на всё» с двадцатью флагами. Если в каталоге уже есть похожий — расширить его, а не делать дубль: дубль не проходит ревью.","origin":"authored"},{"id":"design-system/storybook","topic":"design-system","level":"middle","concept":"atomic-design","q":"Зачем дизайн-системе Storybook?","a":"Компоненты разрабатываются и показываются в изоляции, со всеми состояниями (stories). Это живая документация (autodocs), площадка для дизайнера и база для визуальных и a11y-тестов. Новый разработчик ищет готовый компонент там, а не пишет свой.","origin":"authored"},{"id":"dev-process/dod","topic":"dev-process","level":"junior","concept":"dev-process-basics","q":"Что такое Definition of Done и чем он отличается от критериев приёмки?","a":"DoD — общий для всех задач командный чек-лист «готово»: код на ревью, тесты, сборка зелёная, задеплоено. Критерии приёмки — условия конкретной задачи: что именно должно работать. Задача готова, когда выполнено и то, и другое.","origin":"authored"},{"id":"dev-process/path-to-prod","topic":"dev-process","level":"junior","concept":"dev-process-basics","q":"Как задача попадает от борды до продакшена?","a":"Бэклог → взяли в спринт → ветка → коммиты → MR → CI (линт, тесты, сборка) → ревью → слияние → деплой на стенд → проверка → релиз в прод. На каждом шаге задача видна на борде, чтобы команда знала статус.","origin":"authored"},{"id":"dev-process/status-honesty","topic":"dev-process","level":"junior","concept":"ask-for-help","q":"Ты понял, что не успеваешь задачу к сроку. Что делать?","a":"Сказать сразу, а не в последний день: что сделано, что осталось, новая оценка, что мешает. Предложить варианты: урезать объём, попросить помощь, сдвинуть срок. Плохая новость рано — это управляемый риск, поздно — сорванный план.","origin":"authored"},{"id":"dev-process/bug-report","topic":"dev-process","level":"junior","concept":"ask-for-help","q":"Что должно быть в хорошем баг-репорте?","a":"Шаги воспроизведения, ожидаемый и фактический результат, окружение (браузер или клиент, версия, стенд, пользователь), скриншот или видео, текст ошибки и логи (во фронтенде — Console и Network). Лучше — минимальный пример и гипотеза причины.","origin":"authored"},{"id":"dev-process/search-first","topic":"dev-process","level":"junior","concept":"ask-for-help","q":"Где и в каком порядке искать решение проблемы?","a":"Прочитать текст ошибки целиком. Официальная документация (angular.dev, MDN, learn.microsoft.com) и changelog версии. Issues в GitHub библиотеки. Поиск и Stack Overflow — с проверкой даты и версии. Ответ ИИ или форума проверять запуском, а не верить.","origin":"authored"},{"id":"dev-process/clarify","topic":"dev-process","level":"junior","concept":"dev-process-basics","q":"Почему требования уточняют до начала работы, а не после?","a":"Переделка после релиза стоит в разы дороже вопроса в начале. Перед стартом проверь: понятны ли критерии приёмки, состояния (пусто, ошибка, загрузка), права, граничные случаи, есть ли макеты для мобильного. Неясное — записать вопросом в задаче.","origin":"authored"},{"id":"dev-process/mr-flow","topic":"dev-process","level":"junior","concept":"code-review","q":"Как устроен процесс merge request и что делает его быстрым?","a":"Ветка от main → коммиты → MR с описанием «что и зачем», ссылкой на задачу, скриншотом → CI зелёный → ревью → правки → слияние. Быстрым его делает маленький размер: несколько сотен строк ревьюят за час, тысячи — неделями.","origin":"authored"},{"id":"dev-process/review-junior","topic":"dev-process","level":"junior","concept":"code-review","q":"Ты ревьюишь код стажёра. На что смотреть и как писать замечания?","a":"Сначала: делает ли код то, что нужно, и нет ли ошибок; потом читаемость и тесты; стиль оставь линтеру. Замечание: что, почему и пример, как лучше. Помечай необязательное как nit. Хвали хорошие решения — это тоже обучение.","origin":"authored"},{"id":"dev-tools-git/git-basics","topic":"dev-tools-git","level":"intern","concept":"git-basics","q":"Чем коммит отличается от ветки? Что делают push и pull?","a":"Коммит — неизменяемый снимок файлов с автором и ссылкой на родителя. Ветка — просто подвижный указатель на коммит. push отправляет твои коммиты в удалённый репозиторий. pull = fetch (забрать чужие коммиты) + слияние с текущей веткой.","origin":"authored"},{"id":"dev-tools-git/devtools-tour","topic":"dev-tools-git","level":"intern","concept":"devtools-debugging","q":"Какие вкладки DevTools нужны каждый день и для чего?","a":"Elements — DOM и применённые стили (Computed объясняет, откуда значение). Console — ошибки и выражения. Sources — точки останова и пошаговая отладка. Network — запросы, коды, заголовки, тайминги. Application — куки, localStorage. Performance и Lighthouse — скорость.","origin":"authored"},{"id":"dev-tools-git/lint-vs-format","topic":"dev-tools-git","level":"intern","concept":"lint-format","q":"Зачем в проекте и ESLint, и Prettier? Почему бы не спорить о стиле на ревью?","a":"Prettier форматирует: отступы, кавычки, переносы — без споров. ESLint ищет вероятные ошибки и плохие паттерны: неиспользуемые переменные, опасные конструкции. Если стиль проверяет машина, ревью тратит время на логику. oxlint и oxfmt — быстрые альтернативы с той же ролью.","origin":"authored"},{"id":"dev-tools-git/simple-bug","topic":"dev-tools-git","level":"intern","concept":"devtools-debugging","q":"Кнопка «Сохранить» ничего не делает. Как искать причину?","a":"Воспроизвести стабильно. Посмотреть Console на ошибки. В Network проверить, ушёл ли запрос и что ответил сервер. Поставить точку останова в обработчик и пройти по шагам. Сначала понять причину, потом чинить.","origin":"authored"},{"id":"dev-tools-git/git-conflict","topic":"dev-tools-git","level":"junior","concept":"git-history","q":"Как разрешить конфликт при merge и что делает cherry-pick?","a":"Git помечает конфликтные места <<<<<<< ======= >>>>>>>. Выбрать правильный вариант (часто объединить оба), убрать маркеры, проверить сборку и тесты, git add и завершить merge. cherry-pick копирует один коммит в текущую ветку — например, хотфикс в релизную ветку.","origin":"authored"},{"id":"dev-tools-git/npm-ci","topic":"dev-tools-git","level":"junior","concept":"dependency-trust","q":"Чем npm install отличается от npm ci и зачем package-lock в git?","a":"npm install может обновить lockfile по диапазонам из package.json. npm ci ставит строго по lockfile и падает при расхождении — так сборка в CI повторяема. Lockfile коммитят. В pnpm то же делает pnpm install --frozen-lockfile. Скрипты запускают через npm run <name>.","origin":"authored"},{"id":"dev-tools-git/breakpoints","topic":"dev-tools-git","level":"junior","concept":"devtools-debugging","q":"Какие точки останова есть в DevTools, кроме обычной по строке?","a":"Условная (останов, только если выражение true), logpoint (печать без останова), на изменение DOM, на XHR/fetch по части URL, на обработчик события, на исключения. Они дают поймать редкий баг без правки кода.","origin":"authored"},{"id":"dev-tools-git/network-tab","topic":"dev-tools-git","level":"junior","concept":"devtools-debugging","q":"Что можно узнать во вкладке Network при отладке API?","a":"Ушёл ли запрос, метод, URL, заголовки, тело, код ответа и тело ответа, тайминги (ожидание сервера, загрузка), из кэша ли ответ. Можно включить троттлинг сети, отключить кэш, повторить запрос, скопировать как curl или fetch.","origin":"authored"},{"id":"dev-tools-git/stash","topic":"dev-tools-git","level":"intern","concept":"git-basics","q":"Что делает git stash и чем stash pop отличается от stash apply?","a":"git stash убирает незакоммиченные изменения в отдельное хранилище и возвращает рабочую папку к HEAD — например, чтобы срочно переключиться на другую ветку. Новые неотслеживаемые файлы по умолчанию не прячутся, для них нужен -u. pop возвращает изменения и удаляет запись из списка, apply возвращает и оставляет. При конфликте pop запись не удаляет.","origin":"authored"},{"id":"dev-tools-git/gitignore","topic":"dev-tools-git","level":"intern","concept":"git-basics","q":"Что кладут в .gitignore и почему файл, уже попавший в git, продолжает отслеживаться?","a":"В .gitignore пишут то, что не должно попасть в репозиторий: node_modules, сборку (dist), файлы ОС вроде .DS_Store, локальные .env. Он действует только на неотслеживаемые файлы. Если файл уже закоммичен, сначала git rm --cached file — он останется на диске, но уйдёт из индекса. В старых коммитах файл всё равно останется.","origin":"authored"},{"id":"dom-events/query-change","topic":"dom-events","level":"intern","concept":"dom-api","q":"Как найти элемент на странице и поменять его текст? Почему не innerHTML?","a":"document.querySelector('.title') вернёт первый подходящий элемент (или null), querySelectorAll — статический список всех. Текст меняют через textContent: он вставляет строку как текст. innerHTML парсит строку как HTML — с данными пользователя это путь к XSS.","origin":"authored"},{"id":"dom-events/bubbling","topic":"dom-events","level":"intern","concept":"event-propagation","q":"Клик по кнопке внутри <div> с обработчиком. Сработает ли обработчик <div> и почему?","a":"Да: событие всплывает от цели вверх по предкам. Сначала идёт фаза погружения сверху вниз, потом цель, потом всплытие. event.target — где кликнули, event.currentTarget — чей обработчик сейчас работает. Остановить всплытие можно stopPropagation(), но это редко нужно.","origin":"authored"},{"id":"dom-events/delegation","topic":"dom-events","level":"intern","concept":"event-propagation","q":"Что такое делегирование событий и когда оно полезно?","a":"Один обработчик ставят на общего предка и по event.target (через closest) понимают, по какому ребёнку кликнули. Полезно для длинных и динамических списков: меньше обработчиков, и новые элементы работают без новой подписки.","origin":"authored"},{"id":"dom-events/prevent-default","topic":"dom-events","level":"intern","concept":"event-propagation","q":"Чем preventDefault() отличается от stopPropagation()?","a":"preventDefault() отменяет действие браузера по умолчанию: отправку формы, переход по ссылке. stopPropagation() останавливает всплытие к предкам. Это независимые вещи: одно не делает другое.","origin":"authored"},{"id":"dom-events/add-event-listener","topic":"dom-events","level":"intern","concept":"dom-api","q":"Чем addEventListener лучше, чем свойство onclick или атрибут onclick в HTML?","a":"addEventListener позволяет повесить несколько обработчиков на одно событие, а onclick хранит только одну функцию. У него есть опции: once (снять после первого вызова), passive, capture и signal (снять через AbortController). Чтобы снять обработчик через removeEventListener, нужна та же ссылка на функцию — анонимную снять нельзя. Атрибут onclick в HTML смешивает разметку и код, поэтому его не используют.","origin":"authored"},{"id":"dom-events/target-vs-current-target","topic":"dom-events","level":"intern","concept":"event-propagation","q":"Чем event.target отличается от event.currentTarget?","a":"target — элемент, на котором событие возникло (например, span внутри кнопки). currentTarget — элемент, на котором висит сработавший обработчик. При всплытии они часто разные. Вне обработчика currentTarget равен null — после await его уже не прочитать, поэтому сохрани элемент в переменную заранее.","origin":"authored"},{"id":"dom-events/domcontentloaded-vs-load","topic":"dom-events","level":"intern","concept":"dom-api","q":"Чем событие DOMContentLoaded отличается от load?","a":"DOMContentLoaded наступает, когда HTML разобран и выполнены скрипты с defer и type=\"module\"; картинки, iframe и async-скрипты он не ждёт. load наступает, когда страница загрузилась полностью, со всеми ресурсами. Для работы с DOM обычно хватает DOMContentLoaded — частая ошибка ждать load без нужды.","origin":"authored"},{"id":"dom-events/attribute-vs-property","topic":"dom-events","level":"intern","concept":"dom-api","q":"Чем атрибут отличается от свойства DOM-элемента? Пример с <input value>.","a":"Атрибут записан в HTML и читается через getAttribute. Свойство — поле JS-объекта элемента. Многие атрибуты отражаются в свойства (id, placeholder). Но у input атрибут value — начальное значение (его отражает defaultValue), а свойство value — то, что сейчас в поле: пользователь печатает, меняется только свойство.","origin":"authored"},{"id":"dotnet-basics/components","topic":"dotnet-basics","level":"intern","concept":"dotnet-runtime","q":"Из чего состоит .NET и чем SDK отличается от runtime?","a":"Runtime выполняет код приложения, библиотеки дают готовые функции, компилятор переводит C# в исполняемый код. SDK — набор инструментов для разработки: CLI, компиляторы, сборка. Для запуска нужен runtime, для разработки — SDK.","origin":"authored"},{"id":"dotnet-basics/il-jit","topic":"dotnet-basics","level":"intern","concept":"dotnet-runtime","q":"Что происходит с C#-кодом от компиляции до выполнения?","a":"Компилятор переводит исходный код в промежуточный язык CIL и метаданные (сборка .dll). Во время выполнения JIT-компилятор CLR превращает CIL в машинный код для текущего процессора.","origin":"authored"},{"id":"dotnet-basics/gc-generations","topic":"dotnet-basics","level":"intern","concept":"dotnet-runtime","q":"Как работает сборщик мусора в .NET и что такое поколения?","a":"GC сам освобождает память объектов, на которые больше нет ссылок. Управляемая куча делится на поколения 0, 1 и 2: новые объекты попадают в 0, выжившие переходят дальше. Короткоживущие объекты собираются часто и дёшево.","origin":"authored"},{"id":"dotnet-basics/cli","topic":"dotnet-basics","level":"intern","concept":"dotnet-sdk-projects","q":"Какие команды .NET CLI нужны каждый день?","a":"dotnet new — создать проект по шаблону, dotnet build — собрать, dotnet run — запустить, dotnet test — тесты, dotnet add package (в .NET 10 также dotnet package add) — добавить NuGet-пакет, dotnet publish — подготовить к деплою.","origin":"authored"},{"id":"dotnet-basics/csproj-tfm","topic":"dotnet-basics","level":"intern","concept":"dotnet-sdk-projects","q":"Что задаёт TargetFramework в .csproj и какой TFM у .NET 10?","a":"TargetFramework говорит, под какую версию .NET собирается проект. Для .NET 10 это net10.0. SDK-style проект короткий: Sdk=Microsoft.NET.Sdk (или Microsoft.NET.Sdk.Web), TargetFramework и PackageReference.","code":"<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n  <PropertyGroup>\n    <TargetFramework>net10.0</TargetFramework>\n    <Nullable>enable</Nullable>\n  </PropertyGroup>\n</Project>","origin":"authored"},{"id":"dotnet-basics/nuget","topic":"dotnet-basics","level":"junior","concept":"dotnet-nuget","q":"Как подключают NuGet-пакеты и зачем Central Package Management?","a":"Пакет подключают через PackageReference в .csproj (dotnet add package). В решении с многими проектами версии удобно держать в одном месте: Directory.Packages.props с ManagePackageVersionsCentrally=true и элементами PackageVersion.","origin":"authored"},{"id":"dotnet-basics/lts-sts","topic":"dotnet-basics","level":"intern","concept":"dotnet-releases","q":"Что такое LTS и STS, и к какому типу относится .NET 10?","a":"LTS поддерживается три года, STS — два года (24 месяца). Качество одинаковое, разница только в сроке поддержки. .NET 10 — LTS, поддержка до ноября 2028 года.","origin":"authored"},{"id":"dotnet-basics/global-json","topic":"dotnet-basics","level":"junior","concept":"dotnet-sdk-projects","q":"Зачем в репозитории файл global.json?","a":"Он выбирает версию SDK для сборки. Это важно в CI, чтобы все собирали одним SDK. Поле rollForward задаёт, какие более новые версии допустимы.","code":"{ \"sdk\": { \"version\": \"10.0.100\", \"rollForward\": \"latestFeature\" } }","origin":"authored"},{"id":"dotnet-basics/file-based-apps","topic":"dotnet-basics","level":"junior","concept":"dotnet-releases","q":"Что такое file-based apps в .NET 10?","a":"Можно запустить один .cs-файл без .csproj: dotnet run app.cs. Пакеты и SDK задают директивы в начале файла: #:package, #:sdk, #:property. Удобно для скриптов и прототипов.","origin":"authored"},{"id":"dotnet-basics/namespaces-using","topic":"dotnet-basics","level":"intern","concept":"dotnet-namespaces","q":"Зачем нужны namespace и директива using?","a":"Namespace группирует типы и разводит одинаковые имена: MyApp.Models.Order и Billing.Order — разные типы. using в начале файла позволяет писать короткое имя типа вместо полного. File-scoped запись `namespace MyApp.Models;` относится ко всему файлу без лишних скобок. В SDK-проектах включены ImplicitUsings: частые пространства имён (System, System.Linq и другие) подключаются глобально автоматически.","origin":"authored"},{"id":"dotnet-basics/solution-structure","topic":"dotnet-basics","level":"intern","concept":"dotnet-sdk-projects","q":"Из чего состоит типичное .NET-решение и где у программы точка входа?","a":"Решение объединяет проекты; с .NET 10 `dotnet new sln` по умолчанию создаёт файл .slnx вместо .sln. Проект — это .csproj: TargetFramework, NuGet-пакеты и ссылки на другие проекты. Точка входа обычно Program.cs с top-level statements: код пишут прямо в файле, а метод Main генерирует компилятор, и такой файл в проекте может быть только один. Папки bin и obj — результат сборки, их не коммитят.","origin":"authored"},{"id":"dotnet-basics/debug-vs-release","topic":"dotnet-basics","level":"intern","concept":"dotnet-build-configurations","q":"Чем сборка Debug отличается от Release?","a":"Debug собирается без оптимизаций и с полной отладочной информацией, поэтому breakpoint и пошаговая отладка точно совпадают с исходным кодом. Release включает оптимизации: код быстрее и меньше, но отлаживать его труднее, часть переменных может быть недоступна. В продакшен выкладывают Release, например `dotnet publish -c Release`.","origin":"authored"},{"id":"dotnet-basics/try-catch-finally","topic":"dotnet-basics","level":"intern","concept":"dotnet-exceptions","q":"Как работают try, catch и finally?","a":"Код, который может упасть, кладут в try. Блоки catch проверяются сверху вниз, поэтому конкретные исключения ставят выше общего Exception, а фильтр `when` ловит исключение только при выполнении условия. finally выполняется всегда, когда управление покидает try, — и при успехе, и при исключении, поэтому там освобождают ресурсы. Ловить стоит только то, что умеешь обработать, остальное пусть уходит выше.","origin":"authored"},{"id":"dotnet-basics/using-idisposable","topic":"dotnet-basics","level":"intern","concept":"dotnet-dispose","q":"Что делает инструкция using и зачем нужен IDisposable?","a":"IDisposable реализуют объекты, которые держат внешние ресурсы: файлы, сокеты, соединения с БД. Сборщик мусора про такие ресурсы не знает, поэтому их освобождают явно методом Dispose. Инструкция using гарантирует вызов Dispose при выходе из блока, даже при исключении. Короткая форма `using var reader = ...;` освобождает объект в конце текущей области видимости.","code":"using var reader = File.OpenText(\"numbers.txt\");\nvar firstLine = reader.ReadLine();","origin":"authored"},{"id":"dotnet-basics/framework-core-standard","topic":"dotnet-basics","level":"intern","concept":"dotnet-flavors","q":"Чем .NET Framework отличается от .NET (бывший .NET Core) и что такое .NET Standard?","a":".NET Framework — старая платформа только для Windows, последняя версия 4.8.1. .NET (раньше .NET Core, с версии 5 просто .NET) — кроссплатформенный и open source, новые версии выходят каждый год, для новых проектов берут его. .NET Standard — не платформа, а спецификация общего набора API, и новых версий у неё больше не будет. Сегодня netstandard2.0 выбирают, только если библиотеку нужно использовать ещё и из .NET Framework.","origin":"authored"},{"id":"ef-core-basics/dbcontext-lifetime","topic":"ef-core-basics","level":"junior","concept":"efcore-dbcontext-lifetime","q":"Какой lifetime у DbContext в ASP.NET Core и почему?","a":"AddDbContext регистрирует контекст как Scoped: один экземпляр на HTTP-запрос. DbContext задуман как короткая единица работы: загрузил, изменил, SaveChanges, освободил. Он не потокобезопасен и копит отслеживаемые сущности, поэтому Singleton для него — ошибка.","code":"builder.Services.AddDbContext<AppDbContext>(options =>\n    options.UseNpgsql(builder.Configuration.GetConnectionString(\"Default\")));","origin":"authored"},{"id":"ef-core-basics/second-operation","topic":"ef-core-basics","level":"junior","concept":"efcore-dbcontext-lifetime","q":"Откуда ошибка «A second operation was started on this context instance before a previous operation completed»?","a":"Два запроса одновременно идут через один DbContext: например, забыли await или запустили Task.WhenAll на одном контексте. DbContext не поддерживает параллельные операции. Решение: всегда сразу делать await, а для параллельной работы брать отдельные контексты через IDbContextFactory.","origin":"authored"},{"id":"ef-core-basics/migrations","topic":"ef-core-basics","level":"junior","concept":"efcore-migrations","q":"Как работают миграции EF Core?","a":"dotnet ef migrations add Name сравнивает текущую модель со снапшотом прошлой модели и генерирует файл миграции. dotnet ef database update применяет к базе ещё не применённые миграции. Список применённых миграций EF хранит в отдельной таблице истории в самой базе.","code":"dotnet ef migrations add AddBlogCreatedTimestamp\ndotnet ef database update","origin":"authored"},{"id":"ef-core-basics/migrations-production","topic":"ef-core-basics","level":"middle","concept":"efcore-migrations","q":"Как применять миграции на проде и почему не через Migrate() при старте приложения?","a":"Рекомендуемый способ — SQL-скрипт (dotnet ef migrations script --idempotent), который можно проверить, или migration bundle в CI/CD. Migrate() при старте даёт гонку нескольких экземпляров и требует от приложения прав на изменение схемы. С EF Core 9 Migrate() берёт блокировку на базу и бросает исключение, если в модели есть изменения без миграции.","origin":"authored"},{"id":"ef-core-basics/asnotracking","topic":"ef-core-basics","level":"junior","concept":"efcore-tracking","q":"Что такое change tracking и когда нужен AsNoTracking()?","a":"По умолчанию EF отслеживает загруженные сущности, и SaveChanges находит изменения и пишет их в базу. AsNoTracking() отключает отслеживание: запрос быстрее и тратит меньше памяти, но изменения таких объектов SaveChanges не увидит. Используй его для запросов только на чтение.","origin":"authored"},{"id":"ef-core-basics/n-plus-one","topic":"ef-core-basics","level":"junior","concept":"efcore-loading","q":"Что такое проблема N+1 в EF Core и как её исправить?","a":"Один запрос загружает N блогов, а потом для каждого блога отдельный запрос грузит посты — всего N+1 обращений к базе. Обычно так бывает с lazy loading в цикле. Исправление: загрузить связанные данные сразу через Include или выбрать нужные поля через Select.","code":"// N+1: lazy loading делает запрос на каждой итерации\nforeach (var blog in context.Blogs.ToList())\n    Console.WriteLine(blog.Posts.Count);\n\n// один запрос\nvar blogs = await context.Blogs.Include(b => b.Posts).ToListAsync();","origin":"authored"},{"id":"ef-core-basics/split-query","topic":"ef-core-basics","level":"middle","concept":"efcore-loading","q":"Что такое cartesian explosion и как помогает AsSplitQuery()?","a":"Если сделать Include двух коллекций на одном уровне, JOIN вернёт произведение строк, и данные родителя дублируются много раз. AsSplitQuery() загружает каждую коллекцию отдельным SQL-запросом. Цена: больше обращений к базе и нет гарантии согласованности между запросами без транзакции.","origin":"authored"},{"id":"ef-core-basics/dapper-vs-ef","topic":"ef-core-basics","level":"junior","concept":"dapper-vs-efcore","q":"Чем Dapper отличается от EF Core и когда что выбрать?","a":"Dapper — micro-ORM: ты сам пишешь SQL, а он только мапит результат на объекты. EF Core — полный ORM: переводит LINQ в SQL, отслеживает изменения и ведёт миграции. EF удобен для обычного CRUD, Dapper — когда нужен полный контроль над SQL; их можно использовать вместе.","origin":"authored"},{"id":"ef-core-basics/fromsql-injection","topic":"ef-core-basics","level":"junior","concept":"sql-injection-params","q":"Как безопасно выполнить сырой SQL в EF Core?","a":"FromSql с интерполированной строкой безопасен: EF превращает каждое {значение} в DbParameter. FromSqlRaw со склейкой строк открывает SQL-инъекцию. Имена таблиц и столбцов параметром передать нельзя, их проверяют по белому списку.","code":"// безопасно: user станет параметром\nvar blogs = await context.Blogs\n    .FromSql($\"SELECT * FROM Blogs WHERE Owner = {user}\")\n    .ToListAsync();\n\n// опасно: строка склеивается с вводом\ncontext.Blogs.FromSqlRaw(\"SELECT * FROM Blogs WHERE Owner = '\" + user + \"'\");","origin":"authored"},{"id":"engineering-craft/decompose","topic":"engineering-craft","level":"middle","concept":"estimation","q":"Как оценить и разбить большую фичу?","a":"Разрезать на вертикальные куски, каждый можно показать и проверить (UI + API + тесты), по 1–2 дня. Найти неизвестное и сделать короткий спайк. Оценить диапазоном и назвать допущения. Story points — относительная сложность для планирования, а не часы.","origin":"authored"},{"id":"engineering-craft/prioritize","topic":"engineering-craft","level":"middle","concept":"estimation","q":"У тебя три срочные задачи одновременно. Как решить, что делать первым?","a":"Уточнить у тимлида или продакта реальный приоритет и сроки, а не угадывать. Ограничить работу в процессе: доделать одно, а не начать три. Блокирующее других — раньше. О том, что не успеваешь, сказать сразу.","origin":"authored"},{"id":"engineering-craft/edge-cases","topic":"engineering-craft","level":"middle","concept":"estimation","q":"Какие граничные случаи проверить у формы оплаты?","a":"Пустые и максимальные значения, неверный формат, двойной клик по «Оплатить», потеря сети и повтор, медленный ответ, ошибка сервера, истёкшая сессия, другая валюта и локаль, клавиатура и скринридер, назад в браузере после оплаты.","origin":"authored"},{"id":"engineering-craft/review-feedback","topic":"engineering-craft","level":"middle","concept":"code-review","q":"Как давать полезную обратную связь на ревью?","a":"Про код, а не про человека. Объяснять почему и предлагать вариант. Разделять обязательное и nit. Вопрос вместо приказа, если не уверен. Отвечать быстро: долгое ревью тормозит всю команду. Хорошие решения тоже отмечать.","origin":"authored"},{"id":"engineering-craft/standards","topic":"engineering-craft","level":"middle","concept":"continuous-improvement","q":"Как ввести в команде новый стандарт (code style, процесс ревью, DoD), чтобы он прижился?","a":"Объяснить проблему и предложить решение письменно, обсудить с командой, записать договорённость. Всё, что можно, автоматизировать: линтер, шаблон MR, проверки в CI. Через несколько недель посмотреть, работает ли, и поправить.","origin":"authored"},{"id":"engineering-craft/retro-action","topic":"engineering-craft","level":"middle","concept":"continuous-improvement","q":"Как довести идею с ретро до реального изменения процесса?","a":"Одно конкретное действие с владельцем и сроком, а не «будем лучше». Измерить «до» (например, время ревью), внедрить, через спринт сравнить «после». Не сработало — откатить или поменять. На следующем ретро проверить прошлые действия.","origin":"authored"},{"id":"engineering-craft/mistakes","topic":"engineering-craft","level":"middle","concept":"continuous-improvement","q":"Твоё изменение уронило прод. Как себя вести?","a":"Сразу сообщить, помочь откатить или починить — сначала восстановить сервис. Потом разобрать без поиска виноватых: какая цепочка причин, почему проверки не поймали. Итог — конкретные действия (тест, алерт, проверка в CI), чтобы это не повторилось.","origin":"authored"},{"id":"engineering-craft/tech-pilot","topic":"engineering-craft","level":"middle","concept":"continuous-improvement","q":"Ты хочешь внедрить новую технологию (например, новый тестовый фреймворк или новый API фреймворка). Как это предложить?","a":"Описать проблему, которую она решает, и цену. Проверить статус (stable или experimental) и поддержку. Сделать пилот на небольшой части с критерием успеха и метрикой до и после (время тестов, размер бандла). Показать результат и план отката. Без измеримого эффекта — не внедрять.","origin":"authored"},{"id":"engineering-craft/feature-ownership","topic":"engineering-craft","level":"middle","concept":"estimation","q":"Что значит вести фичу самостоятельно от оценки до релиза?","a":"Уточнить требования, оценить и разбить, договориться об API с бэкендом, сделать с тестами, обновить документацию, провести через ревью и деплой, проверить на проде и в метриках. Блокеры поднимать сразу. Готово — когда пользователи получили ценность, а не когда смёржен MR.","origin":"authored"},{"id":"engineering-craft/api-contract","topic":"engineering-craft","level":"middle","concept":"estimation","q":"Как договориться с бэкендом и дизайнером, чтобы не переделывать фичу?","a":"Зафиксировать контракт API до начала (OpenAPI или пример JSON): поля, ошибки, пагинация. С дизайнером — все состояния экрана и поведение на мобильном. Работать параллельно на моках по контракту. Изменения контракта — только через договорённость.","origin":"authored"},{"id":"engineering-craft/dora-metrics","topic":"engineering-craft","level":"lead","concept":"engineering-metrics","q":"Какие метрики DORA ты бы смотрел у своих команд и что они показывают?","a":"Скорость: change lead time и deployment frequency. Стабильность: change fail rate, failed deployment recovery time и deployment rework rate (в текущей модели DORA метрик пять). Смотреть их вместе, по сервису, и сравнивать команду с собой в прошлом, а не с другими командами. Это индикатор, где искать проблему в процессе поставки, а не оценка людей.","origin":"authored"},{"id":"engineering-craft/productivity-myth","topic":"engineering-craft","level":"lead","concept":"engineering-metrics","q":"Руководство хочет оценивать инженеров по числу коммитов, строк кода или story points. Что ты ответишь?","a":"Это меряет активность, а не результат: строки зависят от языка и стиля, а хороший рефакторинг их удаляет; story points — инструмент планирования, их легко «надуть». Как только такая цифра становится целью, люди оптимизируют цифру (закон Гудхарта). Предлагаю другое: результаты для бизнеса, метрики поставки на уровне команды и опросы инженеров. И спрашиваю, какую проблему руководство хочет решить.","origin":"authored"},{"id":"engineering-craft/standards-scope","topic":"engineering-craft","level":"lead","concept":"platform-thinking","q":"Какие инженерные правила должны быть обязательными для всех команд, а какие — на выбор команды?","a":"Обязательное — там, где ошибка одной команды бьёт по всем: безопасность, данные, контракты API, наблюдаемость, деплой. Остальное — на выбор команды, а платформа даёт удобный «проторённый путь», которым пользоваться выгоднее, чем делать своё. Каждое обязательное правило — с проверкой в CI, а не в документе. Признак перекоса: команды массово просят исключения или обходят правило.","origin":"authored"},{"id":"engineering-craft/security-champions","topic":"engineering-craft","level":"lead","concept":"ssdlc-culture","q":"Security-команда маленькая и не успевает ревьюить всех. Как поднять безопасность в командах?","a":"Запустить программу security champions: в каждой команде инженер, который отвечает за безопасность и связан с security-командой. Нужны поддержка руководства, выделенное время, капитан программы, сообщество, обучение и признание вклада. Чемпионы приносят threat modeling и проверки в обычную работу команды. Успех — уязвимости находят раньше (на дизайне и в CI), а не на проде.","origin":"authored"},{"id":"engineering-craft/inner-source","topic":"engineering-craft","level":"lead","concept":"platform-thinking","q":"Командам постоянно нужны доработки в твоём общем сервисе, а у твоей команды нет на это времени. Что делать?","a":"Открыть сервис по модели InnerSource: другие команды сами присылают изменения, как в open source. Для этого нужны понятные правила вклада, хорошие тесты и CI, документация и люди, которые быстро ревьюят чужие MR. Плюс — меньше очереди и больше общего владения; минус — время на ревью и риск, что код станет сложнее. Успех — время от запроса до релиза доработки сокращается.","origin":"authored"},{"id":"engineering-craft/space-framework","topic":"engineering-craft","level":"principal","concept":"engineering-metrics","q":"Что такое SPACE и как его использовать при измерении продуктивности инженеров?","a":"SPACE — пять измерений: Satisfaction and well-being, Performance, Activity, Communication and collaboration, Efficiency and flow. Главная мысль: продуктивность нельзя измерить одной метрикой. Берут несколько метрик из разных измерений (минимум три) и сочетают опросы с системными данными. Так активность (коммиты) не выдают за результат, а перекосы видно сразу.","origin":"authored"},{"id":"engineering-craft/dora-rollout","topic":"engineering-craft","level":"principal","concept":"engineering-metrics","q":"Как ввести метрики DORA в компании, чтобы их не превратили в KPI и рейтинг команд?","a":"Не ставить метрику как цель («все деплоят каждый день к концу года») — по закону Гудхарта её начнут подгонять. Не сравнивать разные системы между собой и не устраивать соревнование команд. Метрики — у команды, для поиска узких мест и отслеживания своего прогресса, вместе с другими метриками. Успех меряю не цифрами в дашборде, а тем, что команды по ним что-то улучшили.","origin":"authored"},{"id":"engineering-craft/ssdlc-program","topic":"engineering-craft","level":"principal","concept":"ssdlc-culture","q":"Как бы ты построил безопасную разработку (SSDLC) во всей компании?","a":"Начать с оценки зрелости по модели (например, OWASP SAMM: governance, design, implementation, verification, operations) и выбрать 2–3 слабых места, а не всё сразу. Безопасность встроить в обычный путь: threat modeling на дизайне, SAST и проверка зависимостей в CI, security champions в командах, разбор инцидентов. Платформа делает безопасный путь самым простым. Прогресс — повторная оценка зрелости и время исправления уязвимостей.","origin":"authored"},{"id":"engineering-craft/devex-survey","topic":"engineering-craft","level":"principal","concept":"engineering-metrics","q":"Как понять, что на самом деле мешает инженерам компании работать продуктивно?","a":"Спросить самих инженеров: регулярный опрос о developer experience (сборки, ревью, тесты, ожидание других команд, фокус). Сочетать ответы с данными систем: время CI, время ревью, частота деплоев. Выбрать 1–2 самые болезненные точки, исправить и повторить опрос. Опросы без действий быстро теряют доверие, поэтому результаты и план показывают всем.","origin":"authored"},{"id":"frameworks-landscape/read-react","topic":"frameworks-landscape","level":"middle","concept":"ui-frameworks","q":"Ты Angular-разработчик и читаешь React-компонент. Что важно понимать?","a":"Компонент — функция, которая возвращает JSX. Состояние — useState, побочные эффекты — useEffect со списком зависимостей. При изменении состояния функция компонента выполняется заново. Нет DI: зависимости — импорты, props и context.","origin":"authored"},{"id":"frameworks-landscape/reactivity-compare","topic":"frameworks-landscape","level":"middle","concept":"ui-frameworks","q":"Чем реактивность Vue и Svelte похожа на сигналы Angular?","a":"Во Vue ref/computed, в Svelte 5 руны $state/$derived — это та же идея: значение знает своих читателей, и обновляется только то, что от него зависит. Перенос навыка почти прямой: signal ≈ ref ≈ $state, computed ≈ computed ≈ $derived.","origin":"authored"},{"id":"frontend-architecture/project-skeleton","topic":"frontend-architecture","level":"senior","concept":"bounded-contexts","q":"Ты запускаешь новый фронтенд-проект для команды. Что должно быть готово в первую неделю?","a":"Репозиторий со стеком, выбранным по ADR. Линт, форматтер, типы, тесты и сборка в CI. Деплой на стенд и preview на PR. Структура папок и правила зависимостей. Одна сквозная фича как образец. README: как запустить, как добавить фичу. Задачи по настройке делегированы, а не сделаны одним человеком.","origin":"authored"},{"id":"frontend-architecture/handover","topic":"frontend-architecture","level":"senior","concept":"bounded-contexts","q":"Как передать запущенный проект команде, чтобы тебя не дёргали по каждому вопросу?","a":"Решения записаны в ADR, есть README и гайд «как добавить фичу», правила проверяют линтер и CI, а не ты на ревью. Есть второй человек, который знает каждую часть. Пара недель — команда ведёт, ты только ревьюишь. Вопросы, которые повторяются, — в документацию.","origin":"authored"},{"id":"frontend-architecture/ddd","topic":"frontend-architecture","level":"senior","concept":"bounded-contexts","q":"Что из DDD полезно во фронтенде?","a":"Bounded contexts: модули совпадают с бизнес-областями (каталог, корзина, оплата), у каждого свой публичный API. Общий язык: названия в коде как у бизнеса. Модели не делятся между контекстами — «товар» в каталоге и в корзине разный. Границы проверяет линтер (теги Nx, правила импорта).","origin":"authored"},{"id":"frontend-architecture/state-design","topic":"frontend-architecture","level":"senior","concept":"bounded-contexts","q":"Как спроектировать сложное состояние: кэш сервера, UI-состояние, формы, URL?","a":"Разделить виды: серверные данные — кэш с инвалидацией и статусами (resource, SignalStore, query-библиотека); UI-состояние — локально; то, чем делятся по ссылке (фильтры, страница), — в URL; черновики форм — в форме. Один источник истины на каждый факт, остальное — производное (computed). Поток в одну сторону: событие → изменение → отображение.","origin":"authored"},{"id":"frontend-architecture/mfe-when","topic":"frontend-architecture","level":"senior","concept":"micro-frontends","q":"Когда микрофронтенды оправданы, а когда вредны?","a":"Оправданы, когда несколько команд мешают друг другу релизить, нужны независимые деплои или постепенная миграция легаси. Цена: дублирование зависимостей, сложная общая навигация и дизайн, версии общих библиотек, отладка между приложениями. Для одной-двух команд обычно лучше монорепо (Nx) с жёсткими границами модулей.","origin":"authored"},{"id":"frontend-architecture/mfe-integration","topic":"frontend-architecture","level":"senior","concept":"micro-frontends","q":"Какие есть способы собрать микрофронтенды на одной странице?","a":"Во время сборки (пакеты) — просто, но нет независимого деплоя. В рантайме: Module Federation (общие зависимости, загрузка удалённых модулей), Web Components (Angular Elements), iframe (полная изоляция, плохо для UX), серверная/edge-сборка страницы. Нужно договориться о роутинге, общих зависимостях и общении (события, URL).","origin":"authored"},{"id":"frontend-architecture/nx-monorepo","topic":"frontend-architecture","level":"senior","concept":"bounded-contexts","q":"Что даёт монорепо на Nx большому фронтенду?","a":"Код нескольких приложений и библиотек в одном месте, атомарные изменения сразу во всех. Граф зависимостей: affected-команды гоняют тесты и сборку только для затронутого. Кэш задач. Теги и правила границ модулей не дают фичам лезть друг в друга.","origin":"authored"},{"id":"frontend-architecture/pwa","topic":"frontend-architecture","level":"senior","concept":"pwa-service-worker","q":"Что нужно для офлайн-режима и в чём главный риск service worker?","a":"Service worker перехватывает запросы и отдаёт из кэша по стратегии: cache-first для статики, network-first для данных. В Angular — @angular/service-worker и ngsw-config.json. Риск: ошибка кэширования «застревает» у пользователей. Нужны версионирование, проверка обновлений и аварийный выключатель (kill switch).","origin":"authored"},{"id":"frontend-architecture/push","topic":"frontend-architecture","level":"senior","concept":"pwa-service-worker","q":"Как работают push-уведомления в вебе?","a":"Пользователь даёт разрешение, service worker подписывается через PushManager и получает endpoint и ключи — их хранит сервер. Сервер шлёт сообщение через push-сервис браузера, SW получает событие push и показывает уведомление, даже если вкладка закрыта. Просить разрешение — по действию пользователя, а не сразу.","origin":"authored"},{"id":"frontend-architecture/backend-layers","topic":"frontend-architecture","level":"senior","concept":"backend-for-frontend","q":"Как устроено типичное backend-приложение и зачем фронтенду это понимать?","a":"Слои: маршруты/контроллеры (HTTP), сервисы с бизнес-логикой, доступ к данным (БД, другие API). Понимание помогает договориться о контракте, пагинации, ошибках, транзакциях и понять, почему «просто добавить поле» иногда дорого. На Express или Next.js фронтендер может сам написать простой BFF.","origin":"authored"},{"id":"frontend-architecture/bff","topic":"frontend-architecture","level":"senior","concept":"backend-for-frontend","q":"Зачем нужен BFF (Backend for Frontend)?","a":"Тонкий сервер под конкретный фронтенд: собирает данные из нескольких сервисов в один ответ, хранит токены и секреты (куки HttpOnly вместо токенов в JS), адаптирует формат. Не превращать в место для бизнес-логики, которая нужна другим клиентам.","origin":"authored"},{"id":"frontend-architecture/rendering-choice","topic":"frontend-architecture","level":"senior","concept":"rendering-strategies","q":"Как выбрать между CSR, SSR и SSG для нового продукта на Angular?","a":"Зависит от страницы, а не от всего продукта. Одинаковый для всех контент (лендинг, блог, документация) — SSG: HTML готов при сборке, быстрый TTFB и CDN. Персональные и часто меняющиеся страницы, важные для SEO, — SSR, ценой нагрузки на сервер и TTFB. Кабинет за логином — CSR. В Angular 21 режим задают по маршрутам (RenderMode.Prerender / Server / Client); помнить, что гидратация стоит JS и бьёт по INP.","origin":"authored"},{"id":"frontend-architecture/error-handling","topic":"frontend-architecture","level":"senior","concept":"ng-error-handling","q":"Как устроить обработку ошибок во фронтенде на Angular 21?","a":"Ожидаемые ошибки ловят там, где они возникают: catchError в RxJS, try/catch, состояние error у resource — и показывают пользователю понятное состояние экрана. Неожиданные уходят в корневой ErrorHandler: свой обработчик шлёт их в Sentry или логи, а не только в консоль. provideBrowserGlobalErrorListeners (CLI добавляет его в новые приложения) пересылает туда window error и unhandledrejection. ErrorHandler — для отчёта, а не для «починки» состояния.","origin":"authored"},{"id":"frontend-architecture/data-layer","topic":"frontend-architecture","level":"senior","concept":"app-structure","q":"API бэкенда часто меняет формат, и правки расползаются по всем компонентам. Как это исправить?","a":"Разделить слои: тонкий view, доменная модель с логикой и отдельный слой доступа к данным. Слой данных работает как anti-corruption layer: получает ответ API и сразу превращает DTO в доменные объекты. Тогда смена формата API правится в одном файле, а компоненты и тесты не трогаются. Цена — немного больше кода маппинга, поэтому для простых CRUD-экранов слой делают тонким.","origin":"authored"},{"id":"frontend-delivery/ci-pipeline","topic":"frontend-delivery","level":"middle","concept":"frontend-ci","q":"Из каких шагов состоит хороший CI/CD для фронтенда?","a":"Установка по lockfile с кэшем → линт и форматирование → проверка типов → юнит-тесты → сборка с бюджетами → E2E на ключевых сценариях → preview-окружение на каждый PR → автодеплой после слияния. Быстрые проверки идут первыми, чтобы падать рано.","origin":"authored"},{"id":"frontend-delivery/preview-env","topic":"frontend-delivery","level":"middle","concept":"frontend-ci","q":"Что дают preview-окружения на каждый PR?","a":"Дизайнер, тестировщик и продакт смотрят изменение до слияния по ссылке, без локальной сборки. Ошибки находятся раньше, ревью быстрее. Окружение удаляется после закрытия PR.","origin":"authored"},{"id":"frontend-delivery/docker-nginx","topic":"frontend-delivery","level":"middle","concept":"frontend-ci","q":"Как упаковать Angular SPA в Docker и почему после обновления страницы бывает 404?","a":"Multi-stage сборка: в первом образе Node собирает приложение, во второй (nginx) копируется только dist. 404 бывает, потому что сервер ищет файл /orders/42. Нужен fallback на index.html: try_files $uri $uri/ /index.html. Плюс правильные Cache-Control для index.html и хэшированных файлов.","origin":"authored"},{"id":"frontend-delivery/pre-commit","topic":"frontend-delivery","level":"middle","concept":"lint-format","q":"Зачем Husky и lint-staged и что не стоит класть в pre-commit?","a":"Husky ставит git-хуки, lint-staged запускает линтер и форматтер только на изменённых файлах — коммит за секунды. Долгие проверки (все тесты, сборка) в pre-commit не кладут: их обходят через --no-verify. Хук — быстрый фильтр, CI — окончательная проверка.","origin":"authored"},{"id":"frontend-delivery/rebase-vs-merge","topic":"frontend-delivery","level":"middle","concept":"git-history","q":"Когда делать rebase, а когда merge? Чем опасен rebase?","a":"rebase переносит твои коммиты поверх свежей main: история линейная, удобно для своей ветки до слияния. merge сохраняет историю как есть. rebase переписывает коммиты — опубликованную общую ветку так не трогают, иначе у коллег разъедется история. Для чистки своих коммитов — git rebase -i.","origin":"authored"},{"id":"frontend-delivery/bisect","topic":"frontend-delivery","level":"middle","concept":"git-history","q":"Баг появился где-то за последние 200 коммитов. Как быстро найти виноватый?","a":"git bisect: отметить хороший и плохой коммит, git делает двоичный поиск — около 8 шагов на 200 коммитов. С git bisect run <скрипт-тест> поиск идёт сам.","origin":"authored"},{"id":"frontend-delivery/revert","topic":"frontend-delivery","level":"middle","concept":"git-history","q":"Плохой коммит уже в main. Как откатить его безопасно?","a":"git revert <sha> создаёт новый коммит, который отменяет изменения, — история не переписывается, у коллег ничего не ломается. reset и force push в общую ветку не делают.","origin":"authored"},{"id":"frontend-delivery/renovate","topic":"frontend-delivery","level":"middle","concept":"dependency-updates","q":"Как держать зависимости свежими и не утонуть в PR от бота?","a":"Renovate или Dependabot с группировкой (например, все @angular/* одним PR), расписанием, automerge для patch и dev-зависимостей при зелёном CI. Major — вручную по плану. Отставание мерить libyear. Для Angular — ng update: он запускает миграции (schematics) кода.","origin":"authored"},{"id":"frontend-delivery/codemods","topic":"frontend-delivery","level":"middle","concept":"dependency-updates","q":"Что такое codemod и когда он нужен?","a":"Скрипт, который меняет код по AST: переименовать API, перейти на новый синтаксис в сотнях файлов. В Angular это ng generate @angular/core:<migration> (control-flow, inject, signal inputs) и миграции в ng update. Руками такие изменения долго делать и легко ошибиться.","origin":"authored"},{"id":"frontend-platform/feature-flags","topic":"frontend-platform","level":"senior","concept":"safe-releases","q":"Какие бывают фича-флаги и чем они опасны?","a":"Релизные (спрятать недоделанное), экспериментальные (A/B), операционные (выключатель тяжёлой функции), по правам. Опасность — накопление: старые флаги усложняют код и тесты. У флага должен быть владелец и срок удаления.","origin":"authored"},{"id":"frontend-platform/pipeline-security","topic":"frontend-platform","level":"senior","concept":"safe-releases","q":"Как защитить CI/CD-пайплайн фронтенда?","a":"Секреты — в хранилище CI с минимальными правами, не в репозитории и не в логах. Защищённые ветки и обязательные проверки, ревью для изменений пайплайна. Сторонние actions и образы — по фиксированной версии или хэшу. Отдельные права для деплоя в прод.","origin":"authored"},{"id":"frontend-platform/cdn-edge","topic":"frontend-platform","level":"lead","concept":"edge-delivery","q":"Как устроить доставку фронтенда через CDN, чтобы релиз и откат занимали минуты?","a":"Хэшированные файлы — в хранилище за CDN с immutable-кэшем навсегда, старые версии не удаляются сразу. index.html — с коротким кэшем или ревалидацией. Релиз = загрузить новые файлы, потом переключить index.html. Откат = вернуть прошлый index.html. На edge можно делать редиректы, A/B и заголовки безопасности.","origin":"authored"},{"id":"frontend-platform/release-process","topic":"frontend-platform","level":"lead","concept":"safe-releases","q":"Как устроить релизный процесс фронтенда для нескольких команд?","a":"Trunk-based разработка, короткие ветки, автодеплой из main. Незаконченное — за фича-флагами. Канареечный релиз на малую долю с автоматическим откатом по ошибкам и CWV. Защищённые ветки, обязательные проверки на PR, секреты только в хранилище CI. Метрики: частота деплоев, lead time, доля неудачных.","origin":"authored"},{"id":"frontend-platform/hybrid","topic":"frontend-platform","level":"lead","concept":"hybrid-apps","q":"Бизнес хочет мобильное приложение. Когда хватит Capacitor, а когда нужен React Native или нативная разработка?","a":"Capacitor заворачивает существующее веб-приложение в нативную оболочку с доступом к API устройства — быстро, если UI в вебе уже хороший. React Native рисует нативными компонентами — лучше ощущение платформы, но новая кодовая база. Нативно — сложная графика, фоновая работа, максимальная производительность. Иногда достаточно PWA.","origin":"authored"},{"id":"frontend-platform/shared-vs-product","topic":"frontend-platform","level":"principal","concept":"platform-thinking","q":"Несколько команд пишут свои таблицы. Выносить в общую платформу или нет?","a":"Проверить: нужна ли она трём и более командам, похожи ли требования, есть ли владелец и бюджет на поддержку. Выносить, когда это стабильно и есть кому вести. Иначе общий компонент станет тормозом для всех. Эффект мерить: время на фичу, число дублей, размер бандла.","origin":"authored"},{"id":"frontend-platform/blocker","topic":"frontend-platform","level":"principal","concept":"platform-thinking","q":"Медленная общая сборка тормозит пять команд. Как решить и доказать эффект?","a":"Измерить: время сборки и ожидания в CI, сколько раз в день, сколько людей ждут. Найти причину (нет кэша, всё собирается всегда, тяжёлая проверка). Решение: кэш задач, сборка только затронутого (affected), параллелизм. Показать cycle time до и после и закрепить алертом на регрессию.","origin":"authored"},{"id":"frontend-platform/custom-tooling","topic":"frontend-platform","level":"principal","concept":"custom-tooling","q":"Когда платформе нужен свой инструментарий и какой?","a":"Когда одно и то же делают руками во многих командах. Генераторы (Angular schematics, Nx generators) — новая фича по стандарту. Builders — свои шаги сборки. Свои ESLint-правила и codemod-миграции — массовые изменения. Каждому инструменту нужны тесты, версии, changelog и владелец.","origin":"authored"},{"id":"frontend-platform/platform-team-charter","topic":"frontend-platform","level":"lead","concept":"platform-thinking","q":"Тебе поручили собрать фронтенд-платформенную команду. С чего начнёшь и как поймёшь, что она полезна?","a":"Платформа — внутренний продукт, её клиенты — продуктовые команды. Начинаю с интервью и данных: где команды теряют время (сборка, обновления Angular, дублирующиеся компоненты), и беру 1–2 доказанные боли, а не «всё сразу». Всё делаем self-service — без тикетов и ожидания платформы. Обязательность не вводим: платформа должна выигрывать тем, что пользоваться ей проще, чем делать своё. Успех: доля команд на «золотом пути», время сборки и обновления, опрос удовлетворённости.","origin":"authored"},{"id":"frontend-platform/platform-backlog","topic":"frontend-platform","level":"lead","concept":"platform-thinking","q":"Десять продуктовых команд шлют платформе запросы. Как приоритизировать и не стать узким горлом?","a":"Цель платформы — снять сложность со stream-aligned команд, поэтому приоритет у того, что ускоряет больше всего команд, а не у самого громкого запроса. Держу «thinnest viable platform»: делаю минимум, который убирает боль, и не плодю новых зависимостей. Новую возможность сначала делаю в коротком режиме collaboration с одной командой, потом отдаю как X-as-a-Service. Разовые нужды одной команды — её бэклог, платформа помогает советом. Мерю время ожидания платформы и долю запросов, закрытых self-service.","origin":"authored"},{"id":"frontend-platform/monorepo-or-polyrepo","topic":"frontend-platform","level":"lead","concept":"monorepo-strategy","q":"Двенадцать фронтенд-приложений живут в двенадцати репозиториях. Будешь переводить их в монорепо?","a":"Зависит от того, сколько общего кода и как часто ломаются общие библиотеки. Монорепо даёт атомарные изменения (ломающее изменение и правка всех потребителей в одном PR) и общий код без публикации пакетов. Но без инструментов (кэш, affected, правила границ) оно превращается в «большую медленную репу». Выбираю монорепо, если общих библиотек много и команды готовы к общим правилам; иначе оставляю полирепо и публикую общее как версионированные пакеты. Переезд — по одному приложению, мерю время CI и число PR «обновить общую библиотеку».","origin":"authored"},{"id":"frontend-platform/single-version-policy","topic":"frontend-platform","level":"lead","concept":"monorepo-strategy","q":"В Nx-монорепо одна команда хочет остаться на старой версии библиотеки. Разрешишь?","a":"По умолчанию держу single version policy: версии задаются один раз в корне — нет конфликтов в рантайме и обновление делается один раз на всех. Цена — команды должны координировать обновления, и кому-то придётся подстроиться. Исключение разрешаю явно и временно: у проекта свой package.json для этой зависимости, срок и владелец. Для Angular и других фреймворков исключений не делаю: две версии в одном приложении ломаются в рантайме.","origin":"authored"},{"id":"frontend-platform/design-tokens","topic":"frontend-platform","level":"lead","concept":"design-system-governance","q":"Дизайн-система нужна в пяти приложениях и двух брендах. Как раздавать токены цветов, отступов и шрифтов?","a":"Один источник токенов в формате DTCG (первая стабильная версия спецификации — 2025.10), из него сборщик (например, Style Dictionary) генерирует CSS-переменные по брендам и темам. Приложения подключают пакет токенов по версии, а не копируют значения. Компоненты используют только семантические токены (--color-surface), а не сырые цвета — тогда новый бренд — это новый набор значений, а не правка кода. Проверка в CI: запрет сырых цветов линтером, контраст WCAG при сборке.","origin":"authored"},{"id":"frontend-platform/ds-governance","topic":"frontend-platform","level":"lead","concept":"design-system-governance","q":"Продуктовой команде не хватает компонента в дизайн-системе. Какой процесс ты выстроишь?","a":"Сначала команда приходит к команде дизайн-системы — часто решение уже есть. Если нет, вместе решаем: это «снежинка» для одного продукта (живёт в продукте) или общий компонент (уходит в бэклог дизайн-системы). Общий проходит прототип, ревью обеих команд, тесты (a11y, браузеры, крайние случаи), документацию и выходит новой версией по semver. Мерю время от запроса до релиза и долю «снежинок», которые потом стали общими.","origin":"authored"},{"id":"frontend-platform/ng-deep-overrides","topic":"frontend-platform","level":"lead","concept":"design-system-governance","q":"Команды массово перекрашивают компоненты дизайн-системы через ::ng-deep. Что будешь делать?","a":"::ng-deep снимает инкапсуляцию стилей, Angular сильно не советует его для нового кода и держит только для совместимости. Такие правки ломаются при каждом обновлении внутренней разметки компонента. Сначала собираю, что именно перекрашивают — это требования к API дизайн-системы. Потом даю официальные точки настройки (CSS-переменные, inputs вариантов) и запрещаю ::ng-deep линтером для новых файлов, а старые убираю по мере миграции. Мерю число ::ng-deep в коде по месяцам.","origin":"authored"},{"id":"frontend-platform/e2e-sharding","topic":"frontend-platform","level":"lead","concept":"frontend-ci","q":"E2E-тесты на Playwright идут в CI 50 минут и тормозят все команды. Как ускорить?","a":"Сначала снять профиль: какие тесты самые долгие и сколько их запускается без нужды. Затем шардинг: --shard=1/4 … 4/4 на разных машинах, fullyParallel: true, чтобы делить по тестам, а не по файлам, отчёты собрать через blob-репортер и merge-reports. Запускать только e2e затронутых приложений (affected), а полный прогон — по расписанию. Цена — больше машин в CI, поэтому мерю время до зелёного PR и стоимость минут.","origin":"authored"},{"id":"frontend-platform/flaky-policy","topic":"frontend-platform","level":"lead","concept":"frontend-ci","q":"Нестабильные (flaky) e2e-тесты приучили команды жать «перезапустить». Какую политику введёшь?","a":"Включаю retries в CI, но не для того, чтобы прятать проблему: Playwright помечает тест как flaky, если он упал и прошёл при повторе, — эти данные собираю в отчёт по тестам и командам. Тест выше порога нестабильности уходит в карантин (не блокирует merge) с владельцем и сроком починки; без починки — удаляется. Мерю долю flaky-тестов и число ручных перезапусков в неделю.","origin":"authored"},{"id":"frontend-platform/rum-setup","topic":"frontend-platform","level":"lead","concept":"rum-observability","q":"Как построить RUM-мониторинг производительности для всех фронтенд-приложений компании?","a":"Общий пакет на базе web-vitals: onLCP, onINP, onCLS, метрики копятся в очередь и отправляются через navigator.sendBeacon при visibilitychange → hidden. К каждой метрике добавляем приложение, маршрут, версию релиза и тип устройства, чтобы видеть регрессию после конкретного деплоя. Смотрим 75-й перцентиль, а не среднее. Attribution-сборка тяжелее, поэтому её включаю для части трафика. Успех — у каждой команды свой дашборд и алерт на ухудшение p75.","origin":"authored"},{"id":"frontend-platform/trace-context","topic":"frontend-platform","level":"lead","concept":"rum-observability","q":"Пользователь жалуется на медленную кнопку, а бэкенд говорит «у нас всё быстро». Как связать трассы фронтенда и бэкенда?","a":"Передавать стандартный заголовок W3C traceparent (версия-trace-id-parent-id-флаги) из браузера в API, чтобы запрос фронтенда и спаны бэкенда попали в один trace. Для этого есть OpenTelemetry для браузера, но его клиентская инструментация пока экспериментальная, поэтому начинаю с малого: document load и XHR/fetch для ключевых сценариев. Для чужих доменов заголовок должен быть разрешён в CORS. Успех — время разбора таких жалоб и доля запросов с полным trace.","origin":"authored"},{"id":"frontend-platform/budgets-across-teams","topic":"frontend-platform","level":"lead","concept":"performance-budgets","q":"Шесть команд коммитят в одно приложение, и бандл растёт. Как ввести бюджеты производительности, чтобы они работали?","a":"Бюджеты делю по владельцам: initial-бандл — общий, лениво загружаемые чанки — по фичам команд (в angular.json тип bundle с name), плюс полевые цели по LCP и INP на p75. Превышение error-уровня валит сборку в CI, warning — комментарий в PR. Когда бюджет превышен, у команды три выхода: оптимизировать, убрать что-то старое или не добавлять новое; поднять бюджет можно только явным решением с причиной. Мерю тренд размера и число нарушений по командам.","origin":"authored"},{"id":"frontend-platform/fleet-upgrade","topic":"frontend-platform","level":"lead","concept":"ng-version-upgrades","q":"У компании 30 приложений на Angular 19–20. Как перевести всех на v21?","a":"ng update поддерживает переход только на одну мажорную версию за раз, поэтому v19 идут через v20. Сначала пилот на одном типичном приложении: собираю список ручных шагов и пишу для них codemod или шаблон PR. Дальше волнами: самые простые приложения, потом сложные; общие библиотеки обновляются первыми. Прогресс — дашборд «версия Angular по приложениям» и срок: v20 в LTS до 2026-11-28, после этого патчей безопасности нет.","origin":"authored"},{"id":"frontend-platform/version-policy","topic":"frontend-platform","level":"lead","concept":"ng-version-upgrades","q":"Какую политику версий Angular ты задашь для всех приложений компании?","a":"Angular выпускает мажор каждые 6 месяцев, каждую версию поддерживают 18 месяцев: 6 активно и 12 в LTS (только критичные исправления и безопасность). Политика: все приложения на поддерживаемой версии, целевая — текущая или предыдущая; обновление планируется в квартал после релиза, а не «когда будет время». Устаревшие API удаляют не раньше чем через две мажорные версии, поэтому предупреждения о deprecation чиним сразу. Мерю долю приложений на поддерживаемой версии и отставание в мажорах.","origin":"authored"},{"id":"frontend-platform/mfe-governance","topic":"frontend-platform","level":"lead","concept":"micro-frontends","q":"В компании пять микрофронтендов, и каждый тянет свою копию фреймворка. Какие правила введёшь?","a":"Без общих правил пользователь качает фреймворк N раз. Общие зависимости выносим из бандлов и фиксируем версии — но это снова связывает команды и требует согласованных обновлений, такова цена. Микрофронтенды общаются минимально: через URL, кастомные события и явные контракты, общего состояния нет. Контракты и маршруты проверяем автотестами, интеграцию — в окружении, похожем на прод. Мерю суммарный JS на странице и число инцидентов на стыках.","origin":"authored"},{"id":"frontend-platform/mfe-shared-library","topic":"frontend-platform","level":"lead","concept":"micro-frontends","q":"Микрофронтенды выглядят по-разному, а общую библиотеку компонентов «никто не ведёт». Как это исправить?","a":"Если библиотеку «ведут все», на деле не ведёт никто. Модель: вносить изменения может любой, но есть хранитель (человек или команда), который отвечает за качество и релизы. В библиотеке только UI-логика, без бизнес-логики, а новые компоненты забираем из уже работающих решений команд, а не придумываем заранее. Стили пишем так, чтобы они не конфликтовали при сборке на одной странице. Успех — визуальная консистентность (ревью дизайна) и доля экранов на общих компонентах.","origin":"authored"},{"id":"frontend-platform/csp-rollout","topic":"frontend-platform","level":"lead","concept":"csp-trusted-types","q":"Как ввести Content Security Policy во всех фронтенд-приложениях компании и ничего не сломать?","a":"Цель — strict CSP на nonce или hash с 'strict-dynamic', а не список доменов: такие списки обычно обходятся. В Angular для этого есть опция сборки autoCsp или атрибут ngCspNonce, если сервер умеет подставлять nonce в index.html. Сначала включаю Content-Security-Policy-Report-Only (только через HTTP-заголовок) и собираю отчёты, чиню нарушения, потом перевожу в блокирующий режим по одному приложению. Мерю число нарушений в отчётах и долю приложений в режиме enforce.","origin":"authored"},{"id":"frontend-platform/trusted-types","topic":"frontend-platform","level":"lead","concept":"csp-trusted-types","q":"Зачем включать Trusted Types, если Angular и так экранирует шаблоны?","a":"Angular защищает свои привязки, но DOM XSS приходит из прямых вызовов: innerHTML в своём коде, сторонние библиотеки, bypassSecurityTrust. Trusted Types (require-trusted-types-for 'script') запрещают передавать строку в такие места — браузер бросит TypeError. Angular поддерживает их через политику angular, а bypass-методы требуют отдельной политики angular#unsafe-bypass — так её использование видно в заголовке. Внедряю через report-only, затем enforce; не во всех браузерах есть поддержка, там остаётся защита DomSanitizer.","origin":"authored"},{"id":"frontend-platform/shared-eslint-config","topic":"frontend-platform","level":"lead","concept":"custom-tooling","q":"У каждой команды свой ESLint-конфиг, и правила расходятся. Как навести порядок?","a":"Общий shareable config как npm-пакет (@company/eslint-config) в flat-формате; eslint указан в peerDependencies, плагины — в dependencies. Команды подключают его и могут переопределить правила у себя — но переопределения видны в коде и обсуждаются. Новые строгие правила включаю сначала как warn, через минорную версию — как error, с changelog. Мерю число локальных переопределений и долю репозиториев на последней версии конфига.","origin":"authored"},{"id":"frontend-platform/third-party-scripts","topic":"frontend-platform","level":"lead","concept":"csp-trusted-types","q":"Маркетинг хочет подключить пять сторонних скриптов с CDN. Какие правила безопасности поставишь?","a":"Сторонний скрипт выполняется с правами нашей страницы, поэтому каждый — с владельцем и причиной. Где файл версионирован, ставлю Subresource Integrity: integrity=\"sha384-…\" и crossorigin — при подмене файла браузер его не выполнит. Скрипты, которые меняются без версии (теги аналитики), SRI закрыть не может — их ограничиваю CSP и, где можно, изолирую в iframe. Раз в квартал пересматриваю список: неиспользуемые удаляю.","origin":"authored"},{"id":"frontend-platform/ui-sdk-breaking","topic":"frontend-platform","level":"principal","concept":"library-semver","q":"Каталог компонентов выпускается как npm-пакет для 20 команд. Что для него считается ломающим изменением?","a":"По semver MAJOR — любое несовместимое изменение публичного API, а значит, публичный API надо сначала явно описать. Для UI-библиотеки это не только экспорты TS: имена inputs/outputs, селекторы, публичные CSS-переменные и токены, поведение клавиатуры и фокуса, повышение минимальной версии Angular в peerDependencies. Внутренние классы и разметку объявляю непубличными — тогда их можно менять в минорных версиях. Проверяю в CI: снимок публичного API и визуальные тесты.","origin":"authored"},{"id":"frontend-platform/public-api","topic":"frontend-platform","level":"principal","concept":"ng-library-api","q":"Как в Angular-библиотеке отделить публичный API от внутреннего, чтобы команды не завязывались на внутренности?","a":"Всё, что экспортируется из public-api.ts, — публичное, и командам нужен один путь импорта. Внутренние модули не экспортирую, а большие библиотеки делю на подпути импорта (secondary entry points), каждый со своим публичным API. Сборка через ng-packagr в режиме partial compilation, чтобы пакет работал с разными версиями Angular. Глубокие импорты в обход public-api запрещаю правилом линтера у потребителей.","origin":"authored"},{"id":"frontend-platform/peer-deps-window","topic":"frontend-platform","level":"principal","concept":"ng-library-api","q":"Команды сидят на разных версиях Angular. Какие версии должна поддерживать общая библиотека?","a":"@angular/* — только в peerDependencies, чтобы у приложения был один экземпляр фреймворка. Библиотеку собираю в partial-Ivy: её можно использовать в приложении на той же или более новой версии Angular, чем та, с которой собрана. Поэтому собираю на самой старой поддерживаемой версии из окна (например, текущая и предыдущая мажорные). Шире окно — больше тестовая матрица и медленнее внедрение новых API; это решение фиксирую в политике и пересматриваю при каждом мажоре.","origin":"authored"},{"id":"frontend-platform/deprecation-policy","topic":"frontend-platform","level":"principal","concept":"library-semver","q":"Как убирать устаревшие компоненты из общей библиотеки, не ломая команды?","a":"Беру за образец Angular: устаревшее объявляется в changelog вместе с путём замены, живёт минимум две мажорные версии и удаляется только в мажоре. По semver пометка deprecated выходит в минорной версии. В коде — @deprecated в JSDoc (IDE зачеркнёт) и правило линтера, для механических замен — миграция для ng update. Перед удалением смотрю по данным использования, кто ещё не перешёл, и помогаю им адресно.","origin":"authored"},{"id":"frontend-platform/changelog-automation","topic":"frontend-platform","level":"principal","concept":"library-semver","q":"Как выпускать версии и changelog библиотеки без ручной рутины и споров?","a":"Conventional Commits: fix → PATCH, feat → MINOR, BREAKING CHANGE или «!» → MAJOR; по ним инструмент сам считает версию и собирает черновик changelog. Но changelog пишется для людей, а не как дамп git log: группы Added, Changed, Deprecated, Removed, Fixed, Security и раздел Unreleased. Поэтому автогенерацию дополняю ручной правкой для ломающих изменений с примерами миграции. Мерю, сколько вопросов «что сломалось» приходит после релиза.","origin":"authored"},{"id":"frontend-platform/ng-update-migrations","topic":"frontend-platform","level":"principal","concept":"ng-library-api","q":"Как сделать, чтобы командам обновление вашей UI-библиотеки делалось одной командой ng update?","a":"В package.json библиотеки ключ ng-update: migrations указывает на коллекцию schematics, packageGroup — пакеты, которые обновляются вместе. У каждой миграции поле version; ng update запускает все миграции между установленной и целевой версией по порядку. Миграции пишу для механических изменений (переименование input, импорта), а сложное оставляю в гайде. Каждую миграцию тестирую на фикстурах до и после и делаю идемпотентной.","code":"{\n  \"name\": \"@company/ui\",\n  \"ng-update\": {\n    \"migrations\": \"./schematics/migration.json\",\n    \"packageGroup\": [\"@company/ui\", \"@company/ui-icons\"]\n  }\n}","origin":"authored"},{"id":"frontend-platform/migration-rollout","topic":"frontend-platform","level":"principal","concept":"ng-version-upgrades","q":"Миграция библиотеки сломала часть приложений. Как сделать массовые миграции безопасными?","a":"Перед релизом гоняю миграцию на копиях реальных приложений-потребителей, а не только на фикстурах. Командам даю повторяемый путь: ng update --migrate-only с --from/--to перезапускает миграции без смены версии, а --create-commits кладёт каждый шаг в отдельный коммит — проще ревью и откат. Изменения, которые нельзя автоматизировать надёжно, миграция только помечает комментарием TODO и ссылкой на гайд. Мерю долю приложений, обновившихся без ручных правок.","origin":"authored"},{"id":"frontend-platform/prerelease-channel","topic":"frontend-platform","level":"principal","concept":"library-semver","q":"Как ловить поломки общей библиотеки до того, как мажор попадёт ко всем командам?","a":"Как Angular: регулярные пре-релизы (next, rc) перед мажором и предсказуемый график релизов. Две-три команды-пилота ставят next в отдельной ветке CI и сообщают о поломках; их приложения — обязательная часть тестовой матрицы. Мажор выходит по графику, а не «когда готово», чтобы команды могли планировать. Цена — поддержка нескольких линий версий, поэтому окно поддержки ограничиваю. Мерю число регрессий, найденных до и после релиза.","origin":"authored"},{"id":"frontend-platform/rfc-process","topic":"frontend-platform","level":"principal","concept":"contribution-model","q":"Как принимать крупные изменения в общий фронтенд-стек, чтобы решали не «кто громче»?","a":"Процесс RFC: для существенных изменений — новый публичный API, удаление готовой функции, новые соглашения — автор пишет документ по шаблону (мотивация, дизайн, недостатки, альтернативы) и открывает PR. Обсуждение открытое, затем короткий final comment period и решение с объяснением, в том числе при отказе. Мелочи (рефакторинг, исправления) идут обычным PR, иначе процесс станет тормозом. Мерю время от RFC до решения и долю RFC от команд вне платформы.","origin":"authored"},{"id":"frontend-platform/codeowners","topic":"frontend-platform","level":"principal","concept":"contribution-model","q":"Как закрепить владение кодом в общем монорепо и не сделать платформу узким горлом ревью?","a":"Файл CODEOWNERS: для путей указываются владельцы, побеждает последнее совпавшее правило, владельцы автоматически зовутся на ревью, а защита ветки может требовать их одобрения. Владельцами ставлю команды, а не людей, иначе отпуск блокирует merge. Обязательное ревью платформы — только для публичного API и конфигов сборки, остальное — у команд. Мерю время ожидания ревью владельцев; если растёт — добавляю доверенных ревьюеров из других команд.","origin":"authored"},{"id":"frontend-platform/innersource","topic":"frontend-platform","level":"principal","concept":"contribution-model","q":"Продуктовой команде срочно нужна фича в общей библиотеке, а у платформы нет времени. Что делать?","a":"InnerSource: применяем практики open source внутри компании — команда-гость сама пишет изменение, а команда-владелец ревьюит и принимает его, оставляя поддержку за собой. Для этого нужны CONTRIBUTING-гайд, понятная локальная сборка и быстрые ответы на PR. Риск — владельцу потом поддерживать чужой код, поэтому крупное согласуем заранее через issue или RFC. Мерю долю PR от внешних команд и время их принятия.","origin":"authored"},{"id":"frontend-platform/make-vs-buy-ui","topic":"frontend-platform","level":"principal","concept":"build-vs-buy","q":"Строить свою библиотеку UI-компонентов или взять готовую?","a":"Зависит от того, насколько интерфейс — часть ценности продукта. Нет строгих требований бренда — беру готовые стилизованные компоненты (Angular Material): быстро и без своей поддержки. Нужен свой визуальный язык — строю на headless-основе: Angular Aria даёт доступное поведение (клавиатура, ARIA, фокус) без стилей, CDK — примитивы взаимодействия. Полностью своё с нуля — только при уникальных требованиях и наличии команды-владельца. Решение фиксирую в ADR с ценой поддержки на 3 года.","origin":"authored"},{"id":"frontend-platform/aria-maturity","topic":"frontend-platform","level":"principal","concept":"build-vs-buy","q":"Ты хочешь построить дизайн-систему на Angular Aria, а компания живёт на Angular 21. Какие риски учтёшь?","a":"В v21 Angular Aria вышла как developer preview — API может меняться без обычного цикла deprecation; стабильной её объявили в v22. Значит, на v21 изолирую Aria за своими компонентами-обёртками, чтобы изменения API правились в одном месте, и планирую переход на v22. Если обновиться быстро нельзя — беру стабильные примитивы CDK для критичных компонентов. Риск фиксирую в ADR и пересматриваю при каждом мажоре.","origin":"authored"},{"id":"frontend-platform/frontend-threat-model","topic":"frontend-platform","level":"principal","concept":"threat-modeling","q":"Как провести моделирование угроз для SPA, а не только для бэкенда?","a":"Четыре вопроса: что строим, что может пойти не так, что делаем, хорошо ли сделали. Рисую поток данных с границами доверия: браузер, CDN, BFF/API, сторонние скрипты, сборочный пайплайн, postMessage и URL как входы. По каждой стрелке прохожу STRIDE: подмена (украденная сессия), изменение (подмена бандла на CDN), раскрытие (токены и PII в localStorage и логах), повышение прав (проверки только на клиенте). Делаю это на этапе дизайна, а результат — список мер с владельцами.","origin":"authored"},{"id":"frontend-platform/token-storage-standard","topic":"frontend-platform","level":"principal","concept":"backend-for-frontend","q":"Какой стандарт хранения OAuth-токенов ты задашь для всех SPA компании?","a":"IETF-рекомендация для браузерных приложений ставит варианты по убыванию безопасности: BFF (токены на сервере, браузер держит только HttpOnly + Secure cookie сессии), затем token-mediating backend, затем чистый браузерный клиент. Для бизнес-приложений и персональных данных стандарт — BFF: при XSS красть просто нечего. Цена — свой серверный компонент и защита от CSRF. Исключения (чисто браузерный клиент с PKCE) — только письменно, с оценкой риска.","origin":"authored"},{"id":"frontend-platform/supply-chain-policy","topic":"frontend-platform","level":"principal","concept":"dependency-trust","q":"Как управлять рисками npm-зависимостей на уровне всей компании, а не одного репозитория?","a":"OWASP Top 10 2025 выделяет это отдельным риском (A03: Software Supply Chain Failures). Централизованно: SBOM для каждого приложения, учёт и транзитивных зависимостей, процесс патчей с понятными сроками по серьёзности, а не раз в квартал. Убираем неиспользуемые зависимости, ставим пакеты только из доверенных реестров, усиливаем CI/CD (минимальные права, секреты по окружениям). Мерю время от публикации уязвимости до исправления во всех приложениях.","origin":"authored"},{"id":"frontend-platform/frontend-strategy","topic":"frontend-platform","level":"principal","concept":"engineering-strategy","q":"Как бы ты написал фронтенд-стратегию для компании?","a":"Не с чистого листа: беру 5 недавних дизайн-документов и ищу решения, о которых спорили снова и снова (стейт-менеджмент, SSR, микрофронтенды) — это и есть материал стратегии. Стратегия = диагноз + правила выбора с объяснением, конкретные, а не «пишем качественный код». Из нескольких стратегий потом получается видение на 2 года вперёд. Успех — споры не повторяются, и новые дизайны ссылаются на стратегию.","origin":"authored"},{"id":"frontend-platform/tech-radar","topic":"frontend-platform","level":"principal","concept":"engineering-strategy","q":"Команды тянут в стек всё новое подряд. Как управлять выбором технологий, не запрещая всё?","a":"Технологический радар компании: кольца Adopt, Trial, Assess, Hold. Adopt — путь по умолчанию с поддержкой платформы; Trial — можно в одном проекте с владельцем и отчётом; Hold — новые проекты не начинаем. Радар пересматривают вместе с командами, у каждого пункта — короткое объяснение. Цена — время на ревизию раз в полгода. Мерю долю новых проектов на Adopt и число «неучтённых» технологий в коде.","origin":"authored"},{"id":"frontend-platform/measure-adoption","topic":"frontend-platform","level":"principal","concept":"adoption-metrics","q":"Как измерить, насколько команды реально используют дизайн-систему?","a":"Автоматически, а не опросом: регулярная задача клонирует репозитории и разбирает код, собирая число проектов на дизайн-системе, какие компоненты и как часто используются, какие версии пакета стоят и долю новых компонентов против старой библиотеки. Это показывает, где застряли миграции и какие компоненты никому не нужны. Опросы удовлетворённости добавляю как второй сигнал — цифры не объясняют «почему». Метрики без действия — пустые, поэтому к каждой привязываю решение.","origin":"authored"},{"id":"frontend-platform/platform-success","topic":"frontend-platform","level":"principal","concept":"adoption-metrics","q":"Руководство спрашивает, окупается ли фронтенд-платформа. Чем докажешь?","a":"Метрики доставки DORA до и после внедрения: change lead time, частота деплоев, change fail rate, время восстановления после неудачного деплоя и rework rate. Рядом — метрики платформы: время сборки и CI, срок обновления Angular по всем приложениям, доля команд на «золотом пути». Плюс опрос разработчиков: скорость без удовлетворённости не держится. Честно показываю и то, что не улучшилось — иначе цифрам не поверят.","origin":"authored"},{"id":"html-basics/page-skeleton","topic":"html-basics","level":"intern","concept":"html-semantics","q":"Из чего состоит минимальный HTML-документ и зачем каждая часть?","a":"<!doctype html> включает стандартный режим. В <html lang> — язык для скринридеров и переводчика. В <head>: <meta charset=\"utf-8\">, meta viewport, <title>. В <body> — видимое содержимое. Браузер парсит документ сверху вниз и строит DOM.","origin":"authored"},{"id":"html-basics/script-blocking","topic":"html-basics","level":"intern","concept":"html-semantics","q":"Почему обычный <script> в <head> замедляет показ страницы и чем помогают defer и async?","a":"Парсер HTML останавливается, пока скрипт не скачан и не выполнен. defer качает параллельно и выполняет после разбора документа, по порядку. async выполняет сразу после загрузки, порядок не гарантирован. Для своих скриптов обычно defer или type=\"module\" (он отложен по умолчанию).","origin":"authored"},{"id":"html-basics/div-vs-button","topic":"html-basics","level":"intern","concept":"html-semantics","q":"Чем <button> лучше, чем <div> с обработчиком click?","a":"<button> сам получает фокус с клавиатуры, нажимается Enter и Space, объявляет роль «кнопка» скринридеру и умеет disabled. <div> всё это надо дописывать руками (tabindex, role, обработка клавиш) — и обычно забывают.","origin":"authored"},{"id":"html-basics/landmarks","topic":"html-basics","level":"junior","concept":"html-semantics","q":"Зачем нужны header, nav, main, article, aside, footer, если div выглядит так же?","a":"Это ориентиры (landmarks): скринридер даёт по ним прыгать, поисковик понимает структуру, разработчик быстрее читает код. main на странице один. Заголовки h1–h6 идут без пропусков уровней.","origin":"authored"},{"id":"html-basics/label-for","topic":"html-basics","level":"intern","concept":"html-forms","q":"Зачем полю формы <label> и как связать его с <input>?","a":"Явно: for у label равен id у input. Неявно: input лежит внутри label, MDN советует явный способ — его понимают все вспомогательные технологии. Скринридер читает подпись, когда поле в фокусе. Клик по подписи ставит фокус в поле, и попасть в маленький чекбокс проще.","code":"<label for=\"email\">Email</label>\n<input id=\"email\" name=\"email\" type=\"email\">","origin":"authored"},{"id":"html-basics/form-get-post","topic":"html-basics","level":"intern","concept":"html-forms","q":"Что происходит при отправке <form> и чем method=\"get\" отличается от method=\"post\"?","a":"Браузер собирает пары name=value полей и шлёт их на адрес из action. GET дописывает данные в URL после «?» — удобно для поиска и фильтров, ссылку можно сохранить. POST кладёт данные в тело запроса — для паролей, больших данных и действий, которые что-то меняют. Поле без name не отправляется.","origin":"authored"},{"id":"html-basics/form-validation","topic":"html-basics","level":"intern","concept":"html-forms","q":"Какую проверку формы браузер даёт без JavaScript и почему её мало?","a":"Атрибуты required, type=\"email\", pattern, minlength/maxlength, min/max: браузер не отправит форму и покажет подсказку, а поле получит :invalid для стилей. Это удобство для пользователя, а не защита: запрос легко подделать в обход формы. Поэтому сервер всегда проверяет данные сам.","origin":"authored"},{"id":"html-basics/img-alt","topic":"html-basics","level":"intern","concept":"html-semantics","q":"Что дают атрибуты alt, width и height у <img>?","a":"alt — текстовая замена: его читает скринридер и показывает браузер, если картинка не загрузилась. Для декоративной картинки пишут alt=\"\", чтобы её пропустили. width и height (числа без единиц) дают браузеру пропорции заранее, поэтому страница не прыгает, когда картинка догрузится.","origin":"authored"},{"id":"html-basics/data-attributes","topic":"html-basics","level":"intern","concept":"html-semantics","q":"Для чего нужны атрибуты data-*?","a":"Чтобы хранить свои данные прямо на элементе: data-index-number=\"12\". В JS они доступны через element.dataset.indexNumber (дефисы превращаются в camelCase), значения — всегда строки. Их можно выбирать в CSS: [data-state=\"open\"]. Видимый текст туда не кладут: скринридеры и поисковики его могут не увидеть.","origin":"authored"},{"id":"i18n-time/i18n-angular","topic":"i18n-time","level":"middle","concept":"i18n-l10n","q":"Как устроена встроенная i18n в Angular и какая у неё особенность?","a":"Тексты помечают атрибутом i18n и $localize в коде, извлекают через ng extract-i18n, переводят файлы (XLIFF и др.). По умолчанию собирается отдельная сборка на каждую локаль — переводы подставляются при компиляции, переключение языка = переход на другую сборку. Для смены языка без перезагрузки берут runtime-библиотеки.","origin":"authored"},{"id":"i18n-time/plurals-format","topic":"i18n-time","level":"middle","concept":"i18n-l10n","q":"Почему нельзя склеивать строку «Найдено » + n + « товаров»?","a":"В разных языках разный порядок слов и разные формы множественного числа (в русском: 1 товар, 2 товара, 5 товаров). Используют ICU-сообщения (plural, select) и Intl: Intl.PluralRules, Intl.NumberFormat для валют, Intl.DateTimeFormat для дат.","origin":"authored"},{"id":"i18n-time/timezones","topic":"i18n-time","level":"middle","concept":"i18n-l10n","q":"Как правильно хранить и показывать время встречи для пользователей из разных часовых поясов?","a":"Хранить момент в UTC (ISO-строка с Z) и, если важно, исходную зону (Europe/Berlin). Показывать в зоне пользователя: Intl.DateTimeFormat с timeZone. Не считать время «плюс 3 часа» руками — летнее время сломает. Moment.js в режиме поддержки; замены: date-fns, Luxon, Day.js, в будущем — Temporal.","origin":"authored"},{"id":"i18n-time/locale-detect","topic":"i18n-time","level":"middle","concept":"i18n-l10n","q":"Как определить язык и формат для пользователя?","a":"На клиенте — navigator.language и navigator.languages, на сервере — заголовок Accept-Language. Это только подсказка: явный выбор пользователя важнее, его сохраняют. Язык интерфейса и формат дат/чисел могут отличаться.","origin":"authored"},{"id":"incidents-oncall/on-call","topic":"incidents-oncall","level":"senior","concept":"oncall-postmortem","q":"Пришёл алерт: ошибки JS выросли в 10 раз после релиза. Твои действия на дежурстве?","a":"Подтвердить алерт и сообщить в канал инцидента. Оценить масштаб (доля пользователей, ключевые сценарии). Если связано с релизом — откатить или выключить флаг: сначала восстановить. Эскалировать, если нужна помощь. Вести хронологию. После — постмортем.","origin":"authored"},{"id":"incidents-oncall/postmortem","topic":"incidents-oncall","level":"senior","concept":"oncall-postmortem","q":"Что должно быть в постмортеме?","a":"Влияние (кто, сколько, как долго), хронология, как обнаружили, первопричина и способствующие факторы, что сработало и что нет, действия с владельцами и сроками. Без поиска виноватых: вопрос «почему система позволила», а не «кто ошибся».","origin":"authored"},{"id":"incidents-oncall/postmortem-culture","topic":"incidents-oncall","level":"lead","concept":"oncall-postmortem","q":"Как построить культуру постмортемов в направлении?","a":"Чёткий порог: какой инцидент требует постмортем. Шаблон и разбор на общей встрече. Без наказаний — иначе инциденты начнут скрывать. Действия из постмортемов в бэклоге с приоритетом и отслеживанием до закрытия. Периодически искать общие причины по всем постмортемам.","origin":"authored"},{"id":"incidents-oncall/incident-commander","topic":"incidents-oncall","level":"lead","concept":"incident-command","q":"Ты Incident Commander на крупном инциденте. Что ты делаешь и чего не делаешь?","a":"Координирую, а не чиню сам: назначаю роли (кто работает с системой, кто ведёт коммуникацию, кто пишет хронологию), собираю информацию от экспертов и принимаю решения — откат, флаг, эскалация. Слежу, чтобы систему меняли только назначенные люди. Сам в отладку не ухожу, иначе никто не видит картину целиком. Если нужно — передаю роль явно, вслух.","origin":"authored"},{"id":"incidents-oncall/severity-levels","topic":"incidents-oncall","level":"lead","concept":"incident-command","q":"Зачем направлению единые уровни серьёзности инцидентов и как их ввести?","a":"Уровень определяет реакцию: кого будить, кто координирует, как часто обновлять статус, нужен ли постмортем. Описываю уровни через влияние на пользователей и бизнес (доля пользователей, ключевой сценарий, данные), а не через «насколько страшно». Правило: сомневаешься — ставь более высокий уровень, понизить можно потом. Проверяю на разборах, верно ли уровни выставлялись.","origin":"authored"},{"id":"incidents-oncall/incident-updates","topic":"incidents-oncall","level":"lead","concept":"incident-command","q":"Во время инцидента руководство и поддержка каждые пять минут спрашивают в чате «что происходит?». Как это решить?","a":"Выделить роль коммуникации: один человек регулярно отправляет обновления стейкхолдерам — что сломано, кого касается, что делаем, когда следующее обновление. Тогда инженеров не отвлекают. Обновления по расписанию (например, каждые 30 минут), даже если новостей нет. Внешним клиентам — через статус-страницу, по заранее готовым шаблонам.","origin":"authored"},{"id":"incidents-oncall/error-budget-spent","topic":"incidents-oncall","level":"lead","concept":"slo-error-budget","q":"Error budget сервиса исчерпан, а продукт хочет выпускать новые фичи. Что делать?","a":"Действовать по заранее согласованной error budget policy: при исчерпанном бюджете фичи ждут, кроме срочных исправлений и безопасности, а команда занимается надёжностью. Политику подписывают и продукт, и разработка заранее — тогда это не спор, а правило. Это не наказание, а баланс скорости и надёжности. Если сервис стабильно в SLO — бюджет можно тратить на смелые релизы.","origin":"authored"},{"id":"incidents-oncall/oncall-load","topic":"incidents-oncall","level":"lead","concept":"oncall-postmortem","q":"Дежурные жалуются: десятки пейджей за смену, люди выгорают. Что ты сделаешь?","a":"Сначала измерить: сколько пейджей, сколько из них требуют действий, сколько ночью. Ориентир Google SRE — не больше двух инцидентов за 12-часовую смену, иначе нет времени на разбор и исправление. Шумные алерты убрать или перевести на SLO, повторяющиеся причины чинить в первую очередь, ротацию расширить. Успех — меньше пейджей на смену и все они требуют действий.","origin":"authored"},{"id":"incidents-oncall/reliability-metrics","topic":"incidents-oncall","level":"principal","concept":"incident-command","q":"Руководство хочет отчёт: «MTTR снизился — значит надёжность выросла». Что не так?","a":"Средние вроде MTTR по инцидентам плохо подходят для выводов: инцидентов мало и они очень разные, поэтому среднее сильно прыгает само по себе. Честнее смотреть на выполнение SLO, повторяющиеся инциденты и закрытие действий из постмортемов. Для отчёта выбираю несколько метрик вместе и показываю тренд с пояснениями, а не одно число. Метрику не делаю целью для команд.","origin":"authored"},{"id":"incidents-oncall/blame-pressure","topic":"incidents-oncall","level":"principal","concept":"oncall-postmortem","q":"После тяжёлого инцидента руководство требует найти и наказать виноватого инженера. Как ты поступишь?","a":"Говорю «нет» наказанию и объясняю цену: если за ошибки увольняют, инциденты начнут скрывать, и мы потеряем информацию. Перевожу разговор на систему: почему один человек смог это сломать, где не было защиты (алерты, ревью, ограничения). Даю руководству понятный план исправлений с владельцами и сроками. Это решение лидера — защитить людей и при этом не скрывать проблем.","origin":"authored"},{"id":"incidents-oncall/who-owns-oncall","topic":"incidents-oncall","level":"principal","concept":"oncall-postmortem","q":"Кто должен дежурить по сервисам: разработчики или отдельная ops/SRE-команда?","a":"Зависит от зрелости и критичности: по умолчанию сервис сопровождает команда, которая его пишет, — так у неё есть стимул делать его надёжным. Отдельная SRE-команда имеет смысл для самых критичных систем, но с условием: если операционной работы больше лимита (в Google — 50%), лишнее возвращают разработчикам, вплоть до дежурств. Меряю нагрузку и качество: пейджи, toil, выполнение SLO.","origin":"authored"},{"id":"incidents-oncall/public-incident","topic":"incidents-oncall","level":"principal","concept":"incident-command","q":"Крупный инцидент задел ключевых клиентов, CEO просит лично объяснить ситуацию. Как ты построишь коммуникацию?","a":"Заранее готовый план: каналы (статус-страница, письма, чат), аудитории и шаблоны. CEO — коротко: влияние на клиентов и деньги, что уже сделано, следующий шаг и время следующего обновления; без технических деталей и без обещаний, в которых не уверен. Клиентам — честно и регулярно. После — публичный разбор и действия, чтобы восстановить доверие.","origin":"authored"},{"id":"js-data/map-vs-foreach","topic":"js-data","level":"intern","concept":"array-methods","q":"Чем forEach отличается от map и когда что выбирать?","a":"map возвращает новый массив из результатов колбэка — для преобразования данных, его можно продолжить цепочкой. forEach всегда возвращает undefined — только для побочных эффектов. forEach нельзя прервать (только исключением) и он не ждёт промисы из async-колбэка. Нужен ранний выход — for...of, some, find.","origin":"authored"},{"id":"js-data/shallow-vs-deep-copy","topic":"js-data","level":"intern","concept":"copying","q":"Чем поверхностная копия отличается от глубокой? Как сделать каждую?","a":"Поверхностная копия копирует только верхний уровень, вложенные объекты остаются общими: поменял вложенное в копии — поменялось и в оригинале. Так работают spread, Object.assign, slice, Array.from. Глубокая копия делается через structuredClone — она копирует вложенные данные и даже циклические ссылки, но не функции.","origin":"authored"},{"id":"js-data/iterate-object","topic":"js-data","level":"intern","concept":"object-collections","q":"Как перебрать свойства объекта и элементы массива? Почему for...in не для массивов?","a":"Для объекта — Object.keys, Object.values или Object.entries: они дают только собственные свойства. for...in проходит ещё и унаследованные перечисляемые свойства и даёт индексы массива строками, поэтому для массивов он плох. Массив перебирают через for...of, forEach или обычный for.","code":"for (const [key, value] of Object.entries(user)) {\n  console.log(key, value);\n}","origin":"authored"},{"id":"js-data/map-vs-object","topic":"js-data","level":"intern","concept":"object-collections","q":"Когда брать Map, а когда обычный объект?","a":"Map — когда ключи любого типа (объекты, числа), нужен порядок вставки, size и частые добавления и удаления. В Map нет унаследованных ключей, поэтому он безопасен для ключей от пользователя. Объект проще для фиксированного набора полей и сразу сериализуется в JSON, а Map — нет.","origin":"authored"},{"id":"js-types/var-let-const","topic":"js-types","level":"intern","concept":"variables","q":"Чем отличаются var, let и const?","a":"var видна во всей функции, let и const — только в своём блоке { }. Обращение к let/const до объявления даёт ReferenceError (временная мёртвая зона), а var там просто undefined. let и const нельзя объявить дважды в одной области, var на верхнем уровне скрипта становится свойством глобального объекта. const нельзя переприсвоить — поэтому по умолчанию const, let если значение меняется.","origin":"authored"},{"id":"js-types/const-object","topic":"js-types","level":"intern","concept":"variables","q":"Можно ли изменить свойство объекта, объявленного через const? Как это запретить?","a":"Можно: const запрещает только переприсвоить переменную, а сам объект или массив остаётся изменяемым (push, obj.key = ...). Чтобы запретить изменения, есть Object.freeze. Но он поверхностный: вложенные объекты остаются изменяемыми, их надо замораживать отдельно.","origin":"authored"},{"id":"js-types/null-undefined-undeclared","topic":"js-types","level":"intern","concept":"types","q":"Чем отличаются null, undefined и необъявленная переменная? Как их проверить?","a":"undefined — значения нет (переменная объявлена без значения, нет такого свойства). null ставят явно: «объекта здесь нет». Необъявленная переменная при чтении бросает ReferenceError. Проверки: x === null, x === undefined, а x == null ловит оба сразу; typeof null — \"object\", это старая ошибка языка.","origin":"authored"},{"id":"js-types/primitive-vs-reference","topic":"js-types","level":"intern","concept":"types","q":"Чем примитивы отличаются от объектов? Что передаётся в функцию — значение или ссылка?","a":"Примитивов семь: string, number, bigint, boolean, undefined, symbol, null. Они неизменяемы: методы вроде toUpperCase возвращают новое значение. В функцию всё передаётся по значению, но значение объекта — это ссылка. Поэтому функция может поменять свойства переданного объекта, а присваивание параметру нового объекта снаружи не видно.","origin":"authored"},{"id":"ng-change-detection/onpush","topic":"ng-change-detection","level":"middle","concept":"ng-change-detection","q":"Когда Angular проверяет компонент с ChangeDetectionStrategy.OnPush?","a":"Когда изменился вход (по ссылке), когда в нём или в детях сработал обработчик события из шаблона, когда изменился сигнал, прочитанный в его шаблоне, или вызван markForCheck (его вызывает async pipe). Иначе поддерево пропускается. Поэтому с OnPush данные меняют иммутабельно.","origin":"authored"},{"id":"ng-change-detection/zoneless","topic":"ng-change-detection","level":"middle","concept":"ng-change-detection","q":"Что значит zoneless в Angular 21 и что запускает проверку изменений без zone.js?","a":"С v21 новые приложения работают без zone.js по умолчанию. Проверку запускают: изменение сигнала, прочитанного в шаблоне, markForCheck (в том числе из async pipe), обработчики событий шаблона и хоста, ComponentRef.setInput. setTimeout или промис сами по себе ничего не перерисуют.","origin":"authored"},{"id":"ng-change-detection/expression-changed","topic":"ng-change-detection","level":"middle","concept":"ng-change-detection","q":"Что означает ошибка ExpressionChangedAfterItHasBeenCheckedError?","a":"В dev-режиме Angular проверяет привязки второй раз. Ошибка значит: значение изменилось во время проверки, например в ngAfterViewInit или геттер возвращает новое значение каждый раз. Чинят поток данных (computed, перенос логики раньше), а не прячут setTimeout.","origin":"authored"},{"id":"ng-change-detection/memo","topic":"ng-change-detection","level":"middle","concept":"ng-change-detection","q":"Почему вызов метода в шаблоне {{ calcTotal() }} может тормозить и как это исправить?","a":"Обычный метод выполняется при каждой проверке компонента. Исправление: computed (мемоизирован, пересчёт только при смене зависимостей) или чистый pipe (пересчёт при смене входа по ссылке). Сам вызов сигнала total() в шаблоне дешёвый.","origin":"authored"},{"id":"ng-change-detection/virtual-scroll","topic":"ng-change-detection","level":"middle","concept":"virtual-scroll","q":"Список из 10 000 строк тормозит при прокрутке. Что делать?","a":"Виртуальный скролл: рисовать только видимые строки (CDK cdk-virtual-scroll-viewport с *cdkVirtualFor). DOM остаётся маленьким, стили и раскладка дешёвые. Альтернативы: пагинация или «показать ещё». Плюс track в @for и OnPush у строк.","origin":"authored"},{"id":"ng-change-detection/defer","topic":"ng-change-detection","level":"middle","concept":"ng-defer","q":"Как работает @defer и какие у него триггеры?","a":"Зависимости блока выносятся в отдельный чанк и грузятся по триггеру: on idle (по умолчанию), viewport, interaction, hover, immediate, timer(…), или when <условие>. prefetch on … загружает код заранее. @placeholder, @loading, @error покрывают состояния. Зависимости должны быть standalone и не использоваться в этом же файле вне блока.","origin":"authored"},{"id":"ng-change-detection/defer-ssr","topic":"ng-change-detection","level":"middle","concept":"ng-defer","q":"Что рендерит @defer на сервере?","a":"При SSR и SSG блок @defer всегда рендерит @placeholder (или ничего), триггеры не запускаются. Исключение — инкрементальная гидратация с hydrate-триггерами: тогда сервер рендерит основное содержимое.","origin":"authored"},{"id":"ng-change-detection/zoneless-migration","topic":"ng-change-detection","level":"senior","concept":"ng-change-detection","q":"Как перевести существующее приложение с zone.js на zoneless?","a":"Сначала сделать компоненты совместимыми с OnPush: сигналы, async pipe, markForCheck вместо мутаций. Убрать NgZone.onMicrotaskEmpty, onStable, isStable — они не будут срабатывать. Для SSR сообщать о фоновой работе через PendingTasks. Включить provideZonelessChangeDetection (в v20) или убрать provideZoneChangeDetection (в v21), удалить zone.js из polyfills. Проверять по экранам, с тестами.","origin":"authored"},{"id":"ng-components/component-anatomy","topic":"ng-components","level":"junior","concept":"ng-component-model","q":"Из чего состоит компонент Angular?","a":"Класс с декоратором @Component: selector (тег), template или templateUrl, styles, imports (что использует шаблон). Класс хранит состояние и логику, шаблон его показывает. Стили по умолчанию изолированы в компоненте (emulated encapsulation).","origin":"authored"},{"id":"ng-components/standalone","topic":"ng-components","level":"junior","concept":"ng-component-model","q":"Что значит standalone-компонент и где теперь NgModule?","a":"Standalone-компонент сам перечисляет зависимости шаблона в imports. В современных версиях это поведение по умолчанию, NgModule для новых компонентов не нужен. Приложение стартует через bootstrapApplication с провайдерами в app.config.ts.","origin":"authored"},{"id":"ng-components/bindings","topic":"ng-components","level":"junior","concept":"ng-component-model","q":"Какие виды привязок есть в шаблоне Angular?","a":"{{ value }} — интерполяция текста. [prop]=\"expr\" — привязка свойства. (event)=\"handler($event)\" — событие. [(value)] — двусторонняя привязка (свойство + событие). Также [class.x], [style.width.px], [attr.aria-label].","origin":"authored"},{"id":"ng-components/input-output","topic":"ng-components","level":"junior","concept":"ng-inputs-outputs","q":"Как передать данные в дочерний компонент и получить событие обратно?","a":"В ребёнке: name = input<string>() (или input.required<string>()) и saved = output<User>(). В родителе: <user-card [name]=\"n\" (saved)=\"onSave($event)\" />. Ребёнок вызывает this.saved.emit(user). Старые @Input/@Output работают так же, но новые функции дают сигналы.","origin":"authored"},{"id":"ng-components/input-readonly","topic":"ng-components","level":"junior","concept":"ng-inputs-outputs","q":"Можно ли изменить значение input() внутри дочернего компонента?","a":"Нет: input-сигнал только для чтения, значение задаёт родитель. Нужна локальная копия, которую можно править, — linkedSignal от входа. Нужна двусторонняя связь с родителем — model().","origin":"authored"},{"id":"ng-components/lifecycle","topic":"ng-components","level":"junior","concept":"ng-lifecycle","q":"Чем отличаются constructor, ngOnInit, ngOnChanges и ngOnDestroy?","a":"constructor — создание и внедрение зависимостей, входы ещё не заданы. ngOnChanges — до ngOnInit и при каждой смене входов. ngOnInit — один раз, входы уже есть: место для старта логики. ngOnDestroy — уборка: подписки, таймеры. Вместо него часто используют DestroyRef.","origin":"authored"},{"id":"ng-components/after-render","topic":"ng-components","level":"junior","concept":"ng-lifecycle","q":"Где безопасно мерить размеры DOM-элемента в компоненте?","a":"В afterNextRender (один раз после ближайшего рендера) или afterEveryRender (после каждого). Они выполняются только в браузере, не на сервере. ngAfterViewInit тоже даёт готовый view, но на SSR там нет настоящего DOM.","origin":"authored"},{"id":"ng-components/control-flow","topic":"ng-components","level":"junior","concept":"ng-control-flow","q":"Как в шаблоне Angular показать блок по условию и список?","a":"@if (user(); as u) { ... } @else { ... } и @for (item of items(); track item.id) { ... } @empty { Пусто }. Ещё есть @switch. *ngIf и *ngFor — старый синтаксис: NgIf с v20 помечен deprecated, есть миграция ng generate @angular/core:control-flow.","origin":"authored"},{"id":"ng-components/for-track","topic":"ng-components","level":"junior","concept":"ng-control-flow","q":"Зачем в @for обязателен track и что в него писать?","a":"По track Angular понимает, какой элемент тот же после обновления массива, и переиспользует его DOM вместо пересоздания. Пишут стабильный уникальный ключ: track item.id. track $index подходит только для статичных списков: при вставке в начало DOM переедет не туда.","origin":"authored"},{"id":"ng-di/inject","topic":"ng-di","level":"junior","concept":"ng-injection","q":"Что такое DI в Angular и как получить сервис в компоненте?","a":"Angular сам создаёт зависимости и отдаёт их тем, кто попросил. Сервис помечают @Injectable({ providedIn: 'root' }), в компоненте пишут private api = inject(ApiService) (или параметр конструктора). Так легко подменить зависимость в тестах.","origin":"authored"},{"id":"ng-di/injection-context","topic":"ng-di","level":"middle","concept":"ng-injection","q":"Где можно вызывать inject(), а где он упадёт?","a":"В контексте внедрения: при инициализации полей класса, в конструкторе, в фабриках провайдеров, guard/resolver-функциях, внутри runInInjectionContext. В методе по клику или в setTimeout — ошибка NG0203. Нужен инжектор позже — сохрани его через inject(Injector) заранее.","origin":"authored"},{"id":"ng-di/provider-scope","topic":"ng-di","level":"middle","concept":"ng-injection","q":"Сервис указан в providers компонента и одновременно providedIn: 'root'. Что получат компонент и его дети?","a":"Поиск идёт снизу вверх по иерархии ElementInjector, потом EnvironmentInjector. Компонент и его дети получат свой экземпляр из providers компонента — новый на каждый экземпляр компонента. Остальное приложение — синглтон из root.","origin":"authored"},{"id":"ng-di/injection-token","topic":"ng-di","level":"middle","concept":"ng-providers","q":"Как внедрить конфиг-объект или функцию, у которых нет класса?","a":"Создать токен: export const API_URL = new InjectionToken<string>('API_URL'). Зарегистрировать: { provide: API_URL, useValue: 'https://…' } (или useFactory). Получить: inject(API_URL). Строки как токены не используют — возможны конфликты.","origin":"authored"},{"id":"ng-di/provider-kinds","topic":"ng-di","level":"middle","concept":"ng-providers","q":"Чем отличаются useClass, useValue, useFactory и useExisting?","a":"useClass — создать экземпляр другого класса (например, мок). useValue — отдать готовое значение. useFactory — вызвать функцию, внутри можно inject(). useExisting — алиас на уже зарегистрированный токен, без второго экземпляра. multi: true собирает несколько значений в массив.","origin":"authored"},{"id":"ng-di/resolution-modifiers","topic":"ng-di","level":"middle","concept":"ng-providers","q":"Что делают опции inject(Token, { optional, self, skipSelf, host })?","a":"optional — вернуть null вместо ошибки. self — искать только в своём инжекторе. skipSelf — начать с родителя (например, вложенное дерево ищет родительский узел). host — не идти выше хост-компонента.","origin":"authored"},{"id":"ng-forms/reactive-forms","topic":"ng-forms","level":"junior","concept":"ng-reactive-forms","q":"Как устроены Reactive Forms и чем отличаются от template-driven?","a":"Модель формы создаётся в классе: FormGroup из FormControl с валидаторами, шаблон привязывается через [formGroup] и formControlName. Значения и статус доступны синхронно, их легко тестировать. Template-driven строит модель из ngModel в шаблоне — проще для маленьких форм, хуже для сложных.","origin":"authored"},{"id":"ng-forms/validators","topic":"ng-forms","level":"junior","concept":"ng-reactive-forms","q":"Как добавить валидацию поля и показать ошибку только после того, как пользователь его тронул?","a":"new FormControl('', [Validators.required, Validators.email]). В шаблоне: @if (email.invalid && (email.touched || email.dirty)) и вывод по email.hasError('required'). Свой валидатор — функция (control) => ValidationErrors | null.","origin":"authored"},{"id":"ng-forms/typed-forms","topic":"ng-forms","level":"middle","concept":"ng-reactive-forms","q":"Что дают typed forms и зачем nonNullable?","a":"Тип контрола выводится из начального значения: FormControl<string | null>. null добавляется, потому что reset() по умолчанию ставит null. С nonNullable: true (или NonNullableFormBuilder) reset() возвращает начальное значение, и тип — string.","origin":"authored"},{"id":"ng-forms/value-vs-raw","topic":"ng-forms","level":"middle","concept":"ng-reactive-forms","q":"Чем form.value отличается от form.getRawValue()?","a":"value не включает выключенные (disabled) контролы. getRawValue() возвращает все поля, включая disabled. Поэтому в типе value поля группы — необязательные.","origin":"authored"},{"id":"ng-forms/async-validator","topic":"ng-forms","level":"middle","concept":"ng-reactive-forms","q":"Как проверить на сервере, что логин свободен, и не завалить API запросами?","a":"Асинхронный валидатор: функция, которая возвращает Observable или Promise с ValidationErrors | null. Он запускается только после успешных синхронных валидаторов; пока ждёт ответ, статус — PENDING. Чтобы не слать запрос на каждую букву — updateOn: 'blur' или debounce внутри валидатора.","origin":"authored"},{"id":"ng-forms/cva","topic":"ng-forms","level":"middle","concept":"ng-custom-controls","q":"Как сделать свой компонент (например, рейтинг-звёздочки), который работает с formControlName?","a":"Реализовать ControlValueAccessor: writeValue (форма → компонент), registerOnChange и registerOnTouched (компонент → форма), опционально setDisabledState. Зарегистрировать провайдер NG_VALUE_ACCESSOR с multi: true. Тогда валидаторы и статусы формы работают как с input.","origin":"authored"},{"id":"ng-forms/signal-forms-status","topic":"ng-forms","level":"middle","concept":"ng-custom-controls","q":"Что такое Signal Forms и можно ли брать их в прод на Angular 21?","a":"Новый API форм поверх сигналов: модель формы — сигнал, правила валидации задаются схемой. В Angular 21 он экспериментальный (@experimental): API может меняться без обычного цикла deprecation. В прод — осознанно, с готовностью переписать. Reactive Forms остаются стабильным выбором.","origin":"authored"},{"id":"ng-http/http-cold","topic":"ng-http","level":"junior","concept":"ng-http-client","q":"Почему this.http.get('/api/users') ничего не отправил на сервер?","a":"HttpClient возвращает холодный Observable: запрос уходит только при подписке (subscribe, async pipe, toSignal). Каждая новая подписка — новый запрос. Нужен один ответ на многих — shareReplay или держать результат в сигнале.","origin":"authored"},{"id":"ng-http/interceptor","topic":"ng-http","level":"middle","concept":"ng-http-client","q":"Как добавить токен ко всем запросам и обработать 401 в одном месте?","a":"Функциональный интерсептор: (req, next) => next(req.clone({ setHeaders: { Authorization: … } })).pipe(catchError(...)). Запрос неизменяемый, поэтому clone. Подключают provideHttpClient(withInterceptors([authInterceptor])). Интерсепторы выполняются по порядку массива.","origin":"authored"},{"id":"ng-http/retry","topic":"ng-http","level":"middle","concept":"ng-http-client","q":"Какие запросы можно повторять при ошибке и как?","a":"Безопасно повторять идемпотентные (GET, PUT, DELETE) и только при временных ошибках: сеть, 502/503/504, 429 с учётом Retry-After. 4xx повторять бессмысленно. В RxJS: retry({ count: 3, delay: (err, i) => timer(2 ** i * 500) }) — экспоненциальная задержка.","origin":"authored"},{"id":"ng-http/http-testing","topic":"ng-http","level":"middle","concept":"ng-http-client","q":"Как протестировать сервис, который ходит в HttpClient, без сети?","a":"В TestBed: provideHttpClient() и provideHttpClientTesting(). Взять HttpTestingController, вызвать метод сервиса, найти запрос expectOne('/api/x'), ответить req.flush(data) и проверить результат. В конце httpTesting.verify() — нет лишних запросов.","origin":"authored"},{"id":"ng-router/routes-lazy","topic":"ng-router","level":"junior","concept":"ng-routing","q":"Как описать маршруты и загрузить страницу лениво?","a":"Массив Routes: { path: 'orders', loadComponent: () => import('./orders/orders').then(m => m.Orders) }; для группы маршрутов — loadChildren с массивом routes. Код страницы попадёт в отдельный чанк и загрузится при первом переходе. Подключают через provideRouter(routes).","origin":"authored"},{"id":"ng-router/guards","topic":"ng-router","level":"junior","concept":"ng-routing","q":"Как закрыть страницу от неавторизованного пользователя?","a":"Функциональный guard: export const authGuard: CanActivateFn = () => inject(Auth).isLoggedIn() || inject(Router).parseUrl('/login'). Возвращает true, false, UrlTree или RedirectCommand (редирект), либо Observable/Promise. Guard — это UX: проверку прав всё равно делает сервер.","origin":"authored"},{"id":"ng-router/can-match","topic":"ng-router","level":"middle","concept":"ng-routing","q":"Чем canMatch отличается от canActivate?","a":"canActivate решает, можно ли открыть уже найденный маршрут. canMatch решает, подходит ли маршрут вообще: при false роутер ищет следующий маршрут, а ленивый код не загружается. Удобно для разных страниц на одном пути (например, по роли).","origin":"authored"},{"id":"ng-router/resolver","topic":"ng-router","level":"middle","concept":"ng-routing","q":"Что такое resolver и в чём его минус?","a":"ResolveFn загружает данные до активации маршрута, результат приходит в route data. Минус: навигация ждёт ответ, и пользователь видит «зависание». Часто лучше открыть страницу сразу и показать загрузку — или явно показывать индикатор навигации.","origin":"authored"},{"id":"ng-router/input-binding","topic":"ng-router","level":"middle","concept":"ng-routing","q":"Как получить параметр маршрута :id прямо во input компонента?","a":"Включить provideRouter(routes, withComponentInputBinding()). Тогда параметры пути, query-параметры и data/resolve попадают во входы с тем же именем: id = input<string>(). Без этого — inject(ActivatedRoute).paramMap.","origin":"authored"},{"id":"ng-rxjs/subjects","topic":"ng-rxjs","level":"middle","concept":"rxjs-subjects","q":"Чем отличаются Subject, BehaviorSubject и ReplaySubject?","a":"Subject раздаёт значения подписчикам, но поздний подписчик пропустит старые. BehaviorSubject требует начальное значение и сразу отдаёт новому подписчику текущее. ReplaySubject(n) отдаёт последние n значений. Снаружи сервиса отдают asObservable(), чтобы никто не мог вызвать next.","origin":"authored"},{"id":"ng-rxjs/combine-latest","topic":"ng-rxjs","level":"middle","concept":"rxjs-subjects","q":"Почему combineLatest([a$, b$]) ничего не выдаёт, хотя a$ уже выдал значение?","a":"combineLatest ждёт хотя бы одно значение от каждого источника, потом выдаёт массив последних значений при любом изменении. Если b$ ещё молчит — тишина. Помогает startWith у источника. forkJoin — другое: ждёт завершения всех и выдаёт один раз.","origin":"authored"},{"id":"ng-signals/signal-computed-effect","topic":"ng-signals","level":"junior","concept":"ng-signal-basics","q":"Чем signal, computed и effect отличаются друг от друга?","a":"signal — записываемое значение: set, update. computed — значение из других сигналов: ленивое, кэшируется, пересчитывается при изменении зависимостей. effect — побочный эффект при изменении сигналов (логирование, localStorage, сторонний API), выполняется асинхронно. Состояние через effect не синхронизируют.","origin":"authored"},{"id":"ng-signals/effect-antipattern","topic":"ng-signals","level":"middle","concept":"ng-signal-basics","q":"Почему effect(() => this.total.set(this.price() * this.qty())) — плохая идея?","a":"Производное значение нужно делать computed: оно синхронное, ленивое и не даёт лишних циклов. Запись в сигналы из effect ведёт к лишним проверкам, промежуточным неверным значениям и циклам. Для «производное, но можно править вручную» есть linkedSignal.","origin":"authored"},{"id":"ng-signals/untracked","topic":"ng-signals","level":"middle","concept":"ng-signal-basics","q":"Что делает untracked() и когда он нужен?","a":"Читает сигнал без создания зависимости. Пример: effect логирует при смене currentUser(), но читает counter() через untracked — смена counter не перезапустит effect. Также в effect оборачивают вызов внешнего кода, который может читать сигналы.","origin":"authored"},{"id":"ng-signals/equality","topic":"ng-signals","level":"middle","concept":"ng-signal-basics","q":"Почему после this.items().push(x) шаблон не обновился?","a":"Сигнал сравнивает значения через Object.is: массив тот же, значит «не изменился», и никто не узнал о push. Нужна новая ссылка: this.items.update(list => [...list, x]). Своё сравнение можно задать опцией equal.","origin":"authored"},{"id":"ng-signals/linked-signal","topic":"ng-signals","level":"middle","concept":"ng-async-signals","q":"Когда нужен linkedSignal вместо computed?","a":"Когда значение по умолчанию вычисляется из источника, но пользователь может его изменить. Пример: выбранный способ доставки = первый из списка, пользователь выбирает другой; при смене списка выбор сбрасывается. computed записывать нельзя, а linkedSignal можно. Стабилен с v20.","origin":"authored"},{"id":"ng-signals/resource","topic":"ng-signals","level":"middle","concept":"ng-async-signals","q":"Что такое resource() и какой у него статус в Angular 21?","a":"Асинхронные данные в виде сигналов: params (сигнальная функция) + loader({ params, abortSignal }). Даёт value(), status(), isLoading(), error(), reload(); при смене params старый запрос отменяется через abortSignal. Если params вернул undefined — loader не запускается, статус idle. В v21 resource, rxResource и httpResource — experimental.","origin":"authored"},{"id":"ng-signals/to-signal","topic":"ng-signals","level":"middle","concept":"ng-async-signals","q":"Как превратить Observable в сигнал и что будет до первого значения?","a":"toSignal(obs$): подписывается сразу (нужен контекст внедрения) и отписывается при уничтожении. До первого значения сигнал равен undefined, если не задать initialValue. Для BehaviorSubject и подобных — requireSync: true. Ошибка потока выбрасывается при чтении сигнала.","origin":"authored"},{"id":"ng-signals/to-observable","topic":"ng-signals","level":"middle","concept":"ng-async-signals","q":"Сигнал поменяли три раза подряд синхронно. Сколько значений выдаст toObservable?","a":"Одно, последнее. toObservable работает через effect: значение уходит после того, как сигнал «успокоился», а не синхронно на каждое изменение. Сигналы не дают синхронных уведомлений.","origin":"authored"},{"id":"ng-signals/take-until-destroyed","topic":"ng-signals","level":"middle","concept":"ng-async-signals","q":"Как отписаться от Observable при уничтожении компонента без ngOnDestroy?","a":"obs$.pipe(takeUntilDestroyed()).subscribe(...) — в контексте внедрения (поле, конструктор). Вне его — передать DestroyRef: takeUntilDestroyed(this.destroyRef). Оператор ставят последним в pipe, иначе следующие операторы могут держать подписку.","origin":"authored"},{"id":"ng-signals/state-levels","topic":"ng-signals","level":"middle","concept":"state-management","q":"Где хранить состояние в Angular-приложении: компонент, сервис, NgRx?","a":"Локальное (открыт ли дропдаун) — сигнал в компоненте. Общее для фичи — сервис с приватными сигналами и публичными readonly/computed. Сложное (много событий, кэш, оптимистичные обновления) — NgRx SignalStore. Classic NgRx/NGXS с actions и reducers — в основном поддержка legacy или очень большие команды.","origin":"authored"},{"id":"ng-signals/signal-store","topic":"ng-signals","level":"middle","concept":"state-management","q":"Что даёт NgRx SignalStore по сравнению с сервисом на сигналах?","a":"Декларативный стор: withState, withComputed, withMethods, хуки и переиспользуемые фичи (например, работа с коллекциями). Состояние защищено от записи снаружи, есть patchState и rxMethod для асинхронных потоков. Сервис проще; SignalStore выигрывает, когда паттерны повторяются в разных фичах.","origin":"authored"},{"id":"ng-ssr/ssr-vs-ssg","topic":"ng-ssr","level":"middle","concept":"ng-hydration","q":"Чем отличаются CSR, SSR и SSG в Angular и как выбрать режим для маршрута?","a":"CSR — HTML собирается в браузере: проще всего, хуже для первого показа и SEO. SSR — HTML на сервере на каждый запрос: быстрый первый показ, нужен Node-сервер. SSG (prerender) — HTML при сборке: дёшево и быстро, но только для одинаковых для всех страниц. В app.routes.server.ts каждому маршруту задают RenderMode.Client, Server или Prerender.","origin":"authored"},{"id":"ng-ssr/hydration","topic":"ng-ssr","level":"middle","concept":"ng-hydration","q":"Что такое гидратация и что её ломает?","a":"Клиент переиспользует DOM, пришедший с сервера, и навешивает обработчики вместо перерисовки — нет мигания, лучше LCP и CLS. Включают provideClientHydration(). Ломает прямая работа с DOM (innerHTML, appendChild), разная разметка на сервере и клиенте, невалидный HTML. Исключение для компонента — ngSkipHydration.","origin":"authored"},{"id":"ng-ssr/event-replay","topic":"ng-ssr","level":"middle","concept":"ng-hydration","q":"Пользователь кликнул по кнопке до окончания гидратации. Что будет?","a":"Без event replay клик потеряется: обработчиков ещё нет. withEventReplay() в provideClientHydration записывает ранние события и воспроизводит их после гидратации.","origin":"authored"},{"id":"ng-ssr/incremental-hydration","topic":"ng-ssr","level":"middle","concept":"ng-hydration","q":"Что даёт инкрементальная гидратация?","a":"Части страницы в @defer с hydrate-триггерами (hydrate on viewport, interaction, idle, hydrate when, hydrate never) остаются серверным HTML, пока триггер не сработает; их JS грузится позже. Меньше JS на старте. Включают withIncrementalHydration(); она сама включает event replay. Стабильна с v20.","origin":"authored"},{"id":"ng-ssr/browser-only","topic":"ng-ssr","level":"middle","concept":"ng-hydration","q":"Код обращается к window и localStorage. Почему он падает на SSR и как это исправить?","a":"На сервере нет window, document, localStorage. Такой код переносят в afterNextRender или afterEveryRender (выполняются только в браузере), проверяют isPlatformBrowser или прячут за абстракцией через DI с серверной реализацией.","origin":"authored"},{"id":"ng-ssr/transfer-cache","topic":"ng-ssr","level":"middle","concept":"ng-hydration","q":"Почему после SSR запросы HttpClient не уходят второй раз в браузере?","a":"При гидратации HttpClient кэширует ответы, сделанные на сервере, и передаёт их в HTML. Клиент берёт их из кэша. По умолчанию кэшируются GET и HEAD без заголовков Authorization и Proxy-Authorization. Настраивают через withHttpTransferCacheOptions.","origin":"authored"},{"id":"org-transformation/transformation","topic":"org-transformation","level":"principal","concept":"org-change","q":"Как провести миграцию фреймворка в масштабе компании?","a":"Обоснование для бизнеса (риски, стоимость поддержки, скорость). Пилот на одном продукте с метриками. Постепенная замена (strangler fig): новое рядом со старым, переключение по частям. Инструменты для команд: codemod, гайды, поддержка платформенной команды. Прогресс видим всем, без «большого взрыва».","origin":"authored"},{"id":"org-transformation/change-resistance","topic":"org-transformation","level":"principal","concept":"org-change","q":"Новый стандарт встречает сопротивление команд. Что делать?","a":"Выяснить причины: нет времени, не видят пользы, стандарт мешает их случаю. Сделать путь простым (инструменты, миграции, примеры). Найти союзников и показать результат пилота. Учитывать обратную связь и менять стандарт. Закреплять через гейты и платформу, а не через приказ.","origin":"authored"},{"id":"org-transformation/engineering-culture","topic":"org-transformation","level":"principal","concept":"org-change","q":"Как формировать инженерную культуру в компании?","a":"Культура — то, что поощряют и терпят. Открытый обмен информацией и разбор ошибок без наказаний (генеративная культура по Westrum). Стандарты закрепляют в процессах и инструментах. Лидеры показывают поведение сами. Мерить через опросы и метрики поставки (DORA).","origin":"authored"},{"id":"org-transformation/c-level","topic":"org-transformation","level":"principal","concept":"interest-negotiation","q":"Как говорить с CTO и CEO о технической стратегии?","a":"Сначала вывод и что нужно от них. Язык денег, рисков и сроков, а не технологий. Два-три варианта с ценой и рекомендацией. Короткий документ заранее. Доверие строят предсказуемостью: обещал — сделал, о проблемах — рано.","origin":"authored"},{"id":"org-transformation/public-expertise","topic":"org-transformation","level":"principal","concept":"org-change","q":"Зачем principal-инженеру выступления, статьи и open source и как это делать без вреда компании?","a":"Это найм, репутация и обратная связь от сообщества. Начинать с того, что команда уже сделала и измерила. Согласовать с компанией, что можно рассказывать. В open source — вклад в используемые библиотеки (баги, документация, исправления), а не только свои проекты.","origin":"authored"},{"id":"org-transformation/conway-reorg","topic":"org-transformation","level":"principal","concept":"org-design","q":"Две команды постоянно блокируют друг друга: каждая фича требует правок в коде обеих. Как бы ты это исправил?","a":"Сначала смотрю на закон Конвея: система повторяет структуру общения команд, значит, граница проходит не там. Выбираю, что менять: если архитектура правильная — меняю команды (Inverse Conway: перестраиваю команды под нужную архитектуру); если команды правильные — переношу границу модулей или API. Реорганизация дорогая и бьёт по людям, поэтому сначала пробую сменить владение кодом, и только потом — состав команд. Успех — меньше межкомандных зависимостей на фичу и короче lead time.","origin":"authored"},{"id":"org-transformation/team-types","topic":"org-transformation","level":"principal","concept":"org-design","q":"Ты проектируешь структуру из 10 команд. Какие типы команд нужны и как решить, кому что?","a":"Беру четыре типа из Team Topologies: stream-aligned (ведёт поток работы для части бизнеса — их большинство), platform (даёт внутренний продукт, чтобы ускорить остальных), enabling (временно помогает команде освоить новое), complicated-subsystem (сложная математика или экспертиза). Главный критерий — нагрузка на команду: она должна понимать и вести свою часть сама. Platform и enabling создаю, только когда видна повторяющаяся боль у нескольких команд. Успех — команды доставляют фичи без ожидания других.","origin":"authored"},{"id":"org-transformation/interaction-modes","topic":"org-transformation","level":"principal","concept":"platform-thinking","q":"Платформенная команда и продуктовые команды спорят: одни хотят работать вместе каждый день, другие — «просто дайте API». Как выбрать режим?","a":"В Team Topologies есть три режима: collaboration (работаем вместе ограниченное время, чтобы найти решение), X-as-a-Service (одна команда даёт сервис, другая пользуется), facilitation (одна команда учит другую). Выбор зависит от зрелости: новую возможность сначала ищем в collaboration, а когда API стабилен — переходим на X-as-a-Service. Ошибка — бесконечная collaboration: она дорогая и размывает ответственность. Срок перехода записываю заранее и проверяю, что команды перестали ждать друг друга.","origin":"authored"},{"id":"org-transformation/team-sizing","topic":"org-transformation","level":"principal","concept":"org-design","q":"Инженеров стало в три раза больше за год. Как ты будешь перестраивать команды и менеджмент?","a":"Ориентир Lethain: менеджер ведёт 6–8 инженеров. Меньше 4 — это скорее tech lead manager, у которого мало времени на людей; больше 8–9 — он только «тушит пожары». Делю команды по продуктовым областям, а не по технологиям, и заранее готовлю будущих менеджеров и техлидов. Риск — частые реорганизации, поэтому меняю структуру крупными шагами и редко. Слежу за нагрузкой менеджеров, временем найма и вовлечённостью людей.","origin":"authored"},{"id":"org-transformation/team-states","topic":"org-transformation","level":"principal","concept":"org-design","q":"Одна из твоих восьми команд тонет: бэклог растёт каждую неделю, люди устали. Что сделаешь на уровне организации?","a":"По Lethain у команды четыре состояния: отстаёт, держится на плаву, отдаёт долг, создаёт новое — и для каждого своё системное решение. Если отстаёт — нужны новые люди (нанимать, а не забирать из других команд), а пока — честно договориться с пользователями о сроках и найти быстрые победы. Тактическая помощь без системного решения только выматывает всех. Успех — бэклог перестаёт расти, затем команда начинает отдавать техдолг.","origin":"authored"},{"id":"org-transformation/cloud-cost","topic":"org-transformation","level":"principal","concept":"finops","q":"Финансовый директор просит сократить облачный счёт на 30%. Как будешь действовать?","a":"Не сокращаю вслепую: сначала делаю расходы видимыми по командам и сервисам, потом даю командам самим отвечать за свою стоимость — так советует FinOps. Говорю с финансами на языке unit economics: стоимость на клиента или на заказ, а не общая сумма. Быстрые шаги — неиспользуемые ресурсы и размеры машин; дорогие — смена архитектуры, их сравниваю с ценой инженерного времени. Если 30% требуют урезать надёжность — показываю этот размен и решаем вместе.","origin":"authored"},{"id":"org-transformation/tech-radar","topic":"org-transformation","level":"principal","concept":"decision-records","q":"В 40 командах используют 6 разных брокеров очередей и 4 фреймворка для фронтенда. Как навести порядок, не запрещая всё?","a":"Делаю технологический радар компании: кольца Adopt, Trial, Assess, Hold. Hold — не запрет для старого кода, а «новое на этом не начинаем». Радар ведёт небольшая группа сильных инженеров из разных команд, решения открыты и с причинами, его пересматривают регулярно. Размен: меньше свободы выбора, зато проще поддержка и переход людей между командами. Успех — новые сервисы стартуют на Adopt-технологиях, а число стеков постепенно падает.","origin":"authored"},{"id":"org-transformation/eng-vision","topic":"org-transformation","level":"principal","concept":"engineering-strategy","q":"CTO просит техническое видение на два года. Как бы ты его написал?","a":"Видение строю на стратегиях, а не на мечтах: беру несколько уже принятых стратегий и решений и показываю, к чему они приведут через два года. Так документ опирается на реальные решения и в него верят команды. Пишу конкретно: какой станет система, как изменится работа команд, что мы перестанем делать. Риск — красивый документ, который никто не читает, поэтому проверяю, что команды ссылаются на него при выборе решений.","origin":"authored"},{"id":"org-transformation/disagree-sponsor","topic":"org-transformation","level":"principal","concept":"technical-influence","q":"Ты principal без подчинённых, и твой руководитель выбрал направление, с которым ты не согласен. Что делаешь?","a":"Моё влияние держится на доверии руководителя, поэтому спорю наедине и заранее, а не на большом совещании — и никогда его не удивляю. Приношу данные, риски и вариант, а не только «нет». Если решение остаётся в силе и оно не опасно — disagree and commit: поддерживаю публично и делаю его успешным. Если риск серьёзный (безопасность, данные) — записываю его и поднимаю выше открыто, предупредив руководителя.","origin":"authored"},{"id":"org-transformation/hiring-scale","topic":"org-transformation","level":"principal","concept":"hiring-leveling","q":"Нужно нанять 40 инженеров за год и не уронить планку. С чего начнёшь?","a":"Смотрю на воронку найма по шагам: найти кандидатов, заинтересовать, оценить, закрыть оффер — и ищу, где она узкая. Рекомендации обычно заканчиваются быстро, значит, нужен sourcing и заметный бренд инженеров. Оценку делаю одинаковой для всех: стандартные интервью, рубрики, обученные интервьюеры. Размен — скорость против качества: лучше сдвинуть срок, чем снизить планку. Мерю конверсию на каждом шаге, время найма и как новички работают через полгода.","origin":"authored"},{"id":"people-leadership/multi-team","topic":"people-leadership","level":"lead","concept":"delegation-feedback","q":"Ты ведёшь направление из нескольких команд. Как держать согласованность и не стать бутылочным горлышком?","a":"Общие правила — в платформе и инструментах (каталог, линтер, шаблоны), а не в твоей голове. В каждой команде — техлид с правом решать в своей зоне. Регулярная гильдия для общих решений и RFC-процесс. Ты следишь за метриками и снимаешь блокеры, а не ревьюишь каждый MR.","origin":"authored"},{"id":"people-leadership/delegation","topic":"people-leadership","level":"lead","concept":"delegation-feedback","q":"Как правильно делегировать и не забирать задачу обратно?","a":"Передать результат и контекст: цель, критерий готовности, рамки решений, точки сверки. Выбрать уровень свободы по опыту человека. Не переделывать за него — давать обратную связь. Признать, что сделают иначе, чем ты. Ошибки в безопасных рамках — часть роста.","origin":"authored"},{"id":"people-leadership/hard-feedback","topic":"people-leadership","level":"lead","concept":"delegation-feedback","q":"Как дать сложную обратную связь сильному, но токсичному на ревью инженеру?","a":"Наедине и быстро. По модели SBI: ситуация, конкретное поведение, влияние на команду — без ярлыков. Выслушать его сторону. Договориться о конкретном изменении и сроке проверки. Ценность навыков не отменяет требований к поведению.","origin":"authored"},{"id":"people-leadership/mentor-seniors","topic":"people-leadership","level":"lead","concept":"delegation-feedback","q":"Чем наставничество для senior и лидов отличается от работы с джунами?","a":"Не учить технике, а расширять зону влияния: давать неоднозначные задачи, кросс-командные решения, выступления, найм. Больше вопросов, меньше ответов. Помогать с видимостью их работы и выбором пути: техническая экспертиза или управление.","origin":"authored"},{"id":"people-leadership/stakeholder-negotiation","topic":"people-leadership","level":"lead","concept":"interest-negotiation","q":"Продукт требует фичу к дате, а команда видит, что не успевает. Как договориться?","a":"Показать оценку с допущениями и рисками. Выяснить интерес за датой (конференция, договор). Предложить варианты: меньше объём к дате, позже полный, больше людей с ценой. Договориться письменно и предупреждать заранее, если план меняется.","origin":"authored"},{"id":"people-leadership/interview-loop","topic":"people-leadership","level":"lead","concept":"hiring-leveling","q":"Как бы ты построил процесс найма (interview loop) для инженеров в своём направлении?","a":"Сначала — что нужно роли и какому грейду, по матрице. Потом записанный loop: какие интервью, что каждое проверяет, обученные интервьюеры, для каждого — фиксированные вопросы и рубрика. Отдельно описать, как и когда определяем грейд кандидата. Успех меряю воронкой (где отваливаются кандидаты), временем найма и тем, как новые люди работают через полгода.","origin":"authored"},{"id":"people-leadership/fair-evaluation","topic":"people-leadership","level":"lead","concept":"hiring-leveling","q":"Как сделать, чтобы разные интервьюеры оценивали кандидатов одинаково и честно?","a":"Структурированное интервью: одинаковые вопросы для одной роли, подробные записи ответов, общая рубрика с примерами «слабо / нормально / сильно». Интервьюеров обучать и калибровать, отзыв писать до общего обсуждения, чтобы не подстраиваться под других. Минус — рубрики сложно писать и вопросы надо обновлять. Проверка — оценки разных интервьюеров сходятся чаще.","origin":"authored"},{"id":"people-leadership/promotion-disagree","topic":"people-leadership","level":"lead","concept":"hiring-leveling","q":"Инженер уверен, что заслужил повышение, а ты считаешь, что ещё нет. Как проведёшь разговор?","a":"Опираюсь на матрицу грейдов и факты, а не на ощущения: повышают за уже показанный устойчивый результат на следующем уровне, а не «в надежде, что справится». Показываю, где он уже на уровне и каких примеров не хватает. Вместе выбираем задачи, где он это покажет, и срок следующей проверки. Важно, чтобы это не было сюрпризом: о пробелах говорить в 1:1 заранее.","origin":"authored"},{"id":"people-leadership/underperformer","topic":"people-leadership","level":"lead","concept":"performance-conversations","q":"Сильный раньше senior стал заметно хуже работать. Что ты сделаешь?","a":"Говорю рано и прямо, с фактами. Сначала ищу причину: не хватает навыков (skill) или мотивации и обстоятельств (will) — личные проблемы, выгорание, неясные цели. От причины зависит план: обучение и ясные ожидания, или поддержка и смена задач. Договариваемся о конкретных целях и сроке; если улучшения нет — формальный план с HR.","origin":"authored"},{"id":"people-leadership/first-lead","topic":"people-leadership","level":"lead","concept":"delegation-feedback","q":"Как понять, что senior готов стать лидом, и как проверить это без большого риска?","a":"Признаки: уже ведёт решения команды, помогает другим, думает о сроках и людях, а не только о коде. Проверить на ограниченной роли: вести проект или замещать лида на время, с регулярной обратной связью. Заранее договориться, что путь обратимый — можно вернуться в инженеры без потери лица. Через несколько месяцев вместе решить, нравится ли ему эта работа и как она получается.","origin":"authored"},{"id":"people-leadership/skip-level","topic":"people-leadership","level":"lead","concept":"delegation-feedback","q":"Ты руководишь лидами. Как узнать реальное состояние команд, не подрывая их авторитет?","a":"Регулярные skip-level встречи с инженерами: спрашивать, что мешает, какая поддержка нужна, как им работается. Частоту выбираю по людям, чтобы не утонуть во встречах. Задачи напрямую не раздаю и решения лида не отменяю — темы возвращаю через лида. Плюс данные: метрики поставки, опросы, ретро. Признак успеха — люди говорят о проблемах раньше, чем они становятся кризисом.","origin":"authored"},{"id":"people-leadership/burnout","topic":"people-leadership","level":"lead","concept":"performance-conversations","q":"После долгого кранча в команде признаки выгорания. Что ты сделаешь?","a":"Сначала остановить причину: вернуть реальную ёмкость, снять или сдвинуть часть обязательств, договориться с продуктом. Потом искать системные источники: болезненные деплои, много переделок, ночные алерты. Поговорить с людьми в 1:1, не делая вид, что всё нормально. Следить за опросами благополучия, переработками и уходами людей.","origin":"authored"},{"id":"people-leadership/calibration","topic":"people-leadership","level":"lead","concept":"hiring-leveling","q":"Как сделать, чтобы оценки работы в разных командах были сравнимы и справедливы?","a":"Общая матрица ожиданий по грейдам и одинаковый процесс: самооценка, отзывы коллег, оценка менеджера. Потом калибровка: менеджеры вместе сравнивают оценки, объясняют их фактами и выравнивают «строгих» и «мягких». Важнее не шкала, а как считается оценка. Проверяю результат: нет ли перекоса по командам и группам людей.","origin":"authored"},{"id":"people-leadership/layoffs","topic":"people-leadership","level":"principal","concept":"performance-conversations","q":"Компания сокращает часть инженеров, в том числе из твоих команд. Как ты это проведёшь?","a":"Быстро после решения, чтобы не было слухов. Самому чётко понимать и честно объяснять причину. Подготовить менеджеров: каждый лично говорит со своими людьми, не перекладывая это на HR, и объясняет условия и поддержку. Руководство заранее обращается ко всей компании, а после сокращения остаётся на виду и говорит с теми, кто остался: что меняется в планах и почему. Следить за уходом ключевых людей после сокращений.","origin":"authored"},{"id":"people-leadership/career-ladder","topic":"people-leadership","level":"principal","concept":"hiring-leveling","q":"В компании нет понятной карьерной лестницы для инженеров. Как бы ты её создал?","a":"Описать уровни через влияние (impact), масштаб и поведение, а не через годы и технологии: команда — несколько команд — компания. Две ветки: инженерная (staff, principal) и менеджерская, чтобы расти можно было без перехода в менеджмент. Взять за основу открытые примеры (Dropbox, CircleCI), обсудить с инженерами и менеджерами, пилотировать на одном цикле оценки. Использовать её везде: в найме, оценке, повышениях.","origin":"authored"},{"id":"people-leadership/grow-staff","topic":"people-leadership","level":"principal","concept":"delegation-feedback","q":"Как вырастить в компании staff и principal-инженеров?","a":"Давать доступ к контексту: стратегия, планы руководства, «комнаты», где принимают решения — у инженеров этой информации обычно меньше. Спонсировать: отдавать им важные неоднозначные проблемы и делать их работу видимой. Регулярно давать обратную связь, в том числе неприятную. Успех — несколько людей уже работают на уровне staff и ведут кросс-командные решения без тебя.","origin":"authored"},{"id":"perf-debugging/bundle-incident","topic":"perf-debugging","level":"senior","concept":"profiling","q":"Main-бандл вырос с 400 до 900 КБ за релиз. Как найти причину и доказать исправление?","a":"Сравнить stats двух сборок в анализаторе (ng build --stats-json, esbuild analyzer, source-map-explorer). Найти новую тяжёлую зависимость или eager-импорт ленивого кода (импорт из index.ts, общий barrel). Вернуть в lazy route или @defer, заменить библиотеку. Цифры до и после — в MR, бюджет в angular.json — чтобы не повторилось.","origin":"authored"},{"id":"perf-debugging/angular-profiler","topic":"perf-debugging","level":"senior","concept":"profiling","q":"Экран тормозит при вводе текста. Как найти причину в Angular?","a":"Записать Performance-профиль и найти длинные задачи. В Angular DevTools Profiler посмотреть, сколько циклов change detection и какие компоненты дорогие. Типичные причины: методы в шаблоне, нет track в @for, тяжёлые pipe, частые события (scroll, mousemove), которые запускают проверку всего дерева. Исправить, повторить замер.","origin":"authored"},{"id":"perf-debugging/zone-pollution","topic":"perf-debugging","level":"senior","concept":"profiling","q":"Что такое zone pollution и как её найти?","a":"В приложении на zone.js любые setTimeout, requestAnimationFrame, события сторонних библиотек (карты, графики) запускают change detection, хотя UI не меняется. Видно в профилировщике как частые лишние циклы. Лечат runOutsideAngular для такой работы или переходом на zoneless.","origin":"authored"},{"id":"perf-debugging/long-tasks","topic":"perf-debugging","level":"senior","concept":"profiling","q":"Как читать flame chart и что делать с длинной задачей?","a":"По горизонтали — время, по вертикали — стек вызовов; широкие блоки внизу — кто съел время. Задача дольше 50 мс помечается как длинная. Разделить работу и отдавать управление (scheduler.yield или setTimeout), убрать лишнее, тяжёлые вычисления — в Web Worker.","origin":"authored"},{"id":"perf-debugging/field-vs-synthetic","topic":"perf-debugging","level":"senior","concept":"profiling","q":"Зачем и полевые данные (CrUX, RUM), и синтетический мониторинг (SpeedCurve, Calibre)?","a":"Поле показывает реальный опыт пользователей по устройствам и странам, но с задержкой и шумом. Синтетика гоняет одни и те же условия по расписанию — видно регрессию сразу после релиза и есть подробности для разбора. Вместе: синтетика ловит, поле подтверждает влияние.","origin":"authored"},{"id":"perf-debugging/prod-sourcemap","topic":"perf-debugging","level":"senior","concept":"source-maps","q":"В Sentry ошибка в main.3f9a.js:1:48213. Как найти место в коде?","a":"Загрузить source maps этой сборки в Sentry (по release), и стек покажется в исходниках. Публично .map не выкладывают. Дальше — воспроизвести по breadcrumbs и данным события, написать тест, исправить. После — короткий постмортем, если пострадали пользователи.","origin":"authored"},{"id":"perf-debugging/perf-regression","topic":"perf-debugging","level":"senior","concept":"profiling","q":"Как не допустить, чтобы производительность снова тихо деградировала?","a":"Бюджеты в CI: размер бандла (angular.json budgets), Lighthouse CI с порогами. RUM-дашборд по CWV с алертом по p75. Отчёт производительности в описании MR для рискованных изменений. Ответственный за метрики.","origin":"authored"},{"id":"perf-debugging/memory-leak","topic":"perf-debugging","level":"senior","concept":"profiling","q":"После часа работы вкладка SPA съедает гигабайт памяти. Как найти утечку?","a":"Сначала подтвердить: Performance с галочкой Memory, сценарий «открыл-закрыл экран» несколько раз, сборка мусора в начале и в конце — если JS heap растёт, утечка есть. Потом Memory → Heap snapshot: фильтр Detached показывает DOM-узлы, удалённые со страницы, но живые из-за ссылок из JS. Allocations on timeline показывает, что выделяется и не освобождается. Типичные причины: неотписанные подписки, глобальные слушатели, кэши без лимита.","origin":"authored"},{"id":"perf-debugging/inp-in-field","topic":"perf-debugging","level":"senior","concept":"core-web-vitals","q":"CrUX показывает плохой INP, а в лаборатории всё быстро. Как найти виноватое взаимодействие?","a":"Собрать данные с пользователей через attribution-сборку web-vitals (onINP из web-vitals/attribution). Она делит INP на input delay, processing duration и presentation delay и даёт записи Long Animation Frames со скриптом-виновником — видно, свой это код или сторонний. Смотреть, какие элементы и страницы дают худший INP. Потом воспроизвести это взаимодействие в лаборатории и чинить.","origin":"authored"},{"id":"perf-debugging/layout-thrashing","topic":"perf-debugging","level":"senior","concept":"profiling","q":"Что такое forced synchronous layout и layout thrashing и как их исправить?","a":"Если поменять стиль, а потом прочитать размер (offsetHeight, offsetWidth), браузер вынужден пересчитать layout прямо внутри скрипта. В цикле «прочитал-записал-прочитал» это происходит на каждой итерации — layout thrashing. Решение: сначала все чтения, потом все записи. В DevTools это видно как Forced reflow, в поле — через forcedStyleAndLayoutDuration в Long Animation Frames.","code":"// плохо: чтение и запись чередуются\nfor (const p of items) p.style.width = `${box.offsetWidth}px`;\n// хорошо: одно чтение, потом записи\nconst width = box.offsetWidth;\nfor (const p of items) p.style.width = `${width}px`;","origin":"authored"},{"id":"product-ux/ux-metrics","topic":"product-ux","level":"senior","concept":"experiments","q":"Как доказать, что улучшение UX сработало?","a":"До начала выбрать метрику: конверсия шага, доля отказов, время до цели, число ошибок в форме, обращения в поддержку. Снять базовую линию, выкатить на часть пользователей (A/B или флаг), сравнить. Плюс качественные данные: записи сессий, юзабилити-тесты.","origin":"authored"},{"id":"product-ux/ab-pitfalls","topic":"product-ux","level":"senior","concept":"experiments","q":"Какие ошибки делают в A/B-тестах?","a":"Смотрят результат каждый день и останавливают при первой «значимости». Не считают выборку заранее. Меряют десять метрик и выбирают удачную. Делят пользователей неслучайно или меняют условия посреди теста. Игнорируют эффект новизны и сезонность.","origin":"authored"},{"id":"product-ux/event-analytics","topic":"product-ux","level":"senior","concept":"experiments","q":"Как спроектировать событийную аналитику во фронтенде?","a":"План трекинга: события с понятными именами (объект + действие: order_submitted), параметры, кто владелец. Отправка через один сервис-обёртку, не из каждого компонента. Согласие пользователя и без персональных данных. Проверка событий в тестах. Воронки строят из этих событий.","origin":"authored"},{"id":"product-ux/response-time-feedback","topic":"product-ux","level":"senior","concept":"ux-feedback","q":"Операция в интерфейсе длится от доли секунды до минуты. Какую обратную связь показывать?","a":"Ориентиры Nielsen: до 0,1 с — ощущается мгновенно, хватает результата; до 1 с — мысль не прерывается, но кнопка должна сразу показать нажатие; до 10 с — нужен индикатор загрузки, иначе внимание уходит. Дольше 10 с — прогресс в процентах и возможность отменить. Поэтому на фронтенде меряют длительность реальных операций и выбирают индикатор по данным, а не «спиннер везде».","origin":"authored"},{"id":"product-ux/form-errors","topic":"product-ux","level":"senior","concept":"ux-feedback","q":"Пользователи бросают длинную форму на шаге с ошибками. Что изменишь в показе ошибок?","a":"Проверять inline, но не раньше, чем человек закончил ввод в поле (по blur, а не на каждый символ; исключение — подсказки сложности пароля). Сообщение ставить рядом с полем, писать по-человечески и говорить, как исправить. Цвет дополнять иконкой — не все различают красный. Успех мерить долей завершённых форм и числом ошибок на отправку до и после.","origin":"authored"},{"id":"sql-basics/inner-vs-left-join","topic":"sql-basics","level":"junior","concept":"sql-joins","q":"Чем INNER JOIN отличается от LEFT JOIN?","a":"INNER JOIN возвращает только строки, для которых нашлась пара по условию ON. LEFT JOIN возвращает каждую строку левой таблицы хотя бы один раз; если пары справа нет, столбцы правой таблицы заполняются NULL. Приём: LEFT JOIN плюс WHERE right.id IS NULL находит строки без пары.","origin":"authored"},{"id":"sql-basics/join-types","topic":"sql-basics","level":"junior","concept":"sql-joins","q":"Что делают RIGHT JOIN, FULL OUTER JOIN и CROSS JOIN?","a":"RIGHT JOIN — зеркало LEFT JOIN: сохраняются все строки правой таблицы. FULL OUTER JOIN сохраняет строки обеих таблиц, а недостающие поля заполняет NULL. CROSS JOIN не имеет условия и соединяет каждую строку с каждой: N × M строк.","origin":"authored"},{"id":"sql-basics/where-vs-having","topic":"sql-basics","level":"junior","concept":"sql-grouping","q":"Чем WHERE отличается от HAVING?","a":"WHERE фильтрует строки до группировки, поэтому в нём нельзя использовать агрегаты вроде COUNT или MAX. HAVING фильтрует уже готовые группы после GROUP BY и обычно содержит агрегат. Условие без агрегата лучше писать в WHERE: так база не группирует лишние строки.","code":"SELECT city, COUNT(*) AS orders\nFROM orders\nWHERE status = 'paid'      -- строки до группировки\nGROUP BY city\nHAVING COUNT(*) > 10;      -- группы после агрегации","origin":"authored"},{"id":"sql-basics/pk-vs-fk","topic":"sql-basics","level":"junior","concept":"sql-keys","q":"Что такое первичный и внешний ключ?","a":"Первичный ключ однозначно определяет строку: значения уникальны и не NULL, в таблице он только один. Внешний ключ требует, чтобы значение в столбце существовало в связанной таблице (ссылочная целостность). ON DELETE задаёт, что будет с дочерними строками при удалении родителя: CASCADE удалит их, RESTRICT запретит удаление.","origin":"authored"},{"id":"sql-basics/fk-index","topic":"sql-basics","level":"middle","concept":"sql-keys","q":"Создаёт ли внешний ключ индекс автоматически?","a":"Нет: ни PostgreSQL, ни SQL Server не индексируют столбец внешнего ключа сами (индекс создаётся только для PRIMARY KEY и UNIQUE). Индекс на FK часто нужен: он ускоряет JOIN и проверку дочерних строк при DELETE или UPDATE родителя. Без него такое удаление сканирует всю дочернюю таблицу.","origin":"authored"},{"id":"sql-basics/index-tradeoff","topic":"sql-basics","level":"junior","concept":"sql-indexes","q":"Зачем нужен индекс и почему нельзя индексировать всё подряд?","a":"Индекс (обычно B-tree) позволяет найти нужные строки за несколько шагов по дереву, а не сканировать всю таблицу; он помогает WHERE, JOIN и ORDER BY. Но базе нужно поддерживать каждый индекс при изменении данных, поэтому лишние индексы замедляют запись и занимают место. Неиспользуемые индексы лучше удалить.","origin":"authored"},{"id":"sql-basics/clustered-index","topic":"sql-basics","level":"middle","concept":"sql-indexes","q":"Чем кластерный индекс отличается от некластерного в SQL Server?","a":"Кластерный индекс задаёт физический порядок хранения строк, поэтому на таблицу он только один. Некластерный — отдельная структура: ключ плюс указатель на строку, их может быть много. PRIMARY KEY по умолчанию создаёт кластерный индекс, если его ещё нет.","origin":"authored"},{"id":"sql-basics/pagination","topic":"sql-basics","level":"junior","concept":"sql-dml-paging","q":"Как сделать постраничную выборку в PostgreSQL и SQL Server?","a":"В PostgreSQL — LIMIT и OFFSET, в SQL Server — OFFSET ... ROWS FETCH NEXT ... ROWS ONLY (это часть ORDER BY). В обоих случаях нужен ORDER BY с однозначным порядком, иначе страницы будут непредсказуемыми. Большой OFFSET медленный: пропущенные строки всё равно вычисляются.","code":"-- PostgreSQL\nSELECT id, name FROM products ORDER BY id LIMIT 20 OFFSET 40;\n-- SQL Server\nSELECT id, name FROM products ORDER BY id\nOFFSET 40 ROWS FETCH NEXT 20 ROWS ONLY;","origin":"authored"},{"id":"sql-basics/delete-without-where","topic":"sql-basics","level":"junior","concept":"sql-dml-paging","q":"Что будет, если выполнить UPDATE или DELETE без WHERE?","a":"Команда затронет все строки таблицы: DELETE FROM products удалит всё. Перед опасным изменением сначала выполни SELECT с тем же WHERE и проверь, какие строки попадут. Ручные правки на проде удобно делать в транзакции, чтобы при ошибке сделать ROLLBACK.","origin":"authored"},{"id":"supply-chain/attack-surface","topic":"supply-chain","level":"middle","concept":"dependency-trust","q":"Почему devDependencies входят в поверхность атаки frontend-проекта?","a":"Сборщики и тестовые инструменты выполняются в CI с доступом к исходникам и иногда секретам. Их компрометация может изменить опубликованный bundle, даже если пакет не попал в production runtime. Проверяйте прямые и транзитивные зависимости обоих типов; ограничивайте права сборочного окружения.","origin":"authored"},{"id":"supply-chain/age-gate-purpose","topic":"supply-chain","level":"middle","concept":"release-age","q":"Что даёт запрет устанавливать релизы моложе 72 часов и чего он не гарантирует?","a":"Он оставляет время для обнаружения проблем в свежем релизе. Возраст считают от публикации конкретной версии, а не от создания пакета. Старая версия тоже может быть вредоносной или уязвимой: окно ожидания дополняет аудит, проверку источника и ревью обновлений.","origin":"authored"},{"id":"supply-chain/pnpm-age-gate","topic":"supply-chain","level":"middle","concept":"release-age","q":"Как задать окно 72 часа для pnpm и какую ошибку легко сделать с единицами?","a":"pnpm считает минуты: 72 × 60 = 4320. Фильтр относится и к транзитивным зависимостям. Значение 72 задаст лишь 72 минуты. Зафиксируйте версию pnpm и проверьте эффективную конфигурацию; не переносите единицы из другого менеджера.","code":"# pnpm-workspace.yaml; pnpm >= 10.16\nminimumReleaseAge: 4320","origin":"authored"},{"id":"supply-chain/npm-age-gate","topic":"supply-chain","level":"middle","concept":"release-age","q":"Как настроить окно 72 часа в npm? Можно ли рассчитывать на эту настройку в любой старой версии CLI?","a":"Современный npm считает дни: 3 дня = 72 часа. Проверьте поддержку параметра установленной версией CLI и закрепите её в CI. В старом npm наличие строки в .npmrc само по себе не доказывает фильтрацию. --before задаёт абсолютную дату, а не постоянное скользящее окно.","code":"# .npmrc; npm с поддержкой min-release-age\nmin-release-age=3","origin":"authored"},{"id":"supply-chain/bun-age-gate","topic":"supply-chain","level":"middle","concept":"release-age","q":"Как задать 72 часа в Bun? Будет ли пересмотрен уже существующий bun.lock?","a":"Bun считает секунды: 72 × 60 × 60 = 259200. Фильтр работает при новом разрешении прямых и транзитивных зависимостей. Уже записанные в bun.lock версии остаются прежними; настройка не заменяет проверку существующего lockfile.","code":"# bunfig.toml\n[install]\nminimumReleaseAge = 259200","origin":"authored"},{"id":"supply-chain/yarn-age-gate","topic":"supply-chain","level":"middle","concept":"dependency-trust","q":"Как выразить окно 72 часа в современном Yarn и почему нужно учитывать поколение менеджера?","a":"Текущая документация Yarn принимает длительность: 3d означает три дня. Настройка относится к версии пакета в npm registry. Yarn Classic и ранние реализации имеют другой набор возможностей; закрепите совместимую версию и проверьте реальное поведение перед переносом конфигурации.","code":"# .yarnrc.yml; современный Yarn с поддержкой duration\nnpmMinimalAgeGate: \"3d\"","origin":"authored"},{"id":"supply-chain/locked-young-version","topic":"supply-chain","level":"senior","concept":"release-age","q":"После включения age gate в lockfile уже есть версия возрастом 2 часа. Как проверить требование «никаких релизов моложе 72 часов»?","a":"Не считайте настройку resolver достаточной: повторная установка из lockfile может не разрешать версии заново. Проверьте timestamps всех зафиксированных registry-версий относительно одного UTC-момента. Отсутствующие даты и сетевые ошибки должны давать явный провал проверки. Для git, file и URL-зависимостей нужна отдельная политика происхождения.","origin":"authored"},{"id":"supply-chain/frozen-lockfile","topic":"supply-chain","level":"middle","concept":"dependency-trust","q":"Как ставить зависимости в CI воспроизводимо и что lockfile не доказывает?","a":"Коммитьте lockfile и закрепляйте версию менеджера; команда должна падать при несовместимости manifest и lockfile. Lockfile фиксирует разрешённый граф, но не доказывает безопасность кода. Платформа, lifecycle scripts и внешние загрузки также влияют на результат сборки.","code":"# Выберите один менеджер проекта:\nnpm ci\npnpm install --frozen-lockfile\nbun ci\nyarn install --immutable","origin":"authored"},{"id":"supply-chain/install-scripts","topic":"supply-chain","level":"middle","concept":"dependency-trust","q":"Почему postinstall опасен и как разрешать нужные сборочные скрипты прагматично?","a":"Скрипт выполняет произвольные команды ещё при установке. По умолчанию запрещайте ненужные dependency scripts; необходимые пакеты разрешайте по небольшому проверенному списку. Для pnpm 10 применим onlyBuiltDependencies; Bun использует trustedDependencies. Проверяйте и встроенные разрешения менеджера. После изменения списка выполните чистую установку и сборку: native-инструменты могут требовать скрипт.","origin":"authored"},{"id":"supply-chain/audit-and-provenance","topic":"supply-chain","level":"senior","concept":"dependency-trust","q":"npm audit зелёный, а пакет имеет provenance. Достаточно ли этого для доверия обновлению?","a":"Нет. Audit ищет известные уязвимости; новый вредоносный релиз может ещё не иметь advisory. Provenance связывает артефакт с источником и процессом сборки, но не делает код безопасным. Проверяйте имя и registry, diff обновления, права scripts, транзитивный граф и тесты приложения.","origin":"authored"},{"id":"supply-chain/security-fix-exception","topic":"supply-chain","level":"senior","concept":"release-age","q":"Исправление активно эксплуатируемой уязвимости выпущено час назад. Как согласовать его с правилом 72 часов?","a":"Сравните риск эксплуатации с риском нового релиза. Если ждать опаснее, оформите узкое исключение для конкретного пакета и версии: источник исправления, проверка diff, тесты, ответственный и срок удаления исключения. Не отключайте окно для всего графа; исключение родителя не означает доверие его транзитивным зависимостям.","origin":"authored"},{"id":"supply-chain/verify-policy","topic":"supply-chain","level":"senior","concept":"dependency-trust","q":"Как тестировать политику age gate так, чтобы не получить зелёный CI при неработающем фильтре?","a":"Проверяйте поведение на контролируемых registry metadata: релизы моложе окна отклоняются, старше допускаются, граница определена явно. Добавьте случаи новой транзитивной версии, уже заполненного lockfile, отсутствующей даты и исключения. Сверьте effective config локально и в CI; пользовательские настройки и CLI-флаги могут менять результат.","origin":"authored"},{"id":"team-basics/ask-well","topic":"team-basics","level":"intern","concept":"ask-for-help","q":"Ты застрял на задаче. Когда и как правильно просить помощь?","a":"Поставь себе таймбокс (например, 30–60 минут). Не вышло — спроси, не молчи. В вопросе: цель, что сделал, что ожидал, что получил (точный текст ошибки), что уже пробовал. Так помогающему не нужно угадывать, и ты учишься формулировать проблему.","origin":"authored"},{"id":"team-basics/feedback","topic":"team-basics","level":"intern","concept":"code-review","q":"Ревьюер оставил 15 замечаний к твоему MR. Как на это реагировать?","a":"Это замечания к коду, а не к тебе. Исправь очевидное, по спорному — спроси «почему» и объясни свой вариант. Повторяющиеся замечания — повод выучить правило. Ответь на каждое: «исправил» или аргумент.","origin":"authored"},{"id":"tech-leadership/quality-bar","topic":"tech-leadership","level":"senior","concept":"technical-influence","q":"Как поднять планку качества в команде, не став «полицией ревью»?","a":"Договориться о стандартах вместе и записать. Перенести проверки в инструменты: линтер, типы, тесты, бюджеты в CI. Шаблон MR и чек-лист ревью. Разбирать не людей, а повторяющиеся ошибки — и превращать их в правила. Показать эффект: меньше багов и возвратов.","origin":"authored"},{"id":"tech-leadership/ttm","topic":"tech-leadership","level":"senior","concept":"technical-influence","q":"Как сократить время от задачи до прода и доказать это цифрой?","a":"Измерить поток: время ожидания ревью, длительность CI, ручные шаги деплоя, частоту возвратов. Найти самое долгое ожидание. Типичные шаги: маленькие MR, SLA на ревью, параллельный и кэшированный CI, автодеплой, preview, флаки-тесты в карантин. Сравнить lead time до и после.","origin":"authored"},{"id":"tech-leadership/lead-discussion","topic":"tech-leadership","level":"senior","concept":"technical-influence","q":"Как вести архитектурное обсуждение, чтобы оно закончилось решением?","a":"Заранее — короткий документ: проблема, ограничения, варианты, цена каждого. На встрече — держать фокус, дать высказаться тихим, отделять факты от мнений. Критерии выбора связать с бизнес-целями (сроки, риск, стоимость). Закончить решением, владельцем и ADR; несогласие записать.","origin":"authored"},{"id":"tech-leadership/influence","topic":"tech-leadership","level":"senior","concept":"technical-influence","q":"Как убедить другую команду поменять подход, если ты ей не руководишь?","a":"Понять их цели и ограничения. Принести данные: инцидент, метрика, цена текущего подхода. Предложить решение, которое помогает и им, и сделать первый шаг самому (пилот, PR). Договариваться письменно. Не вышло — эскалировать проблему, а не людей.","origin":"authored"},{"id":"tech-leadership/explain-simply","topic":"tech-leadership","level":"senior","concept":"technical-influence","q":"Как объяснить продакту, зачем тратить спринт на обновление фреймворка (Angular, .NET)?","a":"Начать с вывода и цены бездействия: риски безопасности, конец поддержки версии, медленнее разработка фич. Перевести в бизнес-язык: сроки, деньги, риски. Аналогия вместо терминов. Варианты: сейчас — столько, позже — столько и с каким риском.","origin":"authored"},{"id":"tech-leadership/cross-team-conflict","topic":"tech-leadership","level":"senior","concept":"interest-negotiation","q":"Две команды не могут договориться о формате общего API. Как разрешить конфликт?","a":"Собрать обе стороны, выяснить интересы за позициями (у одних — скорость, у других — обратная совместимость). Записать варианты и критерии. Найти решение, закрывающее оба интереса (версионирование, адаптер). Если нет согласия — решает владелец с понятными критериями, решение фиксируется.","origin":"authored"},{"id":"tech-leadership/mentoring","topic":"tech-leadership","level":"senior","concept":"delegation-feedback","q":"Как ты растишь middle-разработчика до senior?","a":"Вместе определить цель и пробелы по матрице грейдов. Давать задачи чуть выше уровня с поддержкой (дизайн-документ, ведение фичи, ревью других). Регулярные 1:1 с конкретной обратной связью. Постепенно убирать подсказки. Отмечать прогресс фактами для повышения.","origin":"authored"},{"id":"tech-leadership/tech-roadmap","topic":"tech-leadership","level":"lead","concept":"engineering-strategy","q":"Как бы ты составил технический roadmap на год для направления из нескольких команд?","a":"Начинаю с диагноза: цели бизнеса на год, главные риски и боли (инциденты, медленная поставка, конец поддержки версий). Из этого — 3–5 тем с ожидаемым эффектом, а не список задач; ёмкость делю явно, например фичи / долг / надёжность. Roadmap — живой документ: пересматриваю раз в квартал вместе с продуктом. Успех меряю не числом закрытых пунктов, а сдвигом целевых метрик (lead time, инциденты, стоимость).","origin":"authored"},{"id":"tech-leadership/tech-debt-priority","topic":"tech-leadership","level":"lead","concept":"tech-debt-strategy","q":"Как ты приоритизируешь техдолг против бизнес-задач в нескольких командах?","a":"Сначала чиню горячие точки: долг, который сейчас вызывает инциденты или тормозит частые изменения. Дальше — рычаги, которые держат качество сами (интерфейсы, модели данных, проверки в CI), а не разовые чистки. Формат зависит от долга: мелкий — постоянная доля ёмкости, крупный — отдельный проект с бизнес-обоснованием. Эффект показываю цифрами: меньше инцидентов, быстрее изменения в этой зоне.","origin":"authored"},{"id":"tech-leadership/rfc-process","topic":"tech-leadership","level":"lead","concept":"decision-records","q":"Как бы ты ввёл RFC-процесс, чтобы он помогал, а не стал бюрократией?","a":"RFC нужен только для нетривиальных изменений: новые сервисы, общие API, данные, то, что трудно откатить; порог записываю явно. Короткий шаблон: проблема, варианты, выбранный подход, риски, кто утверждает. Срок на отзывы (например, неделя) и владелец решения — иначе обсуждение не кончается. Слежу, что RFC реально меняют решения и не тормозят мелкие задачи; если тормозят — поднимаю порог.","origin":"authored"},{"id":"tech-leadership/adr-why","topic":"tech-leadership","level":"lead","concept":"decision-records","q":"Через год никто не помнит, почему выбрали эту базу и этот брокер. Как это предотвратить?","a":"Вести ADR рядом с кодом: короткая запись с контекстом, решением, статусом и последствиями. Без причины решения новые люди либо слепо его терпят, либо меняют, не зная, что сломают. Старые ADR не удаляют, а помечают superseded новым. Хороший признак — на ревью и онбординге ссылаются на ADR, а не спрашивают «почему так».","origin":"authored"},{"id":"tech-leadership/reversible-decisions","topic":"tech-leadership","level":"lead","concept":"decision-records","q":"Как решить, сколько времени и людей тратить на техническое решение?","a":"Сначала спрашиваю: решение легко откатить или нет. Обратимые («двусторонняя дверь») принимает команда быстро, примерно при 70% информации. Необратимые (схема данных, публичный API, вендор с долгим контрактом) — через RFC и широкое обсуждение. Если согласия нет, работает «disagree and commit»: несогласие записали и все делают выбранный вариант.","origin":"authored"},{"id":"tech-leadership/say-no","topic":"tech-leadership","level":"lead","concept":"interest-negotiation","q":"Тебя просят взять ещё один проект, а команды и так не успевают. Что ответишь?","a":"Не «нет», а цена «да»: показываю текущую загрузку и что сдвинется, если взять проект. Если бэклог растёт каждую неделю, команда отстаёт — нужно ограничить работу в процессе или добавить людей, а не новые задачи. Предлагаю варианты: взять вместо чего-то, взять позже, взять меньший объём. Решает владелец приоритетов, решение записано и видно всем.","origin":"authored"},{"id":"tech-leadership/project-success","topic":"tech-leadership","level":"lead","concept":"tech-debt-strategy","q":"Как доказать, что технический проект (рефакторинг, замена библиотеки) был успешным?","a":"Договориться о метрике до старта: что именно должно улучшиться и насколько (время сборки, частота инцидентов, время на типовое изменение). Снять базовое значение «до», потом мерить по ходу и после. Если метрику трудно связать с бизнесом — переводить в часы людей или риск. Нет измеримого эффекта — честно признать и сделать вывод для следующих проектов.","origin":"authored"},{"id":"tech-leadership/strategy-doc","topic":"tech-leadership","level":"principal","concept":"engineering-strategy","q":"Как бы ты написал инженерную стратегию для компании?","a":"Не с чистого листа: беру 5 недавних дизайн-документов и решений и выношу из них общие правила — это и есть стратегия. Структура: диагноз (что происходит и почему), руководящие правила (как выбираем, на что тратим ресурсы), конкретные действия. Хорошая стратегия скучная и отвечает на реальные споры команд. Успех — команды ссылаются на неё в решениях и спорят меньше.","origin":"authored"},{"id":"tech-leadership/org-migration","topic":"tech-leadership","level":"principal","concept":"tech-debt-strategy","q":"Нужно провести миграцию, которая затрагивает 30 команд. Как довести её до конца?","a":"Три фазы: снизить риск, упростить, завершить. Сначала обкатать дизайн на 1–2 самых сложных командах, а не на простых. Потом автоматизировать лёгкие 90% (codemod, скрипты) и сделать инструменты самообслуживания. В конце — запретить старый путь для нового кода (линтер), показывать статус руководителям и самим добить длинный хвост. Успех — старая система выключена, а не «почти все переехали».","origin":"authored"},{"id":"tech-leadership/what-matters","topic":"tech-leadership","level":"principal","concept":"technical-influence","q":"У principal-инженера сто возможных задач. Как ты выбираешь, на чём работать?","a":"Избегаю «перекусов» (лёгкие, но малозначимые задачи) и проектов ради видимости. Ищу то, что важно для компании и где есть место для вклада: проблемы, которые могут навредить бизнесу, рост людей, проекты, которым не хватает одного шага до успеха. Сверяю выбор с руководством, чтобы не уйти в свою тему. Проверка — через полгода видно изменение в компании, а не только мои коммиты.","origin":"authored"},{"id":"tech-leadership/decision-rights","topic":"tech-leadership","level":"principal","concept":"decision-records","q":"Какие архитектурные решения принимать централизованно, а какие оставить командам?","a":"По умолчанию решает команда, но через advice process: любой может принять решение, если спросил совета у тех, кого оно касается, и у экспертов. Центр задаёт рамки — принципы, стратегию, общие стандарты (безопасность, данные, API) — и форум для советов. Решения фиксируются в ADR. Центральное «да» оставляю только для необратимых и общих для всех вещей, иначе архитектор становится узким горлом.","origin":"authored"},{"id":"testing-strategy/integration-tests","topic":"testing-strategy","level":"middle","concept":"test-doubles","q":"Зачем интеграционные тесты, если есть юнит и E2E, и как они влияют на скорость поставки?","a":"Они проверяют стык: компонент + сервис + роутер, с подменой только сети. Ловят ошибки, которые юниты не видят, и работают быстрее и стабильнее E2E. Хорошее покрытие ими позволяет релизить без долгой ручной проверки — это прямо сокращает TTM.","origin":"authored"},{"id":"testing-strategy/mock-stub-spy","topic":"testing-strategy","level":"middle","concept":"test-doubles","q":"Чем отличаются mock, stub и spy?","a":"Stub возвращает заготовленный ответ, ничего не проверяет. Spy оборачивает функцию и записывает вызовы, их проверяют после. Mock заранее знает ожидаемые вызовы и падает, если их нет. В Vitest: vi.fn() — заглушка/шпион, vi.spyOn() — шпион на существующем методе.","origin":"authored"},{"id":"testing-strategy/mock-server","topic":"testing-strategy","level":"middle","concept":"test-doubles","q":"Зачем мок-сервер (например, MSW), если можно мокать сервис?","a":"Мок на уровне сети не трогает код приложения: работают настоящие сервисы, интерсепторы, обработка ошибок. Одни и те же обработчики подходят для тестов, Storybook и локальной разработки без бэкенда.","origin":"authored"},{"id":"testing-strategy/e2e","topic":"testing-strategy","level":"middle","concept":"test-doubles","q":"Как писать стабильные E2E-тесты на Playwright?","a":"Искать элементы как пользователь: getByRole, getByLabel, а не CSS-классы. Не ставить фиксированные sleep — Playwright сам ждёт элементы. Каждый тест независим и готовит свои данные. Внешние сервисы мокать. E2E оставлять для ключевых сценариев — их мало и они дорогие.","origin":"authored"},{"id":"testing-strategy/coverage","topic":"testing-strategy","level":"middle","concept":"static-analysis","q":"Покрытие кода 90%. Это значит, что код хорошо протестирован?","a":"Нет: покрытие показывает, какие строки выполнились, а не проверены ли результаты. Тест без assert даёт покрытие. Полезно искать непокрытые важные места и не давать покрытию падать; SonarQube и подобные показывают ещё дубли и запахи кода. Силу тестов лучше проверяет mutation testing.","origin":"authored"},{"id":"testing-strategy/custom-eslint","topic":"testing-strategy","level":"middle","concept":"static-analysis","q":"Когда писать своё ESLint-правило и как оно устроено?","a":"Когда командную договорённость нарушают снова и снова: запрет импорта между фичами, запрет прямого HttpClient в компонентах. Правило — объект с meta и create(context), который возвращает обработчики узлов AST (например, ImportDeclaration) и вызывает context.report. Тестируют через RuleTester.","origin":"authored"},{"id":"testing-strategy/testing-practice","topic":"testing-strategy","level":"middle","concept":"test-pyramid","q":"В твоей зоне почти нет тестов. Как внедрить тестирование, не остановив разработку?","a":"Не покрывать всё сразу. Правило: новый код и каждый исправленный баг — с тестом. Начать с самых рискованных мест (деньги, права, сложная логика). Добавить тесты в CI и запретить падение покрытия изменённого кода. Через месяц показать эффект: меньше регрессий, быстрее ревью.","origin":"authored"},{"id":"testing-strategy/pyramid","topic":"testing-strategy","level":"senior","concept":"test-pyramid","q":"Как выстроить тестовую стратегию фронтенда?","a":"Основа — быстрые юнит- и интеграционные тесты на логику и компоненты. Сверху — немного E2E на ключевые сценарии. Отдельно: визуальные тесты для дизайн-системы, a11y-проверки, smoke после деплоя. Регрессионный набор растёт из багов. Каждому уровню — своя цель, чтобы не проверять одно и то же трижды.","origin":"authored"},{"id":"testing-strategy/visual-tests","topic":"testing-strategy","level":"senior","concept":"test-pyramid","q":"Что такое визуальные тесты и где они ломаются?","a":"Скриншот компонента или страницы сравнивается с эталоном (в Playwright — toHaveScreenshot). Ловят случайные изменения стилей. Ломаются от шрифтов, анимаций, данных и разных ОС — нужны стабильная среда (Docker), фиксированные данные и отключённые анимации.","origin":"authored"},{"id":"testing-strategy/mutation","topic":"testing-strategy","level":"senior","concept":"test-pyramid","q":"Что такое mutation testing?","a":"Инструмент (например, Stryker) вносит маленькие изменения в код — мутанты: > на >=, true на false. Если тесты не упали, мутант «выжил» — тесты слабые. Это меряет качество тестов лучше покрытия, но медленно: запускают на важных модулях или по расписанию.","origin":"authored"},{"id":"testing-strategy/flaky","topic":"testing-strategy","level":"senior","concept":"test-pyramid","q":"В CI много флаки-тестов. Что делать?","a":"Мерить: какие тесты падают нестабильно (история CI). Флаки-тест сразу в карантин с задачей и владельцем, а не «перезапусти». Частые причины: ожидание по времени, общие данные, порядок тестов, сеть. Цель — доверие к красному CI: иначе команда начинает игнорировать падения.","origin":"authored"},{"id":"ts-advanced/conditional-types","topic":"ts-advanced","level":"senior","concept":"type-level-ts","q":"Что такое conditional types и infer? Пример из практики.","a":"T extends U ? X : Y — выбор типа по условию; над union он распределяется по каждому члену. infer достаёт часть типа: type Unwrap<T> = T extends Promise<infer V> ? V : T. Так устроены встроенные ReturnType, Awaited, Parameters.","origin":"authored"},{"id":"ts-advanced/mapped-types","topic":"ts-advanced","level":"senior","concept":"type-level-ts","q":"Как сделать тип, где все поля объекта стали readonly и необязательными, а имена — с префиксом on?","a":"Mapped type: { readonly [K in keyof T]?: T[K] }. Переименование ключей — через as и template literal: { [K in keyof T as `on${Capitalize<string & K>}`]: (v: T[K]) => void }. Модификаторы снимают через -readonly и -?.","origin":"authored"},{"id":"ts-advanced/ast-basics","topic":"ts-advanced","level":"senior","concept":"type-level-ts","q":"Что такое AST и как сделать простую трансформацию кода?","a":"AST — дерево синтаксиса: код разобран на узлы (импорт, вызов, идентификатор). Трансформация: разобрать (TypeScript Compiler API, ts-morph, Babel), обойти дерево, найти узлы по типу, изменить, напечатать код. Так работают линтеры, codemod и миграции Angular.","origin":"authored"},{"id":"ts-advanced/exhaustive-never","topic":"ts-advanced","level":"senior","concept":"ts-narrowing","q":"Как сделать, чтобы при добавлении нового варианта в union компилятор показал все места, где его забыли обработать?","a":"Описать состояние как discriminated union с общим полем-литералом (status: 'loading' | 'success' | 'error'). В switch по этому полю TypeScript сужает тип в каждой ветке. В default присвоить значение переменной типа never: если появится необработанный вариант, он не присвоится к never и будет ошибка компиляции.","code":"type State =\n  | { status: 'loading' }\n  | { status: 'success'; data: string }\n  | { status: 'error'; message: string };\n\nfunction assertNever(value: never): never {\n  throw new Error(`Unhandled state: ${JSON.stringify(value)}`);\n}\n\nfunction label(state: State): string {\n  switch (state.status) {\n    case 'loading': return 'Загрузка…';\n    case 'success': return state.data;\n    case 'error': return state.message;\n    default: return assertNever(state);\n  }\n}","origin":"authored"},{"id":"ts-advanced/satisfies","topic":"ts-advanced","level":"senior","concept":"ts-narrowing","q":"Чем satisfies отличается от аннотации типа и от as?","a":"Аннотация const x: T проверяет значение, но расширяет его тип до T — конкретные типы полей теряются. as T не проверяет, а просто верит. satisfies T проверяет, что значение подходит под T (ловит опечатку в ключе, пропущенный вариант), и оставляет выведенный узкий тип. Типичный случай — карта вариантов: satisfies Record<Variant, string>.","origin":"authored"},{"id":"ts-basics/any-unknown","topic":"ts-basics","level":"junior","concept":"ts-types","q":"Чем any отличается от unknown?","a":"any выключает проверку типов: с ним можно всё, и ошибки уходят в рантайм. unknown — «что-то неизвестное»: пока не сузишь тип проверкой (typeof, instanceof, схема), ничего делать нельзя. Для внешних данных (API, JSON.parse) правильный тип — unknown.","origin":"authored"},{"id":"ts-basics/interface-vs-type","topic":"ts-basics","level":"junior","concept":"ts-types","q":"interface или type — что выбрать?","a":"Для формы объекта почти одно и то же. interface можно расширять через extends и «дописывать» повторным объявлением. type умеет union, кортежи, mapped и conditional типы. Частое правило: interface для объектов и контрактов, type — для остального.","origin":"authored"},{"id":"ts-basics/function-types","topic":"ts-basics","level":"junior","concept":"ts-types","q":"Как типизировать функцию с необязательным параметром и колбэком?","a":"function load(id: string, onDone?: (result: User) => void): Promise<User>. Необязательный параметр помечают ?, его тип — T | undefined. Тип колбэка пишут как стрелку. Возвращаемый тип явно указывают у публичных функций — это контракт.","origin":"authored"},{"id":"ts-basics/union-narrowing","topic":"ts-basics","level":"junior","concept":"ts-types","q":"Что такое union-тип и как с ним работать?","a":"string | number — значение одного из типов. Пока тип не сужен, доступны только общие операции. Сужают проверкой: typeof, in, сравнение с литералом. Для объектов удобен дискриминант: { kind: 'ok', data } | { kind: 'error', message }.","origin":"authored"},{"id":"ts-basics/generic-basics","topic":"ts-basics","level":"junior","concept":"ts-types","q":"Зачем нужны generics? Приведи простой пример.","a":"Generic — параметр типа, он сохраняет связь между входом и выходом. function first<T>(items: T[]): T | undefined вернёт string для string[] и User для User[]. С any эта связь теряется.","origin":"authored"},{"id":"unit-testing/bundler-role","topic":"unit-testing","level":"junior","concept":"bundlers","q":"Что делает сборщик в Angular-проекте и чем dev-сборка отличается от prod?","a":"Компилирует TypeScript и шаблоны, собирает граф модулей, делит на чанки, обрабатывает стили и ассеты. Angular CLI использует esbuild, а dev-сервер — Vite. Dev — быстрая пересборка и source maps. Prod — минификация, tree shaking, хэши в именах файлов для кэша.","origin":"authored"},{"id":"unit-testing/aaa","topic":"unit-testing","level":"junior","concept":"unit-tests","q":"Как выглядит хороший юнит-тест?","a":"Arrange — подготовить данные, Act — вызвать код, Assert — проверить результат. Один сценарий, понятное имя («возвращает пустой список, если фильтр не совпал»), без сети и случайностей. Тест проверяет поведение, а не приватные детали — иначе ломается при рефакторинге.","origin":"authored"},{"id":"unit-testing/vitest-default","topic":"unit-testing","level":"junior","concept":"unit-tests","q":"Какой тест-раннер в новых проектах Angular 21 и где выполняются тесты?","a":"Vitest. Новые проекты получают vitest и jsdom по умолчанию: тесты идут в Node.js, а DOM эмулирует jsdom. Запуск — ng test.","origin":"authored"},{"id":"unit-testing/component-test","topic":"unit-testing","level":"junior","concept":"unit-tests","q":"Как протестировать простой компонент-счётчик?","a":"TestBed.createComponent(Counter), найти кнопку в fixture.nativeElement, кликнуть, дождаться стабильности (await fixture.whenStable()), проверить текст. Тест работает через DOM, как пользователь, а не дёргает приватные поля.","origin":"authored"},{"id":"ux-quality/four-states","topic":"ux-quality","level":"junior","concept":"ui-states","q":"Какие состояния должен показать экран со списком заказов?","a":"Загрузка (скелетон или спиннер), данные, пустой список (что делать дальше: «Создайте первый заказ»), ошибка (что случилось и кнопка «Повторить»). Плюс частичные: идёт обновление, нет прав. Без них интерфейс «молчит», и пользователь не понимает, что происходит.","origin":"authored"},{"id":"ux-quality/error-messages","topic":"ux-quality","level":"junior","concept":"ui-states","q":"Каким должно быть хорошее сообщение об ошибке?","a":"Видно рядом с местом ошибки. Человеческим языком, без кодов и стектрейсов. Говорит, что случилось и как исправить. Не винит пользователя. Введённые данные не теряются.","origin":"authored"},{"id":"ux-quality/theming","topic":"ux-quality","level":"middle","concept":"css-theming","q":"Как сделать тёмную, светлую и системную тему без перезагрузки?","a":"Цвета — только через CSS-переменные (токены). Значения переключают атрибутом на <html> (data-theme=\"dark\") или light-dark() + color-scheme. «Системная» — @media (prefers-color-scheme: dark), а изменения ловят через matchMedia. Выбор сохраняют в localStorage и применяют до первой отрисовки, чтобы не мигало.","origin":"authored"},{"id":"web-http/request-response","topic":"web-http","level":"intern","concept":"http-basics","q":"Что происходит, когда браузер делает HTTP-запрос к API?","a":"Браузер находит IP через DNS, открывает соединение (с TLS для https) и шлёт запрос: метод, путь, заголовки, иногда тело. Сервер возвращает код статуса, заголовки и тело (часто JSON). Клиент сам решает, что делать с ответом — HTTP не знает про UI.","origin":"authored"},{"id":"web-http/status-codes","topic":"web-http","level":"intern","concept":"http-basics","q":"Что означают коды 200, 201, 204, 301, 304, 400, 401, 403, 404, 409, 422, 500, 503?","a":"200 ок, 201 создано, 204 ок без тела. 301 ресурс переехал навсегда, 304 не изменился — бери из кэша. 400 плохой запрос, 401 не аутентифицирован, 403 нет прав, 404 не найдено, 409 конфликт состояния, 422 данные не прошли проверку. 500 ошибка сервера, 503 сервис временно недоступен.","origin":"authored"},{"id":"web-http/rest-methods","topic":"web-http","level":"junior","concept":"rest-api","q":"Чем PUT отличается от PATCH и почему POST нельзя просто повторить?","a":"PUT заменяет ресурс целиком и идемпотентен: повтор даёт тот же результат. PATCH меняет часть. POST создаёт и не идемпотентен: повтор может создать дубль заказа. Для безопасного повтора POST используют ключ идемпотентности.","origin":"authored"},{"id":"web-http/cors","topic":"web-http","level":"junior","concept":"cors","q":"Почему запрос из браузера падает с ошибкой CORS, а тот же запрос через curl работает?","a":"CORS — правило браузера. Для чужого origin браузер требует, чтобы сервер разрешил его заголовком Access-Control-Allow-Origin. Для «непростых» запросов (JSON, свои заголовки) сначала идёт preflight OPTIONS. curl ничего не проверяет. Чинят на сервере или прокси, а не на фронте.","origin":"authored"},{"id":"web-http/cors-credentials","topic":"web-http","level":"middle","concept":"cors","q":"Как отправить куки на API другого домена через CORS?","a":"На клиенте — credentials: 'include' (в HttpClient — withCredentials: true). Сервер должен вернуть Access-Control-Allow-Credentials: true и конкретный origin, не *. Куке нужен SameSite=None; Secure, если домены разные сайты.","origin":"authored"},{"id":"web-http/http-cache-headers","topic":"web-http","level":"middle","concept":"http-caching","q":"Как настроить кэш для index.html и для main.3f9a.js?","a":"Файлы с хэшем в имени — Cache-Control: public, max-age=31536000, immutable: имя поменяется вместе с содержимым. index.html — no-cache: браузер хранит копию, но каждый раз сверяет с сервером (ETag/Last-Modified), иначе пользователи застрянут на старой версии.","origin":"authored"},{"id":"web-http/etag","topic":"web-http","level":"middle","concept":"http-caching","q":"Как работает ETag?","a":"Сервер отдаёт ETag — отпечаток версии ресурса. Клиент при следующем запросе шлёт If-None-Match с этим значением. Не изменилось — 304 Not Modified без тела, экономия трафика. Для API ETag ещё помогает от потерянных обновлений: If-Match при PUT вернёт 412, если ресурс уже изменили.","origin":"authored"},{"id":"web-http/no-cache-vs-no-store","topic":"web-http","level":"middle","concept":"http-caching","q":"Чем Cache-Control: no-cache отличается от no-store?","a":"no-cache — хранить можно, но перед использованием надо сверить с сервером. no-store — не хранить вообще (персональные и секретные данные). private — только в браузере, не в CDN.","origin":"authored"},{"id":"web-http/graphql-vs-rest","topic":"web-http","level":"middle","concept":"graphql-client","q":"Чем GraphQL отличается от REST для фронтенда?","a":"Клиент описывает, какие поля нужны, и получает их одним запросом — нет лишних данных и цепочек запросов. Изменения — через mutation. Минусы: HTTP-кэш почти не работает (обычно POST на один endpoint), нужен клиентский кэш; сложнее лимиты и мониторинг на сервере.","origin":"authored"},{"id":"web-http/graphql-cache","topic":"web-http","level":"middle","concept":"graphql-client","q":"Как кэшируют данные на клиенте GraphQL?","a":"Нормализованный кэш: каждый объект хранится один раз по ключу тип + id. Ответы разных запросов ссылаются на одни записи. После mutation, вернувшей объект с id, все экраны с ним обновятся сами. Без стабильных id нормализация ломается.","origin":"authored"},{"id":"web-http/ws-vs-sse","topic":"web-http","level":"middle","concept":"realtime","q":"Когда выбрать WebSocket, а когда Server-Sent Events?","a":"SSE — поток только от сервера к клиенту по обычному HTTP: уведомления, статус задачи, ленты. Браузер сам переподключается. WebSocket — двусторонний канал: чаты, совместное редактирование, игры. Переподключение, heartbeat и восстановление пропущенных сообщений пишешь сам.","origin":"authored"},{"id":"web-http/ws-reconnect","topic":"web-http","level":"middle","concept":"realtime","q":"Что нужно предусмотреть при работе с WebSocket в продакшене?","a":"Переподключение с экспоненциальной задержкой и случайным разбросом, heartbeat для обнаружения «мёртвого» соединения, догрузку пропущенного после переподключения (по последнему id), аутентификацию при подключении, закрытие сокета при уходе со страницы.","origin":"authored"},{"id":"web-http/get-vs-post","topic":"web-http","level":"intern","concept":"http-basics","q":"Какие HTTP-методы ты знаешь и чем GET отличается от POST?","a":"GET — получить ресурс; он безопасный (ничего не меняет), идемпотентный и кэшируется, тела у запроса нет. POST — отправить данные, обычно создаёт что-то на сервере, не идемпотентный. Ещё есть PUT (заменить), PATCH (частично изменить), DELETE (удалить), HEAD (как GET, но без тела ответа), OPTIONS (какие возможности есть у ресурса).","origin":"authored"},{"id":"web-http/headers","topic":"web-http","level":"intern","concept":"http-basics","q":"Что такое HTTP-заголовки? Назови те, что встречаются в каждом запросе к API.","a":"Заголовки — пары «имя: значение» с данными о запросе или ответе, имя не зависит от регистра. Content-Type говорит, в каком формате тело (application/json), Accept — какой формат клиент хочет получить. Authorization несёт данные для входа, например Bearer-токен. Cache-Control управляет кэшем, Set-Cookie в ответе сохраняет куку.","origin":"authored"},{"id":"web-http/cookie-vs-storage","topic":"web-http","level":"intern","concept":"browser-storage","q":"Чем cookie отличается от localStorage и sessionStorage?","a":"Куки браузер сам отправляет с каждым запросом на свой домен; размер — около 4 КБ, флаг HttpOnly прячет куку от JS. localStorage и sessionStorage на сервер не уходят и хранят несколько МБ строк на origin. localStorage живёт, пока его не очистят; sessionStorage — пока открыта вкладка. Для данных только на клиенте MDN советует Web Storage или IndexedDB, а не куки.","origin":"authored"},{"id":"web-security-basics/secrets","topic":"web-security-basics","level":"junior","concept":"xss-csrf","q":"Можно ли положить секретный API-ключ в environment.ts фронтенда?","a":"Нет: всё, что попало в бандл, видит любой в DevTools. На фронте могут быть только публичные ключи с ограничениями (по домену, по правам). Секреты живут на сервере или в BFF, фронт ходит через него.","origin":"authored"},{"id":"web-security-basics/xss-basics","topic":"web-security-basics","level":"junior","concept":"xss-csrf","q":"Что такое XSS и как Angular от него защищает?","a":"XSS — атакующий выполняет свой JS на твоей странице и действует от имени пользователя. Angular экранирует интерполяцию и санитизирует [innerHTML], значения считаются недоверенными. Защита ломается при bypassSecurityTrust*, прямой работе с DOM (nativeElement.innerHTML) и при сборке шаблонов из строк.","origin":"authored"},{"id":"web-security-basics/csrf","topic":"web-security-basics","level":"junior","concept":"xss-csrf","q":"Что такое CSRF и что его закрывает?","a":"Чужой сайт заставляет браузер отправить запрос на твой API, и браузер прикладывает куки пользователя. Защита: куки с SameSite=Lax или Strict, CSRF-токен в заголовке, проверка Origin. HttpClient сам читает куку XSRF-TOKEN и шлёт заголовок X-XSRF-TOKEN на изменяющие запросы к своему origin.","origin":"authored"},{"id":"web-vitals/lighthouse","topic":"web-vitals","level":"junior","concept":"core-web-vitals","q":"Что показывает Lighthouse и почему его оценке нельзя верить на 100%?","a":"Lighthouse — лабораторный аудит: Performance, Accessibility, Best Practices, SEO, с подсказками. Он гоняет страницу на одной машине с эмуляцией сети, поэтому результат скачет и не равен опыту реальных пользователей. Реальность — полевые данные (CrUX, RUM).","origin":"authored"},{"id":"web-vitals/cwv-meaning","topic":"web-vitals","level":"middle","concept":"core-web-vitals","q":"Что измеряют LCP, INP, CLS и какие у них хорошие значения?","a":"LCP — когда показан самый крупный элемент первого экрана, хорошо ≤ 2,5 с. INP — задержка реакции на действия за весь визит, хорошо ≤ 200 мс. CLS — сумма неожиданных сдвигов разметки, хорошо ≤ 0,1. Смотрят 75-й перцентиль реальных пользователей.","origin":"authored"},{"id":"web-vitals/lcp-fix","topic":"web-vitals","level":"middle","concept":"core-web-vitals","q":"LCP-картинка грузится медленно. Что проверить?","a":"Найти LCP-элемент в Performance. Картинка должна быть в HTML сразу, а не появляться после JS. Не делать её lazy, дать fetchpriority=\"high\" или preload, правильный размер и формат (AVIF/WebP), CDN. В Angular NgOptimizedImage с priority делает это. Также проверить TTFB и блокирующий CSS.","origin":"authored"},{"id":"web-vitals/cls-fix","topic":"web-vitals","level":"middle","concept":"core-web-vitals","q":"Откуда берётся CLS и как его убрать?","a":"Элементы без размеров (картинки, iframe, реклама), контент, вставленный над уже видимым, подмена шрифтов. Лечение: width/height или aspect-ratio, зарезервированное место под динамику, скелетоны того же размера, font-display и preload шрифтов.","origin":"authored"},{"id":"web-vitals/ttfb","topic":"web-vitals","level":"middle","concept":"core-web-vitals","q":"Что входит в TTFB и как понять, где теряется время?","a":"Время от начала навигации до первого байта ответа: редиректы, DNS, соединение и TLS, ожидание сервера. Разбирают по слоям: попал ли запрос в кэш CDN, сколько думает сервер (Server-Timing), нет ли лишних редиректов. Хороший TTFB — до 0,8 с.","origin":"authored"},{"id":"web-vitals/code-splitting","topic":"web-vitals","level":"middle","concept":"bundle-optimization","q":"Как уменьшить начальный бандл Angular-приложения?","a":"Ленивые маршруты (loadComponent/loadChildren) и @defer для тяжёлых блоков. Не тянуть тяжёлые библиотеки в общий код: импортировать конкретные функции, искать лёгкие замены. Посмотреть состав бандла: ng build --stats-json и анализатор (например, esbuild bundle analyzer). Держать budgets в angular.json.","origin":"authored"},{"id":"web-vitals/tree-shaking","topic":"web-vitals","level":"middle","concept":"bundle-optimization","q":"Что такое tree shaking и что ему мешает?","a":"Удаление неиспользуемых экспортов при сборке. Работает на статических ES-импортах. Мешают CommonJS-модули, побочные эффекты при импорте (нет sideEffects: false), импорт всего пространства и динамический доступ к экспортам.","origin":"authored"},{"id":"web-vitals/critical-css","topic":"web-vitals","level":"middle","concept":"bundle-optimization","q":"Что такое critical CSS и делает ли это Angular?","a":"Стили, нужные для первого экрана, встраивают в HTML, остальные грузят асинхронно — CSS меньше блокирует отрисовку. Angular CLI умеет встраивать критический CSS в prod-сборке (опция optimization.styles.inlineCritical) — это особенно полезно при SSR и SSG.","origin":"authored"},{"id":"web-vitals/lhci","topic":"web-vitals","level":"middle","concept":"core-web-vitals","q":"Зачем Lighthouse CI и WebPageTest, если есть Lighthouse в DevTools?","a":"Lighthouse CI гоняет аудит на каждом PR, несколько раз (медиана), и роняет сборку при выходе за бюджет — регрессии видны до релиза. WebPageTest даёт подробный водопад загрузки, реальные устройства и локации, сравнение до и после — для разбора, а не для гейта.","origin":"authored"},{"id":"web-vitals/worker-wasm","topic":"web-vitals","level":"middle","concept":"webassembly","q":"Когда WebAssembly действительно ускоряет фронтенд?","a":"Для тяжёлых вычислений: обработка изображений и видео, кодеки, криптография, физика, готовые C/C++/Rust-библиотеки. Wasm не имеет прямого доступа к DOM и дорого обменивается данными с JS. Для обычного UI-кода выигрыша нет. Тяжёлую работу ещё и выносят в Web Worker, чтобы не блокировать главный поток.","origin":"authored"},{"id":"js-types/typeof-001","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof 42?","a":"number\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof 42","origin":"runtime"},{"id":"js-types/typeof-002","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof 4.2?","a":"number\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof 4.2","origin":"runtime"},{"id":"js-types/typeof-003","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof NaN?","a":"number\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof NaN","origin":"runtime"},{"id":"js-types/typeof-004","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof Infinity?","a":"number\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof Infinity","origin":"runtime"},{"id":"js-types/typeof-005","topic":"js-types","level":"junior","concept":"types","q":"Что вернёт typeof 10n?","a":"bigint\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof 10n","origin":"runtime"},{"id":"js-types/typeof-006","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof 'str'?","a":"string\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof 'str'","origin":"runtime"},{"id":"js-types/typeof-007","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof ''?","a":"string\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof ''","origin":"runtime"},{"id":"js-types/typeof-008","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof `tpl`?","a":"string\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof `tpl`","origin":"runtime"},{"id":"js-types/typeof-009","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof true?","a":"boolean\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof true","origin":"runtime"},{"id":"js-types/typeof-010","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof undefined?","a":"undefined\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof undefined","origin":"runtime"},{"id":"js-types/typeof-011","topic":"js-types","level":"junior","concept":"types","q":"Что вернёт typeof null?","a":"object\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof null","origin":"runtime"},{"id":"js-types/typeof-012","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof {}?","a":"object\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof {}","origin":"runtime"},{"id":"js-types/typeof-013","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof []?","a":"object\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof []","origin":"runtime"},{"id":"js-types/typeof-014","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof new Date()?","a":"object\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof new Date()","origin":"runtime"},{"id":"js-types/typeof-015","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof /re/?","a":"object\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof /re/","origin":"runtime"},{"id":"js-types/typeof-016","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof function () {}?","a":"function\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof function () {}","origin":"runtime"},{"id":"js-types/typeof-017","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof () => {}?","a":"function\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof () => {}","origin":"runtime"},{"id":"js-types/typeof-018","topic":"js-types","level":"junior","concept":"types","q":"Что вернёт typeof class A {}?","a":"function\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof class A {}","origin":"runtime"},{"id":"js-types/typeof-019","topic":"js-types","level":"junior","concept":"types","q":"Что вернёт typeof Symbol()?","a":"symbol\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof Symbol()","origin":"runtime"},{"id":"js-types/typeof-020","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof Symbol.iterator?","a":"symbol\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof Symbol.iterator","origin":"runtime"},{"id":"js-types/typeof-021","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof new Map()?","a":"object\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof new Map()","origin":"runtime"},{"id":"js-types/typeof-022","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof new Set()?","a":"object\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof new Set()","origin":"runtime"},{"id":"js-types/typeof-023","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof new WeakMap()?","a":"object\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof new WeakMap()","origin":"runtime"},{"id":"js-types/typeof-024","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof Math?","a":"object\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof Math","origin":"runtime"},{"id":"js-types/typeof-025","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof JSON?","a":"object\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof JSON","origin":"runtime"},{"id":"js-types/typeof-026","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof new Number(1)?","a":"object\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof new Number(1)","origin":"runtime"},{"id":"js-types/typeof-027","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof new String('s')?","a":"object\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof new String('s')","origin":"runtime"},{"id":"js-types/typeof-028","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof Number?","a":"function\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof Number","origin":"runtime"},{"id":"js-types/typeof-029","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof Object?","a":"function\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof Object","origin":"runtime"},{"id":"js-types/typeof-030","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof Array?","a":"function\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof Array","origin":"runtime"},{"id":"js-types/typeof-031","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof Promise.resolve()?","a":"object\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof Promise.resolve()","origin":"runtime"},{"id":"js-types/typeof-032","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof async () => {}?","a":"function\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof async () => {}","origin":"runtime"},{"id":"js-types/typeof-033","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof function* () {}?","a":"function\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof function* () {}","origin":"runtime"},{"id":"js-types/typeof-034","topic":"js-types","level":"intern","concept":"types","q":"Что вернёт typeof typeof 1?","a":"string\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof typeof 1","origin":"runtime"},{"id":"js-types/typeof-035","topic":"js-types","level":"junior","concept":"types","q":"Что вернёт typeof Object.create(null)?","a":"object\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof Object.create(null)","origin":"runtime"},{"id":"js-types/typeof-036","topic":"js-types","level":"junior","concept":"types","q":"Что вернёт typeof new Proxy({}, {})?","a":"object\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof new Proxy({}, {})","origin":"runtime"},{"id":"js-types/typeof-037","topic":"js-types","level":"junior","concept":"types","q":"Что вернёт typeof undeclaredVariable?","a":"undefined\n\ntypeof возвращает одну из 8 строк: undefined, boolean, number, bigint, string, symbol, function, object. Ловушки: null → \"object\" (старая ошибка языка), функции и классы → \"function\", NaN → \"number\", необъявленная переменная → \"undefined\" без ошибки.","code":"typeof undeclaredVariable","origin":"runtime"},{"id":"js-types/coercion-001","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно ' ' + true?","a":"' true'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"' ' + true","origin":"runtime"},{"id":"js-types/coercion-002","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [] * undefined?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[] * undefined","origin":"runtime"},{"id":"js-types/coercion-003","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] / 'a'?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] / 'a'","origin":"runtime"},{"id":"js-types/coercion-004","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '5' / 1?","a":"5\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'5' / 1","origin":"runtime"},{"id":"js-types/coercion-005","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно NaN - [1, 2]?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"NaN - [1, 2]","origin":"runtime"},{"id":"js-types/coercion-006","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [1, 2] / ' '?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[1, 2] / ' '","origin":"runtime"},{"id":"js-types/coercion-007","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно true + ''?","a":"'true'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"true + ''","origin":"runtime"},{"id":"js-types/coercion-008","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '0' - 'a'?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'0' - 'a'","origin":"runtime"},{"id":"js-types/coercion-009","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] + NaN?","a":"'3NaN'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] + NaN","origin":"runtime"},{"id":"js-types/coercion-010","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [1, 2] + NaN?","a":"'1,2NaN'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[1, 2] + NaN","origin":"runtime"},{"id":"js-types/coercion-011","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно false / false?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"false / false","origin":"runtime"},{"id":"js-types/coercion-012","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] - {}?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] - {}","origin":"runtime"},{"id":"js-types/coercion-013","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '' * true?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'' * true","origin":"runtime"},{"id":"js-types/coercion-014","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [1, 2] - undefined?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[1, 2] - undefined","origin":"runtime"},{"id":"js-types/coercion-015","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '' - undefined?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'' - undefined","origin":"runtime"},{"id":"js-types/coercion-016","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '0' - '0'?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'0' - '0'","origin":"runtime"},{"id":"js-types/coercion-017","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '' * 0?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'' * 0","origin":"runtime"},{"id":"js-types/coercion-018","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно {} * ' '?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"{} * ' '","origin":"runtime"},{"id":"js-types/coercion-019","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '' - 1?","a":"-1\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'' - 1","origin":"runtime"},{"id":"js-types/coercion-020","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно undefined / [3]?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"undefined / [3]","origin":"runtime"},{"id":"js-types/coercion-021","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [1, 2] + true?","a":"'1,2true'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[1, 2] + true","origin":"runtime"},{"id":"js-types/coercion-022","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] / {}?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] / {}","origin":"runtime"},{"id":"js-types/coercion-023","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно 'a' * true?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'a' * true","origin":"runtime"},{"id":"js-types/coercion-024","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [] * [3]?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[] * [3]","origin":"runtime"},{"id":"js-types/coercion-025","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 'a' * [3]?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'a' * [3]","origin":"runtime"},{"id":"js-types/coercion-026","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [] / 'a'?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[] / 'a'","origin":"runtime"},{"id":"js-types/coercion-027","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно ' ' - 0?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"' ' - 0","origin":"runtime"},{"id":"js-types/coercion-028","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно ' ' / '0'?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"' ' / '0'","origin":"runtime"},{"id":"js-types/coercion-029","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [1, 2] * [1, 2]?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[1, 2] * [1, 2]","origin":"runtime"},{"id":"js-types/coercion-030","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 'a' + [3]?","a":"'a3'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'a' + [3]","origin":"runtime"},{"id":"js-types/coercion-031","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null + ''?","a":"'null'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null + ''","origin":"runtime"},{"id":"js-types/coercion-032","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно 'a' - '5'?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'a' - '5'","origin":"runtime"},{"id":"js-types/coercion-033","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null - 1?","a":"-1\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null - 1","origin":"runtime"},{"id":"js-types/coercion-034","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [] * ''?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[] * ''","origin":"runtime"},{"id":"js-types/coercion-035","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно false / ''?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"false / ''","origin":"runtime"},{"id":"js-types/coercion-036","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно undefined - false?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"undefined - false","origin":"runtime"},{"id":"js-types/coercion-037","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно {} * '0'?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"{} * '0'","origin":"runtime"},{"id":"js-types/coercion-038","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 0 / undefined?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"0 / undefined","origin":"runtime"},{"id":"js-types/coercion-039","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 0 * undefined?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"0 * undefined","origin":"runtime"},{"id":"js-types/coercion-040","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно false * NaN?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"false * NaN","origin":"runtime"},{"id":"js-types/coercion-041","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null * undefined?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null * undefined","origin":"runtime"},{"id":"js-types/coercion-042","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно ' ' + ' '?","a":"'  '\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"' ' + ' '","origin":"runtime"},{"id":"js-types/coercion-043","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно 0 + false?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"0 + false","origin":"runtime"},{"id":"js-types/coercion-044","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '5' + [3]?","a":"'53'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'5' + [3]","origin":"runtime"},{"id":"js-types/coercion-045","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [] + undefined?","a":"'undefined'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[] + undefined","origin":"runtime"},{"id":"js-types/coercion-046","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно false / NaN?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"false / NaN","origin":"runtime"},{"id":"js-types/coercion-047","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно false / null?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"false / null","origin":"runtime"},{"id":"js-types/coercion-048","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно ' ' / 1?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"' ' / 1","origin":"runtime"},{"id":"js-types/coercion-049","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '0' - '5'?","a":"-5\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'0' - '5'","origin":"runtime"},{"id":"js-types/coercion-050","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 'a' - [3]?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'a' - [3]","origin":"runtime"},{"id":"js-types/coercion-051","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 0 * []?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"0 * []","origin":"runtime"},{"id":"js-types/coercion-052","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null / 'a'?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null / 'a'","origin":"runtime"},{"id":"js-types/coercion-053","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно false - NaN?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"false - NaN","origin":"runtime"},{"id":"js-types/coercion-054","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно true - 1?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"true - 1","origin":"runtime"},{"id":"js-types/coercion-055","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '' - ''?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'' - ''","origin":"runtime"},{"id":"js-types/coercion-056","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '0' + ' '?","a":"'0 '\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'0' + ' '","origin":"runtime"},{"id":"js-types/coercion-057","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [1, 2] - true?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[1, 2] - true","origin":"runtime"},{"id":"js-types/coercion-058","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '' + NaN?","a":"'NaN'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'' + NaN","origin":"runtime"},{"id":"js-types/coercion-059","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно true - true?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"true - true","origin":"runtime"},{"id":"js-types/coercion-060","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно false - false?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"false - false","origin":"runtime"},{"id":"js-types/coercion-061","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно 'a' * 1?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'a' * 1","origin":"runtime"},{"id":"js-types/coercion-062","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно false * '0'?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"false * '0'","origin":"runtime"},{"id":"js-types/coercion-063","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] + '0'?","a":"'30'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] + '0'","origin":"runtime"},{"id":"js-types/coercion-064","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null / '5'?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null / '5'","origin":"runtime"},{"id":"js-types/coercion-065","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно undefined + {}?","a":"'undefined[object Object]'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"undefined + {}","origin":"runtime"},{"id":"js-types/coercion-066","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно 0 / NaN?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"0 / NaN","origin":"runtime"},{"id":"js-types/coercion-067","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '0' / ''?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'0' / ''","origin":"runtime"},{"id":"js-types/coercion-068","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно 1 + false?","a":"1\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"1 + false","origin":"runtime"},{"id":"js-types/coercion-069","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 'a' * null?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'a' * null","origin":"runtime"},{"id":"js-types/coercion-070","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] + [1, 2]?","a":"'31,2'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] + [1, 2]","origin":"runtime"},{"id":"js-types/coercion-071","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [] + 0?","a":"'0'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[] + 0","origin":"runtime"},{"id":"js-types/coercion-072","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно NaN / undefined?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"NaN / undefined","origin":"runtime"},{"id":"js-types/coercion-073","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 0 + undefined?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"0 + undefined","origin":"runtime"},{"id":"js-types/coercion-074","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '5' + null?","a":"'5null'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'5' + null","origin":"runtime"},{"id":"js-types/coercion-075","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null + undefined?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null + undefined","origin":"runtime"},{"id":"js-types/coercion-076","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '0' * null?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'0' * null","origin":"runtime"},{"id":"js-types/coercion-077","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно 0 - ''?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"0 - ''","origin":"runtime"},{"id":"js-types/coercion-078","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно ' ' - true?","a":"-1\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"' ' - true","origin":"runtime"},{"id":"js-types/coercion-079","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно 'a' - ' '?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'a' - ' '","origin":"runtime"},{"id":"js-types/coercion-080","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно {} + []?","a":"'[object Object]'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"{} + []","origin":"runtime"},{"id":"js-types/coercion-081","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [1, 2] * ' '?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[1, 2] * ' '","origin":"runtime"},{"id":"js-types/coercion-082","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '5' / '5'?","a":"1\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'5' / '5'","origin":"runtime"},{"id":"js-types/coercion-083","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] * []?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] * []","origin":"runtime"},{"id":"js-types/coercion-084","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно ' ' / '5'?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"' ' / '5'","origin":"runtime"},{"id":"js-types/coercion-085","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно {} + 0?","a":"'[object Object]0'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"{} + 0","origin":"runtime"},{"id":"js-types/coercion-086","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно undefined / []?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"undefined / []","origin":"runtime"},{"id":"js-types/coercion-087","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null * 'a'?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null * 'a'","origin":"runtime"},{"id":"js-types/coercion-088","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '0' + undefined?","a":"'0undefined'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'0' + undefined","origin":"runtime"},{"id":"js-types/coercion-089","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно ' ' - []?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"' ' - []","origin":"runtime"},{"id":"js-types/coercion-090","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно true - undefined?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"true - undefined","origin":"runtime"},{"id":"js-types/coercion-091","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '0' - undefined?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'0' - undefined","origin":"runtime"},{"id":"js-types/coercion-092","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно ' ' - [1, 2]?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"' ' - [1, 2]","origin":"runtime"},{"id":"js-types/coercion-093","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] * undefined?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] * undefined","origin":"runtime"},{"id":"js-types/coercion-094","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно {} + ''?","a":"'[object Object]'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"{} + ''","origin":"runtime"},{"id":"js-types/coercion-095","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null / 1?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null / 1","origin":"runtime"},{"id":"js-types/coercion-096","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '0' - NaN?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'0' - NaN","origin":"runtime"},{"id":"js-types/coercion-097","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно undefined + 1?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"undefined + 1","origin":"runtime"},{"id":"js-types/coercion-098","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно NaN + null?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"NaN + null","origin":"runtime"},{"id":"js-types/coercion-099","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '' - '5'?","a":"-5\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'' - '5'","origin":"runtime"},{"id":"js-types/coercion-100","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '' + 'a'?","a":"'a'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'' + 'a'","origin":"runtime"},{"id":"js-types/coercion-101","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] / ''?","a":"Infinity\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] / ''","origin":"runtime"},{"id":"js-types/coercion-102","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '' * false?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'' * false","origin":"runtime"},{"id":"js-types/coercion-103","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [] / true?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[] / true","origin":"runtime"},{"id":"js-types/coercion-104","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null - true?","a":"-1\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null - true","origin":"runtime"},{"id":"js-types/coercion-105","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null * ' '?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null * ' '","origin":"runtime"},{"id":"js-types/coercion-106","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '5' * [3]?","a":"15\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'5' * [3]","origin":"runtime"},{"id":"js-types/coercion-107","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '0' * undefined?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'0' * undefined","origin":"runtime"},{"id":"js-types/coercion-108","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно true / '5'?","a":"0.2\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"true / '5'","origin":"runtime"},{"id":"js-types/coercion-109","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [1, 2] / 1?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[1, 2] / 1","origin":"runtime"},{"id":"js-types/coercion-110","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно true - NaN?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"true - NaN","origin":"runtime"},{"id":"js-types/coercion-111","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно true - 0?","a":"1\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"true - 0","origin":"runtime"},{"id":"js-types/coercion-112","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно ' ' * [3]?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"' ' * [3]","origin":"runtime"},{"id":"js-types/coercion-113","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [] - [3]?","a":"-3\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[] - [3]","origin":"runtime"},{"id":"js-types/coercion-114","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] - 1?","a":"2\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] - 1","origin":"runtime"},{"id":"js-types/coercion-115","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно NaN + ' '?","a":"'NaN '\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"NaN + ' '","origin":"runtime"},{"id":"js-types/coercion-116","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно false + 0?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"false + 0","origin":"runtime"},{"id":"js-types/coercion-117","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [1, 2] - '0'?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[1, 2] - '0'","origin":"runtime"},{"id":"js-types/coercion-118","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] - 'a'?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] - 'a'","origin":"runtime"},{"id":"js-types/coercion-119","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно '5' - 0?","a":"5\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'5' - 0","origin":"runtime"},{"id":"js-types/coercion-120","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null / true?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null / true","origin":"runtime"},{"id":"js-types/coercion-121","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно NaN * [1, 2]?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"NaN * [1, 2]","origin":"runtime"},{"id":"js-types/coercion-122","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно false - [3]?","a":"-3\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"false - [3]","origin":"runtime"},{"id":"js-types/coercion-123","topic":"js-types","level":"intern","concept":"coercion","q":"Чему равно 0 / ''?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"0 / ''","origin":"runtime"},{"id":"js-types/coercion-124","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [1, 2] + '5'?","a":"'1,25'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[1, 2] + '5'","origin":"runtime"},{"id":"js-types/coercion-125","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null / 0?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null / 0","origin":"runtime"},{"id":"js-types/coercion-126","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 1 / [1, 2]?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"1 / [1, 2]","origin":"runtime"},{"id":"js-types/coercion-127","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно true / [1, 2]?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"true / [1, 2]","origin":"runtime"},{"id":"js-types/coercion-128","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '5' * undefined?","a":"NaN\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'5' * undefined","origin":"runtime"},{"id":"js-types/coercion-129","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '0' - []?","a":"0\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'0' - []","origin":"runtime"},{"id":"js-types/coercion-130","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [] + 'a'?","a":"'a'\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[] + 'a'","origin":"runtime"},{"id":"js-types/equality-001","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '5' === [1, 2]?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'5' === [1, 2]","origin":"runtime"},{"id":"js-types/equality-002","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [1, 2] === ' '?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[1, 2] === ' '","origin":"runtime"},{"id":"js-types/equality-003","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '' == ''?","a":"true\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'' == ''","origin":"runtime"},{"id":"js-types/equality-004","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 1 == 0?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"1 == 0","origin":"runtime"},{"id":"js-types/equality-005","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 0 == ' '?","a":"true\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"0 == ' '","origin":"runtime"},{"id":"js-types/equality-006","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 1 == 'a'?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"1 == 'a'","origin":"runtime"},{"id":"js-types/equality-007","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно undefined == [1, 2]?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"undefined == [1, 2]","origin":"runtime"},{"id":"js-types/equality-008","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно true === ' '?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"true === ' '","origin":"runtime"},{"id":"js-types/equality-009","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 'a' == '0'?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'a' == '0'","origin":"runtime"},{"id":"js-types/equality-010","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '' == 0?","a":"true\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'' == 0","origin":"runtime"},{"id":"js-types/equality-011","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [] === [3]?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[] === [3]","origin":"runtime"},{"id":"js-types/equality-012","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [] == NaN?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[] == NaN","origin":"runtime"},{"id":"js-types/equality-013","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null == ' '?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null == ' '","origin":"runtime"},{"id":"js-types/equality-014","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 'a' == 'a'?","a":"true\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'a' == 'a'","origin":"runtime"},{"id":"js-types/equality-015","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] == NaN?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] == NaN","origin":"runtime"},{"id":"js-types/equality-016","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null == undefined?","a":"true\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null == undefined","origin":"runtime"},{"id":"js-types/equality-017","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 'a' === true?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'a' === true","origin":"runtime"},{"id":"js-types/equality-018","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 1 == '0'?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"1 == '0'","origin":"runtime"},{"id":"js-types/equality-019","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '5' == undefined?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'5' == undefined","origin":"runtime"},{"id":"js-types/equality-020","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно true == [3]?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"true == [3]","origin":"runtime"},{"id":"js-types/equality-021","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно false == NaN?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"false == NaN","origin":"runtime"},{"id":"js-types/equality-022","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно false == ' '?","a":"true\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"false == ' '","origin":"runtime"},{"id":"js-types/equality-023","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] === ' '?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] === ' '","origin":"runtime"},{"id":"js-types/equality-024","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [1, 2] == '0'?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[1, 2] == '0'","origin":"runtime"},{"id":"js-types/equality-025","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно true === NaN?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"true === NaN","origin":"runtime"},{"id":"js-types/equality-026","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно undefined == 0?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"undefined == 0","origin":"runtime"},{"id":"js-types/equality-027","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] == ' '?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] == ' '","origin":"runtime"},{"id":"js-types/equality-028","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно true == [1, 2]?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"true == [1, 2]","origin":"runtime"},{"id":"js-types/equality-029","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 1 == [1, 2]?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"1 == [1, 2]","origin":"runtime"},{"id":"js-types/equality-030","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] == [3]?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] == [3]","origin":"runtime"},{"id":"js-types/equality-031","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 1 === 1?","a":"true\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"1 === 1","origin":"runtime"},{"id":"js-types/equality-032","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно false == [3]?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"false == [3]","origin":"runtime"},{"id":"js-types/equality-033","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [1, 2] == ' '?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[1, 2] == ' '","origin":"runtime"},{"id":"js-types/equality-034","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно {} == [3]?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"{} == [3]","origin":"runtime"},{"id":"js-types/equality-035","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно {} === '0'?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"{} === '0'","origin":"runtime"},{"id":"js-types/equality-036","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 'a' == undefined?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'a' == undefined","origin":"runtime"},{"id":"js-types/equality-037","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно NaN === ' '?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"NaN === ' '","origin":"runtime"},{"id":"js-types/equality-038","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 1 == ''?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"1 == ''","origin":"runtime"},{"id":"js-types/equality-039","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '5' == []?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'5' == []","origin":"runtime"},{"id":"js-types/equality-040","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 1 == '5'?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"1 == '5'","origin":"runtime"},{"id":"js-types/equality-041","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null == NaN?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null == NaN","origin":"runtime"},{"id":"js-types/equality-042","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно null === null?","a":"true\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"null === null","origin":"runtime"},{"id":"js-types/equality-043","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 'a' == null?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'a' == null","origin":"runtime"},{"id":"js-types/equality-044","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно true == ''?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"true == ''","origin":"runtime"},{"id":"js-types/equality-045","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно false == undefined?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"false == undefined","origin":"runtime"},{"id":"js-types/equality-046","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 1 == false?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"1 == false","origin":"runtime"},{"id":"js-types/equality-047","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [3] == '0'?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[3] == '0'","origin":"runtime"},{"id":"js-types/equality-048","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно NaN == '0'?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"NaN == '0'","origin":"runtime"},{"id":"js-types/equality-049","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [] == [1, 2]?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[] == [1, 2]","origin":"runtime"},{"id":"js-types/equality-050","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 'a' === {}?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'a' === {}","origin":"runtime"},{"id":"js-types/equality-051","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно undefined == {}?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"undefined == {}","origin":"runtime"},{"id":"js-types/equality-052","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно true == {}?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"true == {}","origin":"runtime"},{"id":"js-types/equality-053","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно {} == ''?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"{} == ''","origin":"runtime"},{"id":"js-types/equality-054","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно [] == 0?","a":"true\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"[] == 0","origin":"runtime"},{"id":"js-types/equality-055","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '5' === undefined?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'5' === undefined","origin":"runtime"},{"id":"js-types/equality-056","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно '5' == ''?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"'5' == ''","origin":"runtime"},{"id":"js-types/equality-057","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 1 == ' '?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"1 == ' '","origin":"runtime"},{"id":"js-types/equality-058","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно undefined === []?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"undefined === []","origin":"runtime"},{"id":"js-types/equality-059","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно 1 == {}?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"1 == {}","origin":"runtime"},{"id":"js-types/equality-060","topic":"js-types","level":"junior","concept":"coercion","q":"Чему равно false == [1, 2]?","a":"false\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"false == [1, 2]","origin":"runtime"},{"id":"js-types/truthy-001","topic":"js-types","level":"intern","concept":"coercion","q":"Значение 0 — truthy или falsy?","a":"falsy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(0)","origin":"runtime"},{"id":"js-types/truthy-002","topic":"js-types","level":"intern","concept":"coercion","q":"Значение -0 — truthy или falsy?","a":"falsy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(-0)","origin":"runtime"},{"id":"js-types/truthy-003","topic":"js-types","level":"intern","concept":"coercion","q":"Значение 0n — truthy или falsy?","a":"falsy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(0n)","origin":"runtime"},{"id":"js-types/truthy-004","topic":"js-types","level":"intern","concept":"coercion","q":"Значение '' — truthy или falsy?","a":"falsy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean('')","origin":"runtime"},{"id":"js-types/truthy-005","topic":"js-types","level":"intern","concept":"coercion","q":"Значение ' ' — truthy или falsy?","a":"truthy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(' ')","origin":"runtime"},{"id":"js-types/truthy-006","topic":"js-types","level":"intern","concept":"coercion","q":"Значение '0' — truthy или falsy?","a":"truthy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean('0')","origin":"runtime"},{"id":"js-types/truthy-007","topic":"js-types","level":"intern","concept":"coercion","q":"Значение 'false' — truthy или falsy?","a":"truthy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean('false')","origin":"runtime"},{"id":"js-types/truthy-008","topic":"js-types","level":"intern","concept":"coercion","q":"Значение NaN — truthy или falsy?","a":"falsy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(NaN)","origin":"runtime"},{"id":"js-types/truthy-009","topic":"js-types","level":"intern","concept":"coercion","q":"Значение null — truthy или falsy?","a":"falsy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(null)","origin":"runtime"},{"id":"js-types/truthy-010","topic":"js-types","level":"intern","concept":"coercion","q":"Значение undefined — truthy или falsy?","a":"falsy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(undefined)","origin":"runtime"},{"id":"js-types/truthy-011","topic":"js-types","level":"intern","concept":"coercion","q":"Значение [] — truthy или falsy?","a":"truthy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean([])","origin":"runtime"},{"id":"js-types/truthy-012","topic":"js-types","level":"intern","concept":"coercion","q":"Значение {} — truthy или falsy?","a":"truthy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean({})","origin":"runtime"},{"id":"js-types/truthy-013","topic":"js-types","level":"intern","concept":"coercion","q":"Значение [0] — truthy или falsy?","a":"truthy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean([0])","origin":"runtime"},{"id":"js-types/truthy-014","topic":"js-types","level":"intern","concept":"coercion","q":"Значение [''] — truthy или falsy?","a":"truthy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean([''])","origin":"runtime"},{"id":"js-types/truthy-015","topic":"js-types","level":"intern","concept":"coercion","q":"Значение function () {} — truthy или falsy?","a":"truthy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(function () {})","origin":"runtime"},{"id":"js-types/truthy-016","topic":"js-types","level":"intern","concept":"coercion","q":"Значение Infinity — truthy или falsy?","a":"truthy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(Infinity)","origin":"runtime"},{"id":"js-types/truthy-017","topic":"js-types","level":"intern","concept":"coercion","q":"Значение -1 — truthy или falsy?","a":"truthy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(-1)","origin":"runtime"},{"id":"js-types/truthy-018","topic":"js-types","level":"junior","concept":"coercion","q":"Значение new Boolean(false) — truthy или falsy?","a":"truthy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(new Boolean(false))","origin":"runtime"},{"id":"js-types/truthy-019","topic":"js-types","level":"junior","concept":"coercion","q":"Значение Symbol() — truthy или falsy?","a":"truthy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(Symbol())","origin":"runtime"},{"id":"js-types/truthy-020","topic":"js-types","level":"intern","concept":"coercion","q":"Значение 0.0 — truthy или falsy?","a":"falsy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(0.0)","origin":"runtime"},{"id":"js-types/truthy-021","topic":"js-types","level":"intern","concept":"coercion","q":"Значение 1n — truthy или falsy?","a":"truthy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(1n)","origin":"runtime"},{"id":"js-types/truthy-022","topic":"js-types","level":"junior","concept":"coercion","q":"Значение new Number(0) — truthy или falsy?","a":"truthy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(new Number(0))","origin":"runtime"},{"id":"js-types/truthy-023","topic":"js-types","level":"junior","concept":"coercion","q":"Значение new String('') — truthy или falsy?","a":"truthy\n\nБинарный + со строкой → конкатенация; -, *, / → ToNumber. Объекты сначала приводятся к примитиву (valueOf → toString). == приводит типы, === — нет; null == undefined, но ни один из них не == 0.","code":"Boolean(new String(''))","origin":"runtime"},{"id":"js-data/nullish-001","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно 0 || 'x'?","a":"'x'\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"0 || 'x'","origin":"runtime"},{"id":"js-data/nullish-002","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно 0 ?? 'x'?","a":"0\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"0 ?? 'x'","origin":"runtime"},{"id":"js-data/nullish-003","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно 0 && 'x'?","a":"0\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"0 && 'x'","origin":"runtime"},{"id":"js-data/nullish-004","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно '' || 'x'?","a":"'x'\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"'' || 'x'","origin":"runtime"},{"id":"js-data/nullish-005","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно '' ?? 'x'?","a":"''\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"'' ?? 'x'","origin":"runtime"},{"id":"js-data/nullish-006","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно '' && 'x'?","a":"''\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"'' && 'x'","origin":"runtime"},{"id":"js-data/nullish-007","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно null || 'x'?","a":"'x'\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"null || 'x'","origin":"runtime"},{"id":"js-data/nullish-008","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно null ?? 'x'?","a":"'x'\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"null ?? 'x'","origin":"runtime"},{"id":"js-data/nullish-009","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно null && 'x'?","a":"null\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"null && 'x'","origin":"runtime"},{"id":"js-data/nullish-010","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно undefined || 'x'?","a":"'x'\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"undefined || 'x'","origin":"runtime"},{"id":"js-data/nullish-011","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно undefined ?? 'x'?","a":"'x'\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"undefined ?? 'x'","origin":"runtime"},{"id":"js-data/nullish-012","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно undefined && 'x'?","a":"undefined\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"undefined && 'x'","origin":"runtime"},{"id":"js-data/nullish-013","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно false || 'x'?","a":"'x'\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"false || 'x'","origin":"runtime"},{"id":"js-data/nullish-014","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно false ?? 'x'?","a":"false\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"false ?? 'x'","origin":"runtime"},{"id":"js-data/nullish-015","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно false && 'x'?","a":"false\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"false && 'x'","origin":"runtime"},{"id":"js-data/nullish-016","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно NaN || 'x'?","a":"'x'\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"NaN || 'x'","origin":"runtime"},{"id":"js-data/nullish-017","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно NaN ?? 'x'?","a":"NaN\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"NaN ?? 'x'","origin":"runtime"},{"id":"js-data/nullish-018","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно NaN && 'x'?","a":"NaN\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"NaN && 'x'","origin":"runtime"},{"id":"js-data/nullish-019","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно 'a' || 'x'?","a":"'a'\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"'a' || 'x'","origin":"runtime"},{"id":"js-data/nullish-020","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно 'a' ?? 'x'?","a":"'a'\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"'a' ?? 'x'","origin":"runtime"},{"id":"js-data/nullish-021","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно 'a' && 'x'?","a":"'x'\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"'a' && 'x'","origin":"runtime"},{"id":"js-data/nullish-022","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно [] || 'x'?","a":"[]\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"[] || 'x'","origin":"runtime"},{"id":"js-data/nullish-023","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно [] ?? 'x'?","a":"[]\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"[] ?? 'x'","origin":"runtime"},{"id":"js-data/nullish-024","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно [] && 'x'?","a":"'x'\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"[] && 'x'","origin":"runtime"},{"id":"js-data/nullish-025","topic":"js-data","level":"junior","concept":"modern-syntax","q":"Чему равно null?.a?","a":"undefined\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"null?.a","origin":"runtime"},{"id":"js-data/nullish-026","topic":"js-data","level":"junior","concept":"modern-syntax","q":"Чему равно undefined?.a.b.c?","a":"undefined\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"undefined?.a.b.c","origin":"runtime"},{"id":"js-data/nullish-027","topic":"js-data","level":"junior","concept":"modern-syntax","q":"Чему равно ({ a: null }).a?.b?","a":"undefined\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"({ a: null }).a?.b","origin":"runtime"},{"id":"js-data/nullish-028","topic":"js-data","level":"junior","concept":"modern-syntax","q":"Чему равно ({ a: { b: 0 } }).a?.b ?? 5?","a":"0\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"({ a: { b: 0 } }).a?.b ?? 5","origin":"runtime"},{"id":"js-data/nullish-029","topic":"js-data","level":"junior","concept":"modern-syntax","q":"Чему равно ({ a: { b: 0 } }).a?.b || 5?","a":"5\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"({ a: { b: 0 } }).a?.b || 5","origin":"runtime"},{"id":"js-data/nullish-030","topic":"js-data","level":"junior","concept":"modern-syntax","q":"Чему равно ''?.length?","a":"0\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"''?.length","origin":"runtime"},{"id":"js-data/nullish-031","topic":"js-data","level":"junior","concept":"modern-syntax","q":"Чему равно [1, 2]?.[5]?.x?","a":"undefined\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"[1, 2]?.[5]?.x","origin":"runtime"},{"id":"js-data/nullish-032","topic":"js-data","level":"junior","concept":"modern-syntax","q":"Чему равно ({ f: null }).f?.()?","a":"undefined\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"({ f: null }).f?.()","origin":"runtime"},{"id":"js-data/nullish-033","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно (null || undefined) ?? 0?","a":"0\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"(null || undefined) ?? 0","origin":"runtime"},{"id":"js-data/nullish-034","topic":"js-data","level":"intern","concept":"modern-syntax","q":"Чему равно (0 ?? 1) || 2?","a":"2\n\n?? берёт правую часть только для null и undefined; || — для любого ложного значения (0, \"\", false, NaN, null, undefined); && возвращает первое ложное значение или последнее. ?. даёт undefined, если слева null или undefined, и не вычисляет остаток цепочки.","code":"(0 ?? 1) || 2","origin":"runtime"},{"id":"js-data/array-001","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли push исходный массив?","a":"5\n[ 3, 1, 10, 2, 4 ]\n→ исходный массив изменён (мутация)","code":"const a = [3, 1, 10, 2];\nconst r = a.push(4);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-002","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли pop исходный массив?","a":"2\n[ 3, 1, 10 ]\n→ исходный массив изменён (мутация)","code":"const a = [3, 1, 10, 2];\nconst r = a.pop();\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-003","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли shift исходный массив?","a":"3\n[ 1, 10, 2 ]\n→ исходный массив изменён (мутация)","code":"const a = [3, 1, 10, 2];\nconst r = a.shift();\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-004","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли unshift исходный массив?","a":"5\n[ 0, 3, 1, 10, 2 ]\n→ исходный массив изменён (мутация)","code":"const a = [3, 1, 10, 2];\nconst r = a.unshift(0);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-005","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли splice исходный массив?","a":"[ 1, 10 ]\n[ 3, 2 ]\n→ исходный массив изменён (мутация)","code":"const a = [3, 1, 10, 2];\nconst r = a.splice(1, 2);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-006","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли slice исходный массив?","a":"[ 1, 10 ]\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.slice(1, 3);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-007","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли sort исходный массив?","a":"[ 1, 10, 2, 3 ]\n[ 1, 10, 2, 3 ]\n→ исходный массив изменён (мутация)","code":"const a = [3, 1, 10, 2];\nconst r = a.sort();\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-008","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли sort исходный массив?","a":"[ 1, 2, 3, 10 ]\n[ 1, 2, 3, 10 ]\n→ исходный массив изменён (мутация)","code":"const a = [3, 1, 10, 2];\nconst r = a.sort((x, y) => x - y);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-009","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли toSorted исходный массив?","a":"[ 1, 10, 2, 3 ]\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.toSorted();\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-010","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли reverse исходный массив?","a":"[ 2, 10, 1, 3 ]\n[ 2, 10, 1, 3 ]\n→ исходный массив изменён (мутация)","code":"const a = [3, 1, 10, 2];\nconst r = a.reverse();\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-011","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли toReversed исходный массив?","a":"[ 2, 10, 1, 3 ]\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.toReversed();\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-012","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли map исходный массив?","a":"[ 6, 2, 20, 4 ]\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.map((x) => x * 2);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-013","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли filter исходный массив?","a":"[ 3, 10 ]\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.filter((x) => x > 2);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-014","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли forEach исходный массив?","a":"undefined\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.forEach((x) => x * 2);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-015","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли reduce исходный массив?","a":"16\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.reduce((s, x) => s + x, 0);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-016","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли reduce исходный массив?","a":"16\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.reduce((s, x) => s + x);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-017","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли find исходный массив?","a":"3\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.find((x) => x > 2);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-018","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли findIndex исходный массив?","a":"-1\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.findIndex((x) => x > 100);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-019","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли findLast исходный массив?","a":"2\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.findLast((x) => x > 1);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-020","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли some исходный массив?","a":"true\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.some((x) => x > 5);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-021","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли every исходный массив?","a":"true\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.every((x) => x > 0);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-022","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли fill исходный массив?","a":"[ 0, 0, 0, 0 ]\n[ 0, 0, 0, 0 ]\n→ исходный массив изменён (мутация)","code":"const a = [3, 1, 10, 2];\nconst r = a.fill(0);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-023","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли fill исходный массив?","a":"[ 3, 7, 7, 2 ]\n[ 3, 7, 7, 2 ]\n→ исходный массив изменён (мутация)","code":"const a = [3, 1, 10, 2];\nconst r = a.fill(7, 1, 3);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-024","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли concat исходный массив?","a":"[ 3, 1, 10, 2, 9, 8 ]\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.concat([9], 8);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-025","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли includes исходный массив?","a":"true\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.includes(10);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-026","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли indexOf исходный массив?","a":"-1\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.indexOf('10');\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-027","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли at исходный массив?","a":"2\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.at(-1);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-028","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли with исходный массив?","a":"[ 99, 1, 10, 2 ]\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.with(0, 99);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-029","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли toSpliced исходный массив?","a":"[ 1, 10, 2 ]\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.toSpliced(0, 1);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-030","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли copyWithin исходный массив?","a":"[ 10, 2, 10, 2 ]\n[ 10, 2, 10, 2 ]\n→ исходный массив изменён (мутация)","code":"const a = [3, 1, 10, 2];\nconst r = a.copyWithin(0, 2);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-031","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли join исходный массив?","a":"3-1-10-2\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.join('-');\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-032","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли flatMap исходный массив?","a":"[\n   3,  3, 1, 1,\n  10, 10, 2, 2\n]\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.flatMap((x) => [x, x]);\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-033","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли keys исходный массив?","a":"{ value: 0, done: false }\n[ 3, 1, 10, 2 ]\n→ исходный массив не изменён","code":"const a = [3, 1, 10, 2];\nconst r = a.keys().next();\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/array-034","topic":"js-data","level":"junior","concept":"array-methods","q":"Что выведет код? Мутирует ли length = 2 исходный массив?","a":"2\n[ 3, 1 ]\n→ исходный массив изменён (мутация)","code":"const a = [3, 1, 10, 2];\nconst r = a.length = 2;\nconsole.log(r);\nconsole.log(a);","origin":"runtime"},{"id":"js-data/destructuring-001","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"1 2\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"const { a, b = 2 } = { a: 1, b: undefined };\nconsole.log(a, b);","origin":"runtime"},{"id":"js-data/destructuring-002","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"1 null\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"const { a, b = 2 } = { a: 1, b: null };\nconsole.log(a, b);","origin":"runtime"},{"id":"js-data/destructuring-003","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"1 5\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"const [x, , y = 5] = [1, 2];\nconsole.log(x, y);","origin":"runtime"},{"id":"js-data/destructuring-004","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"h [ 'e', 'y' ]\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"const [first, ...rest] = 'hey';\nconsole.log(first, rest);","origin":"runtime"},{"id":"js-data/destructuring-005","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"3\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"const { length } = [1, 2, 3];\nconsole.log(length);","origin":"runtime"},{"id":"js-data/destructuring-006","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"42\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"const { a: { b } } = { a: { b: 42 } };\nconsole.log(b);","origin":"runtime"},{"id":"js-data/destructuring-007","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"2 1\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"let a = 1, b = 2;\n[a, b] = [b, a];\nconsole.log(a, b);","origin":"runtime"},{"id":"js-data/destructuring-008","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"{ y: 2 }\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"const o = { x: 1, y: 2 };\nconst { x, ...others } = o;\nconsole.log(others);","origin":"runtime"},{"id":"js-data/destructuring-009","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"{ a: 1, b: 3, c: 4 }\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"const merged = { a: 1, b: 2, ...{ b: 3, c: 4 } };\nconsole.log(merged);","origin":"runtime"},{"id":"js-data/destructuring-010","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"{ b: 2, c: 4, a: 1 }\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"const merged = { ...{ b: 3, c: 4 }, a: 1, b: 2 };\nconsole.log(merged);","origin":"runtime"},{"id":"js-data/destructuring-011","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"[ 'a', 'b', 'c', 1, 2 ]\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"const arr = [...'abc', ...[1, 2]];\nconsole.log(arr);","origin":"runtime"},{"id":"js-data/destructuring-012","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"{ '0': 'h', '1': 'i' }\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"const o = { ...null, ...undefined, ...'hi' };\nconsole.log(o);","origin":"runtime"},{"id":"js-data/destructuring-013","topic":"js-data","level":"junior","concept":"copying","q":"Что выведет код?","a":"2\n\nSpread, Object.assign и slice копируют один уровень: вложенные объекты остаются общими. structuredClone копирует глубоко (Date, Map, Set, циклы), но на функции бросает DataCloneError. JSON.parse(JSON.stringify(x)) теряет undefined и функции, а Date превращает в строку.","code":"const inner = { n: 1 };\nconst copy = { ...{ inner } };\ncopy.inner.n = 2;\nconsole.log(inner.n);","origin":"runtime"},{"id":"js-data/destructuring-014","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"2 1\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"function f(a, ...rest) { return rest.length; }\nconsole.log(f(1, 2, 3), f.length);","origin":"runtime"},{"id":"js-data/destructuring-015","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"[ 1, undefined ] [ 1, 2 ]\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"function f({ a = 1, b } = {}) { return [a, b]; }\nconsole.log(f(), f({ b: 2 }));","origin":"runtime"},{"id":"js-data/destructuring-016","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"dyn\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"const { ['k' + 1]: v } = { k1: 'dyn' };\nconsole.log(v);","origin":"runtime"},{"id":"js-data/destructuring-017","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"5 -Infinity\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"console.log(Math.max(...[1, 5, 3]), Math.max(...[]));","origin":"runtime"},{"id":"js-data/destructuring-018","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"1\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"const [a = console.log('lazy') ?? 0] = [1];\nconsole.log(a);","origin":"runtime"},{"id":"js-data/destructuring-019","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"2\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"const s = new Set([1, 1, 2]);\nconsole.log([...s].length);","origin":"runtime"},{"id":"js-data/destructuring-020","topic":"js-data","level":"junior","concept":"spread-destructuring","q":"Что выведет код?","a":"a 1\n\nЗначение по умолчанию срабатывает только для undefined, не для null. В spread объектов побеждает свойство, записанное позже. rest собирает остаток в новый массив или объект.","code":"const m = new Map([['a', 1]]);\nconst [[k, v]] = m;\nconsole.log(k, v);","origin":"runtime"},{"id":"js-data/destructuring-021","topic":"js-data","level":"junior","concept":"copying","q":"Что выведет код?","a":"true 2\n\nSpread, Object.assign и slice копируют один уровень: вложенные объекты остаются общими. structuredClone копирует глубоко (Date, Map, Set, циклы), но на функции бросает DataCloneError. JSON.parse(JSON.stringify(x)) теряет undefined и функции, а Date превращает в строку.","code":"const copy = structuredClone({ d: new Date(0), m: new Map([[1, 2]]) });\nconsole.log(copy.d instanceof Date, copy.m.get(1));","origin":"runtime"},{"id":"js-data/destructuring-022","topic":"js-data","level":"junior","concept":"copying","q":"Что выведет код?","a":"{ d: '1970-01-01T00:00:00.000Z', n: null }\n\nSpread, Object.assign и slice копируют один уровень: вложенные объекты остаются общими. structuredClone копирует глубоко (Date, Map, Set, циклы), но на функции бросает DataCloneError. JSON.parse(JSON.stringify(x)) теряет undefined и функции, а Date превращает в строку.","code":"const copy = JSON.parse(JSON.stringify({ d: new Date(0), u: undefined, n: NaN, f() {} }));\nconsole.log(copy);","origin":"runtime"},{"id":"js-data/destructuring-023","topic":"js-data","level":"junior","concept":"copying","q":"Что выведет код?","a":"DataCloneError\n\nSpread, Object.assign и slice копируют один уровень: вложенные объекты остаются общими. structuredClone копирует глубоко (Date, Map, Set, циклы), но на функции бросает DataCloneError. JSON.parse(JSON.stringify(x)) теряет undefined и функции, а Date превращает в строку.","code":"try { structuredClone({ f() {} }); } catch (e) { console.log(e.name); }","origin":"runtime"},{"id":"js-data/destructuring-024","topic":"js-data","level":"junior","concept":"copying","q":"Что выведет код?","a":"2\n\nSpread, Object.assign и slice копируют один уровень: вложенные объекты остаются общими. structuredClone копирует глубоко (Date, Map, Set, циклы), но на функции бросает DataCloneError. JSON.parse(JSON.stringify(x)) теряет undefined и функции, а Date превращает в строку.","code":"const a = { n: { deep: 1 } };\nconst b = Object.assign({}, a);\nb.n.deep = 2;\nconsole.log(a.n.deep);","origin":"runtime"},{"id":"js-scope/scope-001","topic":"js-scope","level":"junior","concept":"closures","q":"Что выведет код?","a":"3\n3\n3\n\nЗамыкание хранит ссылку на переменную, а не снимок значения. var в цикле — одна переменная на все итерации; let создаёт новую привязку на каждой итерации.","code":"for (var i = 0; i < 3; i++) setTimeout(() => console.log(i));","origin":"runtime"},{"id":"js-scope/scope-002","topic":"js-scope","level":"junior","concept":"closures","q":"Что выведет код?","a":"0\n1\n2\n\nЗамыкание хранит ссылку на переменную, а не снимок значения. var в цикле — одна переменная на все итерации; let создаёт новую привязку на каждой итерации.","code":"for (let i = 0; i < 3; i++) setTimeout(() => console.log(i));","origin":"runtime"},{"id":"js-scope/scope-003","topic":"js-scope","level":"junior","concept":"closures","q":"Что выведет код?","a":"0\n1\n2\n\nЗамыкание хранит ссылку на переменную, а не снимок значения. var в цикле — одна переменная на все итерации; let создаёт новую привязку на каждой итерации.","code":"for (var i = 0; i < 3; i++) ((j) => setTimeout(() => console.log(j)))(i);","origin":"runtime"},{"id":"js-scope/scope-004","topic":"js-scope","level":"junior","concept":"closures","q":"Что выведет код?","a":"[ 3, 3, 3 ]\n\nЗамыкание хранит ссылку на переменную, а не снимок значения. var в цикле — одна переменная на все итерации; let создаёт новую привязку на каждой итерации.","code":"const fns = [];\nfor (var i = 0; i < 3; i++) fns.push(() => i);\nconsole.log(fns.map((f) => f()));","origin":"runtime"},{"id":"js-scope/scope-005","topic":"js-scope","level":"junior","concept":"closures","q":"Что выведет код?","a":"[ 0, 1, 2 ]\n\nЗамыкание хранит ссылку на переменную, а не снимок значения. var в цикле — одна переменная на все итерации; let создаёт новую привязку на каждой итерации.","code":"const fns = [];\nfor (let i = 0; i < 3; i++) fns.push(() => i);\nconsole.log(fns.map((f) => f()));","origin":"runtime"},{"id":"js-scope/scope-006","topic":"js-scope","level":"junior","concept":"closures","q":"Что выведет код?","a":"3 1\n\nЗамыкание хранит ссылку на переменную, а не снимок значения. var в цикле — одна переменная на все итерации; let создаёт новую привязку на каждой итерации.","code":"function counter() {\n  let n = 0;\n  return () => ++n;\n}\nconst a = counter(), b = counter();\na(); a();\nconsole.log(a(), b());","origin":"runtime"},{"id":"js-scope/scope-007","topic":"js-scope","level":"junior","concept":"closures","q":"Что выведет код?","a":"2\n\nЗамыкание хранит ссылку на переменную, а не снимок значения. var в цикле — одна переменная на все итерации; let создаёт новую привязку на каждой итерации.","code":"let x = 1;\nfunction f() { return x; }\nx = 2;\nconsole.log(f());","origin":"runtime"},{"id":"js-scope/scope-008","topic":"js-scope","level":"junior","concept":"closures","q":"Что выведет код?","a":"2\n\nЗамыкание хранит ссылку на переменную, а не снимок значения. var в цикле — одна переменная на все итерации; let создаёт новую привязку на каждой итерации.","code":"let x = 1;\nfunction outer() {\n  let x = 2;\n  return function () { return x; };\n}\nconsole.log(outer()());","origin":"runtime"},{"id":"js-scope/scope-009","topic":"js-scope","level":"junior","concept":"closures","q":"Что выведет код?","a":"undefined s3\n\nЗамыкание хранит ссылку на переменную, а не снимок значения. var в цикле — одна переменная на все итерации; let создаёт новую привязку на каждой итерации.","code":"function make() {\n  const secret = 's3';\n  return { get: () => secret };\n}\nconst o = make();\nconsole.log(o.secret, o.get());","origin":"runtime"},{"id":"js-scope/scope-010","topic":"js-scope","level":"junior","concept":"closures","q":"Что выведет код?","a":"4 4\n\nЗамыкание хранит ссылку на переменную, а не снимок значения. var в цикле — одна переменная на все итерации; let создаёт новую привязку на каждой итерации.","code":"const once = (fn) => { let done = false, r; return (...a) => done ? r : (done = true, r = fn(...a)); };\nconst init = once((x) => x * 2);\nconsole.log(init(2), init(10));","origin":"runtime"},{"id":"js-scope/scope-011","topic":"js-scope","level":"junior","concept":"variables","q":"Что выведет код?","a":"undefined\n\nvar поднимается и до присваивания равна undefined. let и const тоже поднимаются, но до объявления находятся в TDZ → ReferenceError. Function declaration поднимается целиком, function expression — нет. const запрещает переприсваивание, а не изменение объекта.","code":"console.log(a);\nvar a = 1;","origin":"runtime"},{"id":"js-scope/scope-012","topic":"js-scope","level":"junior","concept":"variables","q":"Что выведет код?","a":"ReferenceError\n\nvar поднимается и до присваивания равна undefined. let и const тоже поднимаются, но до объявления находятся в TDZ → ReferenceError. Function declaration поднимается целиком, function expression — нет. const запрещает переприсваивание, а не изменение объекта.","code":"try { console.log(b); let b = 1; } catch (e) { console.log(e.name); }","origin":"runtime"},{"id":"js-scope/scope-013","topic":"js-scope","level":"junior","concept":"variables","q":"Что выведет код?","a":"function undefined\n\nvar поднимается и до присваивания равна undefined. let и const тоже поднимаются, но до объявления находятся в TDZ → ReferenceError. Function declaration поднимается целиком, function expression — нет. const запрещает переприсваивание, а не изменение объекта.","code":"console.log(typeof f, typeof g);\nfunction f() {}\nvar g = function () {};","origin":"runtime"},{"id":"js-scope/scope-014","topic":"js-scope","level":"junior","concept":"variables","q":"Что выведет код?","a":"TypeError\n\nvar поднимается и до присваивания равна undefined. let и const тоже поднимаются, но до объявления находятся в TDZ → ReferenceError. Function declaration поднимается целиком, function expression — нет. const запрещает переприсваивание, а не изменение объекта.","code":"try { g(); } catch (e) { console.log(e.name); }\nvar g = () => 1;","origin":"runtime"},{"id":"js-scope/scope-015","topic":"js-scope","level":"junior","concept":"variables","q":"Что выведет код?","a":"ReferenceError\n\nvar поднимается и до присваивания равна undefined. let и const тоже поднимаются, но до объявления находятся в TDZ → ReferenceError. Function declaration поднимается целиком, function expression — нет. const запрещает переприсваивание, а не изменение объекта.","code":"try { h(); } catch (e) { console.log(e.name); }\nconst h = () => 1;","origin":"runtime"},{"id":"js-scope/scope-016","topic":"js-scope","level":"junior","concept":"variables","q":"Что выведет код?","a":"3\n\nvar поднимается и до присваивания равна undefined. let и const тоже поднимаются, но до объявления находятся в TDZ → ReferenceError. Function declaration поднимается целиком, function expression — нет. const запрещает переприсваивание, а не изменение объекта.","code":"console.log(sum(1, 2));\nfunction sum(a, b) { return a + b; }","origin":"runtime"},{"id":"js-scope/scope-017","topic":"js-scope","level":"junior","concept":"variables","q":"Что выведет код?","a":"2\n\nvar поднимается и до присваивания равна undefined. let и const тоже поднимаются, но до объявления находятся в TDZ → ReferenceError. Function declaration поднимается целиком, function expression — нет. const запрещает переприсваивание, а не изменение объекта.","code":"var v = 1;\n{ var v = 2; }\nconsole.log(v);","origin":"runtime"},{"id":"js-scope/scope-018","topic":"js-scope","level":"junior","concept":"variables","q":"Что выведет код?","a":"1\n\nvar поднимается и до присваивания равна undefined. let и const тоже поднимаются, но до объявления находятся в TDZ → ReferenceError. Function declaration поднимается целиком, function expression — нет. const запрещает переприсваивание, а не изменение объекта.","code":"let v = 1;\n{ let v = 2; }\nconsole.log(v);","origin":"runtime"},{"id":"js-scope/scope-019","topic":"js-scope","level":"junior","concept":"variables","q":"Что выведет код?","a":"2\n\nvar поднимается и до присваивания равна undefined. let и const тоже поднимаются, но до объявления находятся в TDZ → ReferenceError. Function declaration поднимается целиком, function expression — нет. const запрещает переприсваивание, а не изменение объекта.","code":"const o = { n: 1 };\no.n = 2;\nconsole.log(o.n);","origin":"runtime"},{"id":"js-scope/scope-020","topic":"js-scope","level":"junior","concept":"variables","q":"Что выведет код?","a":"TypeError\n\nvar поднимается и до присваивания равна undefined. let и const тоже поднимаются, но до объявления находятся в TDZ → ReferenceError. Function declaration поднимается целиком, function expression — нет. const запрещает переприсваивание, а не изменение объекта.","code":"'use strict';\ntry { const c = 1; c = 2; } catch (e) { console.log(e.name); }","origin":"runtime"},{"id":"js-scope/scope-021","topic":"js-scope","level":"junior","concept":"variables","q":"Что выведет код?","a":"ReferenceError\n\nvar поднимается и до присваивания равна undefined. let и const тоже поднимаются, но до объявления находятся в TDZ → ReferenceError. Function declaration поднимается целиком, function expression — нет. const запрещает переприсваивание, а не изменение объекта.","code":"let x = 'outer';\n{\n  try { console.log(x); } catch (e) { console.log(e.name); }\n  let x = 'inner';\n}","origin":"runtime"},{"id":"js-scope/scope-022","topic":"js-scope","level":"junior","concept":"variables","q":"Что выведет код?","a":"function\n\nvar поднимается и до присваивания равна undefined. let и const тоже поднимаются, но до объявления находятся в TDZ → ReferenceError. Function declaration поднимается целиком, function expression — нет. const запрещает переприсваивание, а не изменение объекта.","code":"if (true) { function inBlock() { return 1; } }\nconsole.log(typeof inBlock);","origin":"runtime"},{"id":"js-scope/scope-023","topic":"js-scope","level":"junior","concept":"this","q":"Что выведет код?","a":"o\n\nthis задаёт способ вызова: obj.f() → obj; f() → undefined в strict mode; call, apply и bind задают this явно, повторный bind его не меняет; new → новый объект. Стрелка своего this не имеет и берёт его из внешней функции — call не может это изменить.","code":"'use strict';\nconst o = { n: 'o', get() { return this.n; } };\nconsole.log(o.get());","origin":"runtime"},{"id":"js-scope/scope-024","topic":"js-scope","level":"junior","concept":"this","q":"Что выведет код?","a":"undefined\n\nthis задаёт способ вызова: obj.f() → obj; f() → undefined в strict mode; call, apply и bind задают this явно, повторный bind его не меняет; new → новый объект. Стрелка своего this не имеет и берёт его из внешней функции — call не может это изменить.","code":"'use strict';\nconst o = { n: 'o', get() { return this?.n; } };\nconst g = o.get;\nconsole.log(g());","origin":"runtime"},{"id":"js-scope/scope-025","topic":"js-scope","level":"junior","concept":"this","q":"Что выведет код?","a":"object\n\nthis задаёт способ вызова: obj.f() → obj; f() → undefined в strict mode; call, apply и bind задают this явно, повторный bind его не меняет; new → новый объект. Стрелка своего this не имеет и берёт его из внешней функции — call не может это изменить.","code":"'use strict';\nconst o = { n: 'o', get: () => typeof this };\nconsole.log(o.get());","origin":"runtime"},{"id":"js-scope/scope-026","topic":"js-scope","level":"junior","concept":"this","q":"Что выведет код?","a":"[ 'o' ]\n\nthis задаёт способ вызова: obj.f() → obj; f() → undefined в strict mode; call, apply и bind задают this явно, повторный bind его не меняет; new → новый объект. Стрелка своего this не имеет и берёт его из внешней функции — call не может это изменить.","code":"'use strict';\nconst o = { n: 'o', get() { return [1].map(() => this.n); } };\nconsole.log(o.get());","origin":"runtime"},{"id":"js-scope/scope-027","topic":"js-scope","level":"junior","concept":"this","q":"Что выведет код?","a":"[ undefined ]\n\nthis задаёт способ вызова: obj.f() → obj; f() → undefined в strict mode; call, apply и bind задают this явно, повторный bind его не меняет; new → новый объект. Стрелка своего this не имеет и берёт его из внешней функции — call не может это изменить.","code":"'use strict';\nconst o = { n: 'o', get() { return [1].map(function () { return this?.n; }); } };\nconsole.log(o.get());","origin":"runtime"},{"id":"js-scope/scope-028","topic":"js-scope","level":"junior","concept":"this","q":"Что выведет код?","a":"call apply\n\nthis задаёт способ вызова: obj.f() → obj; f() → undefined в strict mode; call, apply и bind задают this явно, повторный bind его не меняет; new → новый объект. Стрелка своего this не имеет и берёт его из внешней функции — call не может это изменить.","code":"'use strict';\nfunction show() { return this?.n; }\nconsole.log(show.call({ n: 'call' }), show.apply({ n: 'apply' }));","origin":"runtime"},{"id":"js-scope/scope-029","topic":"js-scope","level":"junior","concept":"this","q":"Что выведет код?","a":"first\n\nthis задаёт способ вызова: obj.f() → obj; f() → undefined в strict mode; call, apply и bind задают this явно, повторный bind его не меняет; new → новый объект. Стрелка своего this не имеет и берёт его из внешней функции — call не может это изменить.","code":"'use strict';\nfunction show() { return this.n; }\nconst b = show.bind({ n: 'first' }).bind({ n: 'second' });\nconsole.log(b());","origin":"runtime"},{"id":"js-scope/scope-030","topic":"js-scope","level":"junior","concept":"this","q":"Что выведет код?","a":"o\n\nthis задаёт способ вызова: obj.f() → obj; f() → undefined в strict mode; call, apply и bind задают this явно, повторный bind его не меняет; new → новый объект. Стрелка своего this не имеет и берёт его из внешней функции — call не может это изменить.","code":"const o = {\n  n: 'o',\n  get() {\n    const arrow = () => this.n;\n    return arrow.call({ n: 'call' });\n  },\n};\nconsole.log(o.get());","origin":"runtime"},{"id":"js-scope/scope-031","topic":"js-scope","level":"junior","concept":"this","q":"Что выведет код?","a":"new\n\nthis задаёт способ вызова: obj.f() → obj; f() → undefined в strict mode; call, apply и bind задают this явно, повторный bind его не меняет; new → новый объект. Стрелка своего this не имеет и берёт его из внешней функции — call не может это изменить.","code":"'use strict';\nfunction F() { this.n = 'new'; }\nconst Bound = F.bind({ n: 'bound' });\nconsole.log(new Bound().n);","origin":"runtime"},{"id":"js-scope/scope-032","topic":"js-scope","level":"junior","concept":"this","q":"Что выведет код?","a":"o\nTypeError\n\nthis задаёт способ вызова: obj.f() → obj; f() → undefined в strict mode; call, apply и bind задают this явно, повторный bind его не меняет; new → новый объект. Стрелка своего this не имеет и берёт его из внешней функции — call не может это изменить.","code":"'use strict';\nconst o = { n: 'o', f() { return this.n; } };\nsetTimeout(() => console.log(o.f()));\nsetTimeout(function () { try { console.log(o.f.call(undefined)); } catch (e) { console.log(e.name); } });","origin":"runtime"},{"id":"js-scope/scope-033","topic":"js-scope","level":"junior","concept":"this","q":"Что выведет код?","a":"undefined c\n\nthis задаёт способ вызова: obj.f() → obj; f() → undefined в strict mode; call, apply и bind задают this явно, повторный bind его не меняет; new → новый объект. Стрелка своего this не имеет и берёт его из внешней функции — call не может это изменить.","code":"'use strict';\nclass C { n = 'c'; m() { return this?.n; } a = () => this.n; }\nconst c = new C();\nconst { m, a } = c;\nconsole.log(m(), a());","origin":"runtime"},{"id":"js-scope/scope-034","topic":"js-scope","level":"junior","concept":"this","q":"Что выведет код?","a":"13 7\n\nthis задаёт способ вызова: obj.f() → obj; f() → undefined в strict mode; call, apply и bind задают this явно, повторный bind его не меняет; new → новый объект. Стрелка своего this не имеет и берёт его из внешней функции — call не может это изменить.","code":"'use strict';\nfunction sum(...xs) { return xs.reduce((s, x) => s + x, this.base); }\nconsole.log(sum.call({ base: 10 }, 1, 2), sum.apply({ base: 0 }, [3, 4]));","origin":"runtime"},{"id":"js-scope/scope-035","topic":"js-scope","level":"junior","concept":"this","q":"Что выведет код?","a":"inner\n\nthis задаёт способ вызова: obj.f() → obj; f() → undefined в strict mode; call, apply и bind задают this явно, повторный bind его не меняет; new → новый объект. Стрелка своего this не имеет и берёт его из внешней функции — call не может это изменить.","code":"'use strict';\nconst o = { n: 'o', inner: { n: 'inner', f() { return this.n; } } };\nconsole.log(o.inner.f());","origin":"runtime"},{"id":"js-scope/scope-036","topic":"js-scope","level":"junior","concept":"this","q":"Что выведет код?","a":"o true\n\nthis задаёт способ вызова: obj.f() → obj; f() → undefined в strict mode; call, apply и bind задают this явно, повторный bind его не меняет; new → новый объект. Стрелка своего this не имеет и берёт его из внешней функции — call не может это изменить.","code":"'use strict';\nconst o = { n: 'o', f() { return this.n; } };\nconsole.log((o.f)(), (0, o.f) === o.f);","origin":"runtime"},{"id":"js-oop/oop-001","topic":"js-oop","level":"middle","concept":"prototypes","q":"Что выведет код?","a":"1 2\n\nЧтение свойства идёт по цепочке [[Prototype]]; запись создаёт собственное свойство объекта. Object.create(proto) задаёт прототип, Object.create(null) — объект без прототипа. instanceof проверяет, есть ли F.prototype в цепочке.","code":"function A() {}\nA.prototype.x = 1;\nconst a = new A();\nA.prototype = { x: 2 };\nconsole.log(a.x, new A().x);","origin":"runtime"},{"id":"js-oop/oop-002","topic":"js-oop","level":"junior","concept":"prototypes","q":"Что выведет код?","a":"hi Ann false\n\nЧтение свойства идёт по цепочке [[Prototype]]; запись создаёт собственное свойство объекта. Object.create(proto) задаёт прототип, Object.create(null) — объект без прототипа. instanceof проверяет, есть ли F.prototype в цепочке.","code":"const base = { greet() { return 'hi ' + this.name; } };\nconst u = Object.create(base);\nu.name = 'Ann';\nconsole.log(u.greet(), Object.hasOwn(u, 'greet'));","origin":"runtime"},{"id":"js-oop/oop-003","topic":"js-oop","level":"junior","concept":"prototypes","q":"Что выведет код?","a":"true true\n\nЧтение свойства идёт по цепочке [[Prototype]]; запись создаёт собственное свойство объекта. Object.create(proto) задаёт прототип, Object.create(null) — объект без прототипа. instanceof проверяет, есть ли F.prototype в цепочке.","code":"function A() {}\nconsole.log(A.prototype.constructor === A, Object.getPrototypeOf(new A()) === A.prototype);","origin":"runtime"},{"id":"js-oop/oop-004","topic":"js-oop","level":"junior","concept":"prototypes","q":"Что выведет код?","a":"null\n\nЧтение свойства идёт по цепочке [[Prototype]]; запись создаёт собственное свойство объекта. Object.create(proto) задаёт прототип, Object.create(null) — объект без прототипа. instanceof проверяет, есть ли F.prototype в цепочке.","code":"console.log(Object.getPrototypeOf(Object.prototype));","origin":"runtime"},{"id":"js-oop/oop-005","topic":"js-oop","level":"junior","concept":"prototypes","q":"Что выведет код?","a":"undefined false\n\nЧтение свойства идёт по цепочке [[Prototype]]; запись создаёт собственное свойство объекта. Object.create(proto) задаёт прототип, Object.create(null) — объект без прототипа. instanceof проверяет, есть ли F.prototype в цепочке.","code":"const o = Object.create(null);\nconsole.log(typeof o.toString, 'toString' in o);","origin":"runtime"},{"id":"js-oop/oop-006","topic":"js-oop","level":"junior","concept":"classes","q":"Что выведет код?","a":"1 true\n\nМетоды класса лежат в prototype, static — на самом классе. #поля доступны только внутри класса. super.m() вызывает метод родителя. Тело класса всегда в strict mode.","code":"class A { static s = 1; }\nclass B extends A {}\nconsole.log(B.s, Object.getPrototypeOf(B) === A);","origin":"runtime"},{"id":"js-oop/oop-007","topic":"js-oop","level":"junior","concept":"classes","q":"Что выведет код?","a":"B\n\nМетоды класса лежат в prototype, static — на самом классе. #поля доступны только внутри класса. super.m() вызывает метод родителя. Тело класса всегда в strict mode.","code":"class A { constructor() { this.v = this.init(); } init() { return 'A'; } }\nclass B extends A { init() { return 'B'; } }\nconsole.log(new B().v);","origin":"runtime"},{"id":"js-oop/oop-008","topic":"js-oop","level":"middle","concept":"classes","q":"Что выведет код?","a":"true false\n\nМетоды класса лежат в prototype, static — на самом классе. #поля доступны только внутри класса. super.m() вызывает метод родителя. Тело класса всегда в strict mode.","code":"class A { #x = 1; static has(o) { return #x in o; } }\nconsole.log(A.has(new A()), A.has({}));","origin":"runtime"},{"id":"js-oop/oop-009","topic":"js-oop","level":"middle","concept":"classes","q":"Что выведет код?","a":"ReferenceError\n\nМетоды класса лежат в prototype, static — на самом классе. #поля доступны только внутри класса. super.m() вызывает метод родителя. Тело класса всегда в strict mode.","code":"class B { constructor() { try { this.x = 1; } catch (e) { console.log(e.name); } } }\nclass C extends B { constructor() { try { this.y = 2; } catch (e) { console.log(e.name); } super(); } }\nnew C();","origin":"runtime"},{"id":"js-oop/oop-010","topic":"js-oop","level":"junior","concept":"prototypes","q":"Что выведет код?","a":"{ b: 2 }\n\nЧтение свойства идёт по цепочке [[Prototype]]; запись создаёт собственное свойство объекта. Object.create(proto) задаёт прототип, Object.create(null) — объект без прототипа. instanceof проверяет, есть ли F.prototype в цепочке.","code":"function F() { this.a = 1; return { b: 2 }; }\nconsole.log(new F());","origin":"runtime"},{"id":"js-oop/oop-011","topic":"js-oop","level":"junior","concept":"prototypes","q":"Что выведет код?","a":"F { a: 1 }\n\nЧтение свойства идёт по цепочке [[Prototype]]; запись создаёт собственное свойство объекта. Object.create(proto) задаёт прототип, Object.create(null) — объект без прототипа. instanceof проверяет, есть ли F.prototype в цепочке.","code":"function F() { this.a = 1; return 42; }\nconsole.log(new F());","origin":"runtime"},{"id":"js-oop/oop-012","topic":"js-oop","level":"junior","concept":"prototypes","q":"Что выведет код?","a":"TypeError\n\nЧтение свойства идёт по цепочке [[Prototype]]; запись создаёт собственное свойство объекта. Object.create(proto) задаёт прототип, Object.create(null) — объект без прототипа. instanceof проверяет, есть ли F.prototype в цепочке.","code":"try { new (() => {})(); } catch (e) { console.log(e.name); }","origin":"runtime"},{"id":"js-oop/oop-013","topic":"js-oop","level":"junior","concept":"classes","q":"Что выведет код?","a":"getter []\n\nМетоды класса лежат в prototype, static — на самом классе. #поля доступны только внутри класса. super.m() вызывает метод родителя. Тело класса всегда в strict mode.","code":"class P { get full() { return 'getter'; } }\nconst p = new P();\nconsole.log(p.full, Object.keys(p));","origin":"runtime"},{"id":"js-oop/oop-014","topic":"js-oop","level":"junior","concept":"prototypes","q":"Что выведет код?","a":"[ 1 ]\n\nЧтение свойства идёт по цепочке [[Prototype]]; запись создаёт собственное свойство объекта. Object.create(proto) задаёт прототип, Object.create(null) — объект без прототипа. instanceof проверяет, есть ли F.prototype в цепочке.","code":"const proto = { list: [] };\nconst a = Object.create(proto), b = Object.create(proto);\na.list.push(1);\nconsole.log(b.list);","origin":"runtime"},{"id":"js-oop/oop-015","topic":"js-oop","level":"junior","concept":"prototypes","q":"Что выведет код?","a":"1 0\n\nЧтение свойства идёт по цепочке [[Prototype]]; запись создаёт собственное свойство объекта. Object.create(proto) задаёт прототип, Object.create(null) — объект без прототипа. instanceof проверяет, есть ли F.prototype в цепочке.","code":"const proto = { n: 0 };\nconst a = Object.create(proto);\na.n++;\nconsole.log(a.n, proto.n);","origin":"runtime"},{"id":"js-oop/oop-016","topic":"js-oop","level":"junior","concept":"classes","q":"Что выведет код?","a":"AB\n\nМетоды класса лежат в prototype, static — на самом классе. #поля доступны только внутри класса. super.m() вызывает метод родителя. Тело класса всегда в strict mode.","code":"class A { m() { return 'A'; } }\nclass B extends A { m() { return super.m() + 'B'; } }\nconsole.log(new B().m());","origin":"runtime"},{"id":"js-oop/oop-017","topic":"js-oop","level":"junior","concept":"prototypes","q":"Что выведет код?","a":"true false\n\nЧтение свойства идёт по цепочке [[Prototype]]; запись создаёт собственное свойство объекта. Object.create(proto) задаёт прототип, Object.create(null) — объект без прототипа. instanceof проверяет, есть ли F.prototype в цепочке.","code":"console.log([] instanceof Object, Object.create(null) instanceof Object);","origin":"runtime"},{"id":"js-oop/oop-018","topic":"js-oop","level":"junior","concept":"classes","q":"Что выведет код?","a":"function [ 'constructor' ]\n\nМетоды класса лежат в prototype, static — на самом классе. #поля доступны только внутри класса. super.m() вызывает метод родителя. Тело класса всегда в strict mode.","code":"class A {}\nconsole.log(typeof A, Object.getOwnPropertyNames(A.prototype));","origin":"runtime"},{"id":"js-oop/oop-019","topic":"js-oop","level":"junior","concept":"prototypes","q":"Что выведет код?","a":"Rex! true\n\nЧтение свойства идёт по цепочке [[Prototype]]; запись создаёт собственное свойство объекта. Object.create(proto) задаёт прототип, Object.create(null) — объект без прототипа. instanceof проверяет, есть ли F.prototype в цепочке.","code":"function Dog(name) { this.name = name; }\nDog.prototype.bark = function () { return this.name + '!'; };\nconst d = new Dog('Rex');\nconsole.log(d.bark(), d.__proto__ === Dog.prototype);","origin":"runtime"},{"id":"js-oop/oop-020","topic":"js-oop","level":"middle","concept":"prototypes","q":"Что выведет код?","a":"[ 'b', 'a' ] [ 'b' ]\n\nЧтение свойства идёт по цепочке [[Prototype]]; запись создаёт собственное свойство объекта. Object.create(proto) задаёт прототип, Object.create(null) — объект без прототипа. instanceof проверяет, есть ли F.prototype в цепочке.","code":"const o = { a: 1 };\nconst c = Object.create(o, { b: { value: 2, enumerable: true } });\nconst keys = [];\nfor (const k in c) keys.push(k);\nconsole.log(keys, Object.keys(c));","origin":"runtime"},{"id":"js-async/event-loop-001","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n4\n5\n6\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nconsole.log('3');\nconsole.log('4');\nconsole.log('5');\nconsole.log('6');","origin":"runtime"},{"id":"js-async/event-loop-002","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nsetTimeout(() => {\n  console.log('3');\n  Promise.resolve().then(() => console.log('4'));\n});\nsetTimeout(() => console.log('5'), 50);","origin":"runtime"},{"id":"js-async/event-loop-003","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"6\n1\n2\n7\n3\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\nqueueMicrotask(() => console.log('2'));\nsetTimeout(() => console.log('3'));\nsetTimeout(() => {\n  console.log('4');\n  Promise.resolve().then(() => console.log('5'));\n});\nnew Promise((resolve) => {\n  console.log('6');\n  resolve();\n}).then(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-004","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"6\n1\n4\n7\n8\n3\n2\n5\n9\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => {\n  console.log('1');\n  setTimeout(() => console.log('2'));\n});\nsetTimeout(() => console.log('3'));\nPromise.resolve().then(() => {\n  console.log('4');\n  setTimeout(() => console.log('5'));\n});\n(async () => {\n  console.log('6');\n  await null;\n  console.log('7');\n})();\nPromise.resolve().then(() => {\n  console.log('8');\n  setTimeout(() => console.log('9'));\n});","origin":"runtime"},{"id":"js-async/event-loop-005","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n2\n5\n3\n1\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'));\nPromise.resolve()\n  .then(() => console.log('2'))\n  .then(() => console.log('3'));\nnew Promise((resolve) => {\n  console.log('4');\n  resolve();\n}).then(() => console.log('5'));\nsetTimeout(() => console.log('6'));","origin":"runtime"},{"id":"js-async/event-loop-006","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nconsole.log('2');\nqueueMicrotask(() => console.log('3'));\nsetTimeout(() => console.log('4'), 50);","origin":"runtime"},{"id":"js-async/event-loop-007","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n1\n3\n5\n6\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve()\n  .then(() => console.log('1'))\n  .then(() => console.log('2'));\nPromise.resolve().then(() => console.log('3'));\nnew Promise((resolve) => {\n  console.log('4');\n  resolve();\n}).then(() => console.log('5'));\nqueueMicrotask(() => console.log('6'));","origin":"runtime"},{"id":"js-async/event-loop-008","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n4\n6\n5\n7\n2\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nsetTimeout(() => {\n  console.log('2');\n  Promise.resolve().then(() => console.log('3'));\n});\nconsole.log('4');\nPromise.resolve().then(() => console.log('5'));\nnew Promise((resolve) => {\n  console.log('6');\n  resolve();\n}).then(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-009","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => console.log('1'));\nqueueMicrotask(() => console.log('2'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('3'))\n  .finally(() => console.log('4'));","origin":"runtime"},{"id":"js-async/event-loop-010","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => console.log('1'));\n(async () => {\n  await Promise.resolve();\n  console.log('2');\n})();\nsetTimeout(() => console.log('3'));","origin":"runtime"},{"id":"js-async/event-loop-011","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n4\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"queueMicrotask(() => console.log('1'));\nsetTimeout(() => console.log('2'), 50);\nPromise.reject(new Error('x'))\n  .catch(() => console.log('3'))\n  .finally(() => console.log('4'));","origin":"runtime"},{"id":"js-async/event-loop-012","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n4\n3\n5\n7\n1\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'));\nnew Promise((resolve) => {\n  console.log('2');\n  resolve();\n}).then(() => console.log('3'));\nconsole.log('4');\n(async () => {\n  await Promise.resolve();\n  console.log('5');\n})();\nsetTimeout(() => console.log('6'));\nPromise.resolve().then(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-013","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n4\n2\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve()\n  .then(() => console.log('1'))\n  .then(() => console.log('2'));\n(async () => {\n  await Promise.resolve();\n  console.log('3');\n})();\nPromise.reject(new Error('x'))\n  .catch(() => console.log('4'))\n  .finally(() => console.log('5'));","origin":"runtime"},{"id":"js-async/event-loop-014","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n6\n4\n7\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nPromise.resolve()\n  .then(() => console.log('3'))\n  .then(() => console.log('4'));\nsetTimeout(() => console.log('5'), 50);\nPromise.resolve().then(() => {\n  console.log('6');\n  setTimeout(() => console.log('7'));\n});","origin":"runtime"},{"id":"js-async/event-loop-015","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n7\n2\n4\n5\n6\n8\n9\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve()\n  .then(() => console.log('1'))\n  .then(() => console.log('2'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('3'))\n  .finally(() => console.log('4'));\nsetTimeout(() => {\n  console.log('5');\n  Promise.resolve().then(() => console.log('6'));\n});\n(async () => {\n  await Promise.resolve();\n  console.log('7');\n})();\nsetTimeout(() => {\n  console.log('8');\n  Promise.resolve().then(() => console.log('9'));\n});","origin":"runtime"},{"id":"js-async/event-loop-016","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"8\n1\n2\n4\n5\n6\n9\n3\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"queueMicrotask(() => console.log('1'));\nPromise.resolve().then(() => {\n  console.log('2');\n  setTimeout(() => console.log('3'));\n});\nPromise.resolve().then(() => console.log('4'));\nqueueMicrotask(() => console.log('5'));\nPromise.resolve().then(() => {\n  console.log('6');\n  setTimeout(() => console.log('7'));\n});\nnew Promise((resolve) => {\n  console.log('8');\n  resolve();\n}).then(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-017","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\n(async () => {\n  await Promise.resolve();\n  console.log('2');\n})();\nsetTimeout(() => console.log('3'));","origin":"runtime"},{"id":"js-async/event-loop-018","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n7\n1\n3\n8\n6\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\nnew Promise((resolve) => {\n  console.log('2');\n  resolve();\n}).then(() => console.log('3'));\nsetTimeout(() => console.log('4'), 50);\nsetTimeout(() => console.log('5'), 50);\nsetTimeout(() => console.log('6'));\nnew Promise((resolve) => {\n  console.log('7');\n  resolve();\n}).then(() => console.log('8'));","origin":"runtime"},{"id":"js-async/event-loop-019","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n4\n2\n3\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nqueueMicrotask(() => console.log('3'));\nnew Promise((resolve) => {\n  console.log('4');\n  resolve();\n}).then(() => console.log('5'));","origin":"runtime"},{"id":"js-async/event-loop-020","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n4\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => {\n  console.log('1');\n  setTimeout(() => console.log('2'));\n});\n(async () => {\n  await Promise.resolve();\n  console.log('3');\n})();\nqueueMicrotask(() => console.log('4'));","origin":"runtime"},{"id":"js-async/event-loop-021","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"5\n1\n9\n2\n3\n4\n6\n7\n8\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\nsetTimeout(() => {\n  console.log('3');\n  Promise.resolve().then(() => console.log('4'));\n});\nconsole.log('5');\nsetTimeout(() => console.log('6'));\nsetTimeout(() => {\n  console.log('7');\n  Promise.resolve().then(() => console.log('8'));\n});\nPromise.resolve().then(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-022","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"7\n4\n1\n2\n3\n5\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\nsetTimeout(() => console.log('3'), 50);\n(async () => {\n  await Promise.resolve();\n  console.log('4');\n})();\nsetTimeout(() => console.log('5'), 50);\nsetTimeout(() => console.log('6'), 50);\nconsole.log('7');","origin":"runtime"},{"id":"js-async/event-loop-023","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n5\n1\n6\n2\n4\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve()\n  .then(() => console.log('1'))\n  .then(() => console.log('2'));\nconsole.log('3');\nsetTimeout(() => console.log('4'));\nconsole.log('5');\n(async () => {\n  await Promise.resolve();\n  console.log('6');\n})();\nsetTimeout(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-024","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n3\n1\n4\n5\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'));\nnew Promise((resolve) => {\n  console.log('2');\n  resolve();\n}).then(() => console.log('3'));\nsetTimeout(() => console.log('4'));\nsetTimeout(() => {\n  console.log('5');\n  Promise.resolve().then(() => console.log('6'));\n});","origin":"runtime"},{"id":"js-async/event-loop-025","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"8\n1\n3\n5\n7\n9\n2\n4\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('3'))\n  .finally(() => console.log('4'));\nPromise.resolve().then(() => {\n  console.log('5');\n  setTimeout(() => console.log('6'));\n});\n(async () => {\n  await Promise.resolve();\n  console.log('7');\n})();\nconsole.log('8');\nPromise.resolve().then(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-026","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n5\n7\n2\n3\n4\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\nsetTimeout(() => console.log('2'));\nsetTimeout(() => {\n  console.log('3');\n  Promise.resolve().then(() => console.log('4'));\n});\nPromise.resolve().then(() => {\n  console.log('5');\n  setTimeout(() => console.log('6'));\n});\n(async () => {\n  await Promise.resolve();\n  console.log('7');\n})();","origin":"runtime"},{"id":"js-async/event-loop-027","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n1\n2\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\n(async () => {\n  await Promise.resolve();\n  console.log('2');\n})();\n(async () => {\n  console.log('3');\n  await null;\n  console.log('4');\n})();","origin":"runtime"},{"id":"js-async/event-loop-028","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n6\n7\n8\n5\n1\n2\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\nsetTimeout(() => console.log('3'));\nPromise.resolve()\n  .then(() => console.log('4'))\n  .then(() => console.log('5'));\n(async () => {\n  await Promise.resolve();\n  console.log('6');\n})();\nqueueMicrotask(() => console.log('7'));\n(async () => {\n  await Promise.resolve();\n  console.log('8');\n})();","origin":"runtime"},{"id":"js-async/event-loop-029","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n4\n6\n5\n7\n1\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\nqueueMicrotask(() => console.log('3'));\nPromise.resolve()\n  .then(() => console.log('4'))\n  .then(() => console.log('5'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('6'))\n  .finally(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-030","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n4\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\nPromise.resolve()\n  .then(() => console.log('2'))\n  .then(() => console.log('3'));\n(async () => {\n  await Promise.resolve();\n  console.log('4');\n})();","origin":"runtime"},{"id":"js-async/event-loop-031","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n5\n4\n1\n2\n7\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'));\nsetTimeout(() => console.log('2'));\nPromise.resolve()\n  .then(() => console.log('3'))\n  .then(() => console.log('4'));\nPromise.resolve().then(() => {\n  console.log('5');\n  setTimeout(() => console.log('6'));\n});\nsetTimeout(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-032","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n6\n4\n5\n2\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nsetTimeout(() => console.log('2'), 50);\nsetTimeout(() => console.log('3'), 50);\nsetTimeout(() => {\n  console.log('4');\n  Promise.resolve().then(() => console.log('5'));\n});\nqueueMicrotask(() => console.log('6'));","origin":"runtime"},{"id":"js-async/event-loop-033","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n4\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\nsetTimeout(() => console.log('2'), 50);\nPromise.reject(new Error('x'))\n  .catch(() => console.log('3'))\n  .finally(() => console.log('4'));","origin":"runtime"},{"id":"js-async/event-loop-034","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n5\n7\n2\n3\n4\n6\n8\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => console.log('1'));\nsetTimeout(() => {\n  console.log('2');\n  Promise.resolve().then(() => console.log('3'));\n});\nsetTimeout(() => console.log('4'));\nPromise.resolve().then(() => {\n  console.log('5');\n  setTimeout(() => console.log('6'));\n});\nPromise.resolve().then(() => {\n  console.log('7');\n  setTimeout(() => console.log('8'));\n});","origin":"runtime"},{"id":"js-async/event-loop-035","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nPromise.reject(new Error('x'))\n  .catch(() => console.log('2'))\n  .finally(() => console.log('3'));\nsetTimeout(() => {\n  console.log('4');\n  Promise.resolve().then(() => console.log('5'));\n});","origin":"runtime"},{"id":"js-async/event-loop-036","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n6\n1\n3\n4\n7\n8\n9\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => console.log('1'));\n(async () => {\n  console.log('2');\n  await null;\n  console.log('3');\n})();\nPromise.resolve()\n  .then(() => console.log('4'))\n  .then(() => console.log('5'));\n(async () => {\n  console.log('6');\n  await null;\n  console.log('7');\n})();\nqueueMicrotask(() => console.log('8'));\nPromise.resolve().then(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-037","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n6\n2\n3\n4\n7\n8\n5\n9\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nPromise.resolve().then(() => console.log('3'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('4'))\n  .finally(() => console.log('5'));\n(async () => {\n  console.log('6');\n  await null;\n  console.log('7');\n})();\nPromise.reject(new Error('x'))\n  .catch(() => console.log('8'))\n  .finally(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-038","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n5\n6\n2\n4\n7\n8\n9\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nsetTimeout(() => console.log('3'), 50);\n(async () => {\n  await Promise.resolve();\n  console.log('4');\n})();\nconsole.log('5');\nnew Promise((resolve) => {\n  console.log('6');\n  resolve();\n}).then(() => console.log('7'));\nsetTimeout(() => {\n  console.log('8');\n  Promise.resolve().then(() => console.log('9'));\n});","origin":"runtime"},{"id":"js-async/event-loop-039","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n6\n1\n2\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve()\n  .then(() => console.log('1'))\n  .then(() => console.log('2'));\nconsole.log('3');\nsetTimeout(() => {\n  console.log('4');\n  Promise.resolve().then(() => console.log('5'));\n});\nconsole.log('6');","origin":"runtime"},{"id":"js-async/event-loop-040","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n1\n2\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\nqueueMicrotask(() => console.log('3'));\nsetTimeout(() => console.log('4'));","origin":"runtime"},{"id":"js-async/event-loop-041","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n4\n7\n2\n3\n5\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nPromise.resolve().then(() => console.log('3'));\n(async () => {\n  console.log('4');\n  await null;\n  console.log('5');\n})();\nqueueMicrotask(() => console.log('6'));\nconsole.log('7');","origin":"runtime"},{"id":"js-async/event-loop-042","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n5\n4\n6\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\nsetTimeout(() => console.log('2'), 50);\nPromise.reject(new Error('x'))\n  .catch(() => console.log('3'))\n  .finally(() => console.log('4'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('5'))\n  .finally(() => console.log('6'));","origin":"runtime"},{"id":"js-async/event-loop-043","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n7\n4\n8\n2\n5\n6\n1\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'), 50);\nsetTimeout(() => console.log('2'));\nnew Promise((resolve) => {\n  console.log('3');\n  resolve();\n}).then(() => console.log('4'));\nsetTimeout(() => console.log('5'));\nsetTimeout(() => console.log('6'));\nnew Promise((resolve) => {\n  console.log('7');\n  resolve();\n}).then(() => console.log('8'));","origin":"runtime"},{"id":"js-async/event-loop-044","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n4\n3\n5\n1\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('2'))\n  .finally(() => console.log('3'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('4'))\n  .finally(() => console.log('5'));","origin":"runtime"},{"id":"js-async/event-loop-045","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n6\n1\n5\n8\n2\n9\n3\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\nsetTimeout(() => console.log('3'));\n(async () => {\n  console.log('4');\n  await null;\n  console.log('5');\n})();\nconsole.log('6');\nsetTimeout(() => console.log('7'), 50);\nPromise.reject(new Error('x'))\n  .catch(() => console.log('8'))\n  .finally(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-046","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n2\n5\n7\n6\n3\n1\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'), 50);\nPromise.resolve().then(() => {\n  console.log('2');\n  setTimeout(() => console.log('3'));\n});\nconsole.log('4');\nPromise.resolve()\n  .then(() => console.log('5'))\n  .then(() => console.log('6'));\nqueueMicrotask(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-047","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n7\n3\n4\n5\n6\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => console.log('1'));\nsetTimeout(() => console.log('2'), 50);\nsetTimeout(() => {\n  console.log('3');\n  Promise.resolve().then(() => console.log('4'));\n});\nsetTimeout(() => {\n  console.log('5');\n  Promise.resolve().then(() => console.log('6'));\n});\n(async () => {\n  await Promise.resolve();\n  console.log('7');\n})();","origin":"runtime"},{"id":"js-async/event-loop-048","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nPromise.resolve().then(() => {\n  console.log('3');\n  setTimeout(() => console.log('4'));\n});\nsetTimeout(() => console.log('5'), 50);","origin":"runtime"},{"id":"js-async/event-loop-049","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => console.log('1'));\nsetTimeout(() => {\n  console.log('2');\n  Promise.resolve().then(() => console.log('3'));\n});\nsetTimeout(() => console.log('4'));","origin":"runtime"},{"id":"js-async/event-loop-050","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n5\n1\n4\n8\n6\n2\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => {\n  console.log('1');\n  setTimeout(() => console.log('2'));\n});\n(async () => {\n  console.log('3');\n  await null;\n  console.log('4');\n})();\nconsole.log('5');\nsetTimeout(() => console.log('6'));\nsetTimeout(() => console.log('7'), 50);\nqueueMicrotask(() => console.log('8'));","origin":"runtime"},{"id":"js-async/event-loop-051","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n4\n7\n2\n3\n5\n6\n8\n9\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nqueueMicrotask(() => console.log('3'));\nnew Promise((resolve) => {\n  console.log('4');\n  resolve();\n}).then(() => console.log('5'));\nqueueMicrotask(() => console.log('6'));\n(async () => {\n  console.log('7');\n  await null;\n  console.log('8');\n})();\n(async () => {\n  await Promise.resolve();\n  console.log('9');\n})();","origin":"runtime"},{"id":"js-async/event-loop-052","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n5\n7\n2\n4\n6\n8\n9\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nsetTimeout(() => console.log('3'));\nqueueMicrotask(() => console.log('4'));\nnew Promise((resolve) => {\n  console.log('5');\n  resolve();\n}).then(() => console.log('6'));\nconsole.log('7');\nPromise.reject(new Error('x'))\n  .catch(() => console.log('8'))\n  .finally(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-053","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n6\n8\n2\n3\n5\n7\n9\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nPromise.resolve()\n  .then(() => console.log('3'))\n  .then(() => console.log('4'));\n(async () => {\n  await Promise.resolve();\n  console.log('5');\n})();\n(async () => {\n  console.log('6');\n  await null;\n  console.log('7');\n})();\nnew Promise((resolve) => {\n  console.log('8');\n  resolve();\n}).then(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-054","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n3\n4\n1\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'));\nconsole.log('2');\nnew Promise((resolve) => {\n  console.log('3');\n  resolve();\n}).then(() => console.log('4'));","origin":"runtime"},{"id":"js-async/event-loop-055","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n4\n6\n2\n7\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nconsole.log('3');\nconsole.log('4');\nsetTimeout(() => console.log('5'), 50);\n(async () => {\n  console.log('6');\n  await null;\n  console.log('7');\n})();","origin":"runtime"},{"id":"js-async/event-loop-056","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n4\n5\n2\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => {\n  console.log('1');\n  setTimeout(() => console.log('2'));\n});\n(async () => {\n  await Promise.resolve();\n  console.log('3');\n})();\nPromise.reject(new Error('x'))\n  .catch(() => console.log('4'))\n  .finally(() => console.log('5'));\nsetTimeout(() => console.log('6'), 50);","origin":"runtime"},{"id":"js-async/event-loop-057","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n5\n1\n4\n6\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\nnew Promise((resolve) => {\n  console.log('3');\n  resolve();\n}).then(() => console.log('4'));\n(async () => {\n  console.log('5');\n  await null;\n  console.log('6');\n})();","origin":"runtime"},{"id":"js-async/event-loop-058","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"6\n1\n2\n3\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\nPromise.reject(new Error('x'))\n  .catch(() => console.log('2'))\n  .finally(() => console.log('3'));\nsetTimeout(() => {\n  console.log('4');\n  Promise.resolve().then(() => console.log('5'));\n});\nconsole.log('6');","origin":"runtime"},{"id":"js-async/event-loop-059","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n5\n8\n4\n6\n7\n9\n1\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\n(async () => {\n  console.log('3');\n  await null;\n  console.log('4');\n})();\n(async () => {\n  console.log('5');\n  await null;\n  console.log('6');\n})();\nqueueMicrotask(() => console.log('7'));\nnew Promise((resolve) => {\n  console.log('8');\n  resolve();\n}).then(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-060","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n5\n3\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nsetTimeout(() => {\n  console.log('3');\n  Promise.resolve().then(() => console.log('4'));\n});\nqueueMicrotask(() => console.log('5'));","origin":"runtime"},{"id":"js-async/event-loop-061","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n5\n6\n4\n7\n8\n1\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\n(async () => {\n  console.log('3');\n  await null;\n  console.log('4');\n})();\nconsole.log('5');\nnew Promise((resolve) => {\n  console.log('6');\n  resolve();\n}).then(() => console.log('7'));\nqueueMicrotask(() => console.log('8'));","origin":"runtime"},{"id":"js-async/event-loop-062","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n5\n8\n9\n7\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\n(async () => {\n  await Promise.resolve();\n  console.log('3');\n})();\n(async () => {\n  await Promise.resolve();\n  console.log('4');\n})();\nPromise.resolve().then(() => {\n  console.log('5');\n  setTimeout(() => console.log('6'));\n});\nsetTimeout(() => console.log('7'));\nPromise.resolve()\n  .then(() => console.log('8'))\n  .then(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-063","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n4\n5\n3\n6\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nconsole.log('2');\nsetTimeout(() => console.log('3'));\n(async () => {\n  console.log('4');\n  await null;\n  console.log('5');\n})();\nsetTimeout(() => {\n  console.log('6');\n  Promise.resolve().then(() => console.log('7'));\n});","origin":"runtime"},{"id":"js-async/event-loop-064","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n6\n9\n4\n7\n8\n5\n2\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nsetTimeout(() => {\n  console.log('2');\n  Promise.resolve().then(() => console.log('3'));\n});\nPromise.reject(new Error('x'))\n  .catch(() => console.log('4'))\n  .finally(() => console.log('5'));\nnew Promise((resolve) => {\n  console.log('6');\n  resolve();\n}).then(() => console.log('7'));\nqueueMicrotask(() => console.log('8'));\nconsole.log('9');","origin":"runtime"},{"id":"js-async/event-loop-065","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n5\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nPromise.resolve()\n  .then(() => console.log('3'))\n  .then(() => console.log('4'));\nsetTimeout(() => {\n  console.log('5');\n  Promise.resolve().then(() => console.log('6'));\n});","origin":"runtime"},{"id":"js-async/event-loop-066","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n2\n4\n5\n6\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nnew Promise((resolve) => {\n  console.log('3');\n  resolve();\n}).then(() => console.log('4'));\n(async () => {\n  await Promise.resolve();\n  console.log('5');\n})();\nPromise.resolve()\n  .then(() => console.log('6'))\n  .then(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-067","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"5\n7\n1\n3\n6\n8\n4\n9\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\nsetTimeout(() => console.log('2'));\nPromise.resolve()\n  .then(() => console.log('3'))\n  .then(() => console.log('4'));\nnew Promise((resolve) => {\n  console.log('5');\n  resolve();\n}).then(() => console.log('6'));\nconsole.log('7');\nPromise.resolve()\n  .then(() => console.log('8'))\n  .then(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-068","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n5\n7\n9\n8\n4\n6\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\nsetTimeout(() => console.log('2'), 50);\nPromise.resolve().then(() => {\n  console.log('3');\n  setTimeout(() => console.log('4'));\n});\nPromise.resolve().then(() => {\n  console.log('5');\n  setTimeout(() => console.log('6'));\n});\nPromise.resolve()\n  .then(() => console.log('7'))\n  .then(() => console.log('8'));\nPromise.resolve().then(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-069","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n4\n7\n2\n3\n5\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"queueMicrotask(() => console.log('1'));\nsetTimeout(() => {\n  console.log('2');\n  Promise.resolve().then(() => console.log('3'));\n});\n(async () => {\n  await Promise.resolve();\n  console.log('4');\n})();\nsetTimeout(() => {\n  console.log('5');\n  Promise.resolve().then(() => console.log('6'));\n});\nPromise.resolve().then(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-070","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n4\n5\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nsetTimeout(() => console.log('3'));\n(async () => {\n  await Promise.resolve();\n  console.log('4');\n})();\nqueueMicrotask(() => console.log('5'));","origin":"runtime"},{"id":"js-async/event-loop-071","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"6\n7\n1\n3\n4\n8\n5\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"queueMicrotask(() => console.log('1'));\nsetTimeout(() => console.log('2'), 50);\nqueueMicrotask(() => console.log('3'));\nPromise.resolve().then(() => {\n  console.log('4');\n  setTimeout(() => console.log('5'));\n});\nconsole.log('6');\nnew Promise((resolve) => {\n  console.log('7');\n  resolve();\n}).then(() => console.log('8'));","origin":"runtime"},{"id":"js-async/event-loop-072","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nqueueMicrotask(() => console.log('3'));\nPromise.resolve().then(() => {\n  console.log('4');\n  setTimeout(() => console.log('5'));\n});","origin":"runtime"},{"id":"js-async/event-loop-073","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n4\n2\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\n(async () => {\n  await Promise.resolve();\n  console.log('3');\n})();\nconsole.log('4');","origin":"runtime"},{"id":"js-async/event-loop-074","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"6\n1\n3\n5\n7\n8\n2\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\nPromise.resolve()\n  .then(() => console.log('3'))\n  .then(() => console.log('4'));\nPromise.resolve().then(() => console.log('5'));\nnew Promise((resolve) => {\n  console.log('6');\n  resolve();\n}).then(() => console.log('7'));\n(async () => {\n  await Promise.resolve();\n  console.log('8');\n})();","origin":"runtime"},{"id":"js-async/event-loop-075","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n5\n2\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\nPromise.resolve().then(() => {\n  console.log('3');\n  setTimeout(() => console.log('4'));\n});\nPromise.resolve().then(() => console.log('5'));","origin":"runtime"},{"id":"js-async/event-loop-076","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"5\n7\n1\n3\n6\n2\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\nqueueMicrotask(() => console.log('3'));\nsetTimeout(() => console.log('4'), 50);\nnew Promise((resolve) => {\n  console.log('5');\n  resolve();\n}).then(() => console.log('6'));\nconsole.log('7');","origin":"runtime"},{"id":"js-async/event-loop-077","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n6\n5\n1\n2\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'));\nsetTimeout(() => {\n  console.log('2');\n  Promise.resolve().then(() => console.log('3'));\n});\nPromise.reject(new Error('x'))\n  .catch(() => console.log('4'))\n  .finally(() => console.log('5'));\n(async () => {\n  await Promise.resolve();\n  console.log('6');\n})();","origin":"runtime"},{"id":"js-async/event-loop-078","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n5\n7\n8\n2\n6\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve()\n  .then(() => console.log('1'))\n  .then(() => console.log('2'));\nPromise.resolve().then(() => {\n  console.log('3');\n  setTimeout(() => console.log('4'));\n});\nPromise.resolve()\n  .then(() => console.log('5'))\n  .then(() => console.log('6'));\nPromise.resolve().then(() => console.log('7'));\n(async () => {\n  await Promise.resolve();\n  console.log('8');\n})();","origin":"runtime"},{"id":"js-async/event-loop-079","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n2\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve()\n  .then(() => console.log('1'))\n  .then(() => console.log('2'));\nPromise.resolve().then(() => {\n  console.log('3');\n  setTimeout(() => console.log('4'));\n});\nsetTimeout(() => console.log('5'), 50);","origin":"runtime"},{"id":"js-async/event-loop-080","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n1\n4\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"queueMicrotask(() => console.log('1'));\nsetTimeout(() => console.log('2'));\nnew Promise((resolve) => {\n  console.log('3');\n  resolve();\n}).then(() => console.log('4'));","origin":"runtime"},{"id":"js-async/event-loop-081","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n4\n1\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\nPromise.resolve().then(() => console.log('3'));\n(async () => {\n  await Promise.resolve();\n  console.log('4');\n})();","origin":"runtime"},{"id":"js-async/event-loop-082","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n4\n6\n5\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nsetTimeout(() => console.log('3'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('4'))\n  .finally(() => console.log('5'));\nPromise.resolve().then(() => console.log('6'));","origin":"runtime"},{"id":"js-async/event-loop-083","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n2\n4\n5\n7\n8\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nnew Promise((resolve) => {\n  console.log('3');\n  resolve();\n}).then(() => console.log('4'));\n(async () => {\n  await Promise.resolve();\n  console.log('5');\n})();\nsetTimeout(() => console.log('6'), 50);\nsetTimeout(() => {\n  console.log('7');\n  Promise.resolve().then(() => console.log('8'));\n});","origin":"runtime"},{"id":"js-async/event-loop-084","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n1\n2\n5\n6\n7\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => console.log('1'));\nqueueMicrotask(() => console.log('2'));\nsetTimeout(() => console.log('3'));\nconsole.log('4');\nPromise.resolve().then(() => console.log('5'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('6'))\n  .finally(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-085","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n5\n7\n3\n4\n6\n8\n1\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'));\nconsole.log('2');\nPromise.resolve().then(() => console.log('3'));\n(async () => {\n  await Promise.resolve();\n  console.log('4');\n})();\nnew Promise((resolve) => {\n  console.log('5');\n  resolve();\n}).then(() => console.log('6'));\nnew Promise((resolve) => {\n  console.log('7');\n  resolve();\n}).then(() => console.log('8'));","origin":"runtime"},{"id":"js-async/event-loop-086","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n2\n4\n5\n6\n7\n8\n9\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nnew Promise((resolve) => {\n  console.log('3');\n  resolve();\n}).then(() => console.log('4'));\nPromise.resolve().then(() => console.log('5'));\nPromise.resolve().then(() => console.log('6'));\nsetTimeout(() => {\n  console.log('7');\n  Promise.resolve().then(() => console.log('8'));\n});\nsetTimeout(() => console.log('9'), 50);","origin":"runtime"},{"id":"js-async/event-loop-087","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n4\n5\n6\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nPromise.reject(new Error('x'))\n  .catch(() => console.log('2'))\n  .finally(() => console.log('3'));\n(async () => {\n  await Promise.resolve();\n  console.log('4');\n})();\nqueueMicrotask(() => console.log('5'));\n(async () => {\n  await Promise.resolve();\n  console.log('6');\n})();","origin":"runtime"},{"id":"js-async/event-loop-088","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n6\n7\n1\n3\n5\n8\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\nPromise.resolve().then(() => console.log('3'));\n(async () => {\n  console.log('4');\n  await null;\n  console.log('5');\n})();\nconsole.log('6');\nnew Promise((resolve) => {\n  console.log('7');\n  resolve();\n}).then(() => console.log('8'));","origin":"runtime"},{"id":"js-async/event-loop-089","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve()\n  .then(() => console.log('1'))\n  .then(() => console.log('2'));\nsetTimeout(() => {\n  console.log('3');\n  Promise.resolve().then(() => console.log('4'));\n});\nsetTimeout(() => console.log('5'), 50);","origin":"runtime"},{"id":"js-async/event-loop-090","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n4\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\nPromise.resolve().then(() => {\n  console.log('2');\n  setTimeout(() => console.log('3'));\n});\n(async () => {\n  await Promise.resolve();\n  console.log('4');\n})();","origin":"runtime"},{"id":"js-async/event-loop-091","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n4\n5\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nsetTimeout(() => console.log('3'), 50);\nsetTimeout(() => {\n  console.log('4');\n  Promise.resolve().then(() => console.log('5'));\n});","origin":"runtime"},{"id":"js-async/event-loop-092","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n2\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve()\n  .then(() => console.log('1'))\n  .then(() => console.log('2'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('3'))\n  .finally(() => console.log('4'));\nsetTimeout(() => console.log('5'), 50);","origin":"runtime"},{"id":"js-async/event-loop-093","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n1\n4\n5\n7\n8\n6\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\nsetTimeout(() => console.log('2'));\nnew Promise((resolve) => {\n  console.log('3');\n  resolve();\n}).then(() => console.log('4'));\nPromise.resolve()\n  .then(() => console.log('5'))\n  .then(() => console.log('6'));\nPromise.resolve().then(() => console.log('7'));\nPromise.resolve().then(() => console.log('8'));","origin":"runtime"},{"id":"js-async/event-loop-094","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n4\n5\n6\n1\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\nnew Promise((resolve) => {\n  console.log('3');\n  resolve();\n}).then(() => console.log('4'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('5'))\n  .finally(() => console.log('6'));","origin":"runtime"},{"id":"js-async/event-loop-095","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n2\n5\n3\n6\n1\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'));\nPromise.resolve()\n  .then(() => console.log('2'))\n  .then(() => console.log('3'));\nconsole.log('4');\nPromise.resolve()\n  .then(() => console.log('5'))\n  .then(() => console.log('6'));","origin":"runtime"},{"id":"js-async/event-loop-096","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nsetTimeout(() => {\n  console.log('3');\n  Promise.resolve().then(() => console.log('4'));\n});\nsetTimeout(() => console.log('5'));","origin":"runtime"},{"id":"js-async/event-loop-097","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n4\n6\n8\n3\n7\n5\n9\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"queueMicrotask(() => console.log('1'));\nPromise.resolve()\n  .then(() => console.log('2'))\n  .then(() => console.log('3'));\nPromise.resolve().then(() => {\n  console.log('4');\n  setTimeout(() => console.log('5'));\n});\nPromise.reject(new Error('x'))\n  .catch(() => console.log('6'))\n  .finally(() => console.log('7'));\nPromise.resolve().then(() => {\n  console.log('8');\n  setTimeout(() => console.log('9'));\n});","origin":"runtime"},{"id":"js-async/event-loop-098","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"5\n6\n7\n8\n1\n2\n3\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\nsetTimeout(() => {\n  console.log('3');\n  Promise.resolve().then(() => console.log('4'));\n});\nnew Promise((resolve) => {\n  console.log('5');\n  resolve();\n}).then(() => console.log('6'));\n(async () => {\n  await Promise.resolve();\n  console.log('7');\n})();\n(async () => {\n  await Promise.resolve();\n  console.log('8');\n})();","origin":"runtime"},{"id":"js-async/event-loop-099","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nsetTimeout(() => {\n  console.log('2');\n  Promise.resolve().then(() => console.log('3'));\n});\nsetTimeout(() => console.log('4'));","origin":"runtime"},{"id":"js-async/event-loop-100","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n5\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nsetTimeout(() => console.log('3'));\nsetTimeout(() => console.log('4'));\nsetTimeout(() => {\n  console.log('5');\n  Promise.resolve().then(() => console.log('6'));\n});","origin":"runtime"},{"id":"js-async/event-loop-101","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n2\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nsetTimeout(() => console.log('2'), 50);\n(async () => {\n  await Promise.resolve();\n  console.log('3');\n})();\nsetTimeout(() => console.log('4'), 50);","origin":"runtime"},{"id":"js-async/event-loop-102","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n4\n5\n2\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nsetTimeout(() => console.log('2'));\nPromise.resolve().then(() => console.log('3'));\nqueueMicrotask(() => console.log('4'));\nqueueMicrotask(() => console.log('5'));\nsetTimeout(() => console.log('6'));","origin":"runtime"},{"id":"js-async/event-loop-103","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"7\n1\n3\n4\n8\n2\n5\n6\n9\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\nqueueMicrotask(() => console.log('3'));\n(async () => {\n  await Promise.resolve();\n  console.log('4');\n})();\nsetTimeout(() => {\n  console.log('5');\n  Promise.resolve().then(() => console.log('6'));\n});\nconsole.log('7');\nPromise.resolve().then(() => {\n  console.log('8');\n  setTimeout(() => console.log('9'));\n});","origin":"runtime"},{"id":"js-async/event-loop-104","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n5\n1\n4\n6\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\n(async () => {\n  console.log('3');\n  await null;\n  console.log('4');\n})();\nnew Promise((resolve) => {\n  console.log('5');\n  resolve();\n}).then(() => console.log('6'));","origin":"runtime"},{"id":"js-async/event-loop-105","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"5\n1\n6\n2\n3\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve()\n  .then(() => console.log('1'))\n  .then(() => console.log('2'));\nsetTimeout(() => console.log('3'), 50);\nsetTimeout(() => console.log('4'), 50);\n(async () => {\n  console.log('5');\n  await null;\n  console.log('6');\n})();","origin":"runtime"},{"id":"js-async/event-loop-106","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n3\n4\n5\n1\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'), 50);\nconsole.log('2');\n(async () => {\n  await Promise.resolve();\n  console.log('3');\n})();\nsetTimeout(() => {\n  console.log('4');\n  Promise.resolve().then(() => console.log('5'));\n});","origin":"runtime"},{"id":"js-async/event-loop-107","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n5\n6\n7\n3\n4\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nsetTimeout(() => console.log('2'), 50);\nsetTimeout(() => {\n  console.log('3');\n  Promise.resolve().then(() => console.log('4'));\n});\nconsole.log('5');\nnew Promise((resolve) => {\n  console.log('6');\n  resolve();\n}).then(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-108","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n6\n4\n5\n2\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => {\n  console.log('1');\n  setTimeout(() => console.log('2'));\n});\n(async () => {\n  await Promise.resolve();\n  console.log('3');\n})();\nsetTimeout(() => {\n  console.log('4');\n  Promise.resolve().then(() => console.log('5'));\n});\nPromise.resolve().then(() => {\n  console.log('6');\n  setTimeout(() => console.log('7'));\n});","origin":"runtime"},{"id":"js-async/event-loop-109","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nPromise.resolve().then(() => console.log('3'));\nsetTimeout(() => {\n  console.log('4');\n  Promise.resolve().then(() => console.log('5'));\n});","origin":"runtime"},{"id":"js-async/event-loop-110","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"5\n1\n3\n6\n7\n2\n4\n8\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('3'))\n  .finally(() => console.log('4'));\nconsole.log('5');\n(async () => {\n  await Promise.resolve();\n  console.log('6');\n})();\nPromise.reject(new Error('x'))\n  .catch(() => console.log('7'))\n  .finally(() => console.log('8'));","origin":"runtime"},{"id":"js-async/event-loop-111","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"5\n1\n3\n6\n8\n7\n9\n2\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"queueMicrotask(() => console.log('1'));\nsetTimeout(() => console.log('2'));\nPromise.resolve().then(() => {\n  console.log('3');\n  setTimeout(() => console.log('4'));\n});\nconsole.log('5');\nPromise.reject(new Error('x'))\n  .catch(() => console.log('6'))\n  .finally(() => console.log('7'));\nPromise.resolve()\n  .then(() => console.log('8'))\n  .then(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-112","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n2\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve()\n  .then(() => console.log('1'))\n  .then(() => console.log('2'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('3'))\n  .finally(() => console.log('4'));\nsetTimeout(() => console.log('5'));","origin":"runtime"},{"id":"js-async/event-loop-113","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n2\n5\n3\n1\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'));\nPromise.resolve()\n  .then(() => console.log('2'))\n  .then(() => console.log('3'));\n(async () => {\n  console.log('4');\n  await null;\n  console.log('5');\n})();","origin":"runtime"},{"id":"js-async/event-loop-114","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n6\n1\n5\n7\n2\n3\n8\n9\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve()\n  .then(() => console.log('1'))\n  .then(() => console.log('2'));\nsetTimeout(() => console.log('3'), 50);\nnew Promise((resolve) => {\n  console.log('4');\n  resolve();\n}).then(() => console.log('5'));\n(async () => {\n  console.log('6');\n  await null;\n  console.log('7');\n})();\nsetTimeout(() => console.log('8'), 50);\nsetTimeout(() => console.log('9'), 50);","origin":"runtime"},{"id":"js-async/event-loop-115","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n1\n5\n6\n7\n2\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => {\n  console.log('1');\n  setTimeout(() => console.log('2'));\n});\nsetTimeout(() => console.log('3'), 50);\n(async () => {\n  console.log('4');\n  await null;\n  console.log('5');\n})();\nsetTimeout(() => console.log('6'));\nsetTimeout(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-116","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n5\n7\n4\n6\n2\n8\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => console.log('1'));\nsetTimeout(() => console.log('2'));\nPromise.resolve()\n  .then(() => console.log('3'))\n  .then(() => console.log('4'));\nPromise.resolve()\n  .then(() => console.log('5'))\n  .then(() => console.log('6'));\nPromise.resolve().then(() => {\n  console.log('7');\n  setTimeout(() => console.log('8'));\n});","origin":"runtime"},{"id":"js-async/event-loop-117","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n5\n1\n2\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\nsetTimeout(() => console.log('3'));\n(async () => {\n  console.log('4');\n  await null;\n  console.log('5');\n})();","origin":"runtime"},{"id":"js-async/event-loop-118","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n1\n4\n5\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => {\n  console.log('1');\n  setTimeout(() => console.log('2'));\n});\n(async () => {\n  console.log('3');\n  await null;\n  console.log('4');\n})();\n(async () => {\n  await Promise.resolve();\n  console.log('5');\n})();","origin":"runtime"},{"id":"js-async/event-loop-119","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n6\n1\n3\n5\n7\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => console.log('1'));\n(async () => {\n  console.log('2');\n  await null;\n  console.log('3');\n})();\nsetTimeout(() => console.log('4'));\nqueueMicrotask(() => console.log('5'));\n(async () => {\n  console.log('6');\n  await null;\n  console.log('7');\n})();","origin":"runtime"},{"id":"js-async/event-loop-120","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n4\n5\n2\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nsetTimeout(() => {\n  console.log('2');\n  Promise.resolve().then(() => console.log('3'));\n});\n(async () => {\n  await Promise.resolve();\n  console.log('4');\n})();\nPromise.resolve().then(() => console.log('5'));","origin":"runtime"},{"id":"js-async/event-loop-121","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n4\n1\n3\n5\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"queueMicrotask(() => console.log('1'));\n(async () => {\n  console.log('2');\n  await null;\n  console.log('3');\n})();\nnew Promise((resolve) => {\n  console.log('4');\n  resolve();\n}).then(() => console.log('5'));\n(async () => {\n  await Promise.resolve();\n  console.log('6');\n})();","origin":"runtime"},{"id":"js-async/event-loop-122","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n1\n3\n5\n2\n6\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve()\n  .then(() => console.log('1'))\n  .then(() => console.log('2'));\nqueueMicrotask(() => console.log('3'));\nconsole.log('4');\nqueueMicrotask(() => console.log('5'));\nsetTimeout(() => console.log('6'));\nsetTimeout(() => console.log('7'), 50);","origin":"runtime"},{"id":"js-async/event-loop-123","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n6\n4\n5\n7\n1\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\nnew Promise((resolve) => {\n  console.log('3');\n  resolve();\n}).then(() => console.log('4'));\nPromise.resolve().then(() => console.log('5'));\nnew Promise((resolve) => {\n  console.log('6');\n  resolve();\n}).then(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-124","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n4\n8\n9\n3\n5\n6\n2\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => {\n  console.log('1');\n  setTimeout(() => console.log('2'));\n});\nsetTimeout(() => console.log('3'));\nPromise.resolve().then(() => console.log('4'));\nsetTimeout(() => {\n  console.log('5');\n  Promise.resolve().then(() => console.log('6'));\n});\nsetTimeout(() => console.log('7'), 50);\nPromise.resolve()\n  .then(() => console.log('8'))\n  .then(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-125","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"5\n6\n1\n2\n4\n3\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\nPromise.resolve()\n  .then(() => console.log('2'))\n  .then(() => console.log('3'));\n(async () => {\n  await Promise.resolve();\n  console.log('4');\n})();\nconsole.log('5');\nconsole.log('6');\nsetTimeout(() => console.log('7'), 50);","origin":"runtime"},{"id":"js-async/event-loop-126","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"6\n2\n3\n4\n5\n1\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'), 50);\nsetTimeout(() => {\n  console.log('2');\n  Promise.resolve().then(() => console.log('3'));\n});\nsetTimeout(() => {\n  console.log('4');\n  Promise.resolve().then(() => console.log('5'));\n});\nconsole.log('6');","origin":"runtime"},{"id":"js-async/event-loop-127","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n5\n6\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"queueMicrotask(() => console.log('1'));\nqueueMicrotask(() => console.log('2'));\nPromise.resolve()\n  .then(() => console.log('3'))\n  .then(() => console.log('4'));\nqueueMicrotask(() => console.log('5'));\n(async () => {\n  await Promise.resolve();\n  console.log('6');\n})();","origin":"runtime"},{"id":"js-async/event-loop-128","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n4\n6\n3\n5\n7\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nsetTimeout(() => console.log('2'), 50);\nPromise.resolve().then(() => console.log('3'));\n(async () => {\n  console.log('4');\n  await null;\n  console.log('5');\n})();\nconsole.log('6');\n(async () => {\n  await Promise.resolve();\n  console.log('7');\n})();","origin":"runtime"},{"id":"js-async/event-loop-129","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n5\n7\n4\n6\n1\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\nPromise.resolve()\n  .then(() => console.log('3'))\n  .then(() => console.log('4'));\nPromise.resolve()\n  .then(() => console.log('5'))\n  .then(() => console.log('6'));\nqueueMicrotask(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-130","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n4\n6\n1\n3\n5\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"queueMicrotask(() => console.log('1'));\nnew Promise((resolve) => {\n  console.log('2');\n  resolve();\n}).then(() => console.log('3'));\n(async () => {\n  console.log('4');\n  await null;\n  console.log('5');\n})();\nconsole.log('6');\nqueueMicrotask(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-131","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n5\n6\n4\n7\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => {\n  console.log('1');\n  setTimeout(() => console.log('2'));\n});\nPromise.reject(new Error('x'))\n  .catch(() => console.log('3'))\n  .finally(() => console.log('4'));\nPromise.resolve().then(() => console.log('5'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('6'))\n  .finally(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-132","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n5\n6\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\n(async () => {\n  await Promise.resolve();\n  console.log('2');\n})();\nPromise.resolve().then(() => {\n  console.log('3');\n  setTimeout(() => console.log('4'));\n});\nPromise.reject(new Error('x'))\n  .catch(() => console.log('5'))\n  .finally(() => console.log('6'));","origin":"runtime"},{"id":"js-async/event-loop-133","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n1\n4\n5\n7\n2\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve()\n  .then(() => console.log('1'))\n  .then(() => console.log('2'));\n(async () => {\n  console.log('3');\n  await null;\n  console.log('4');\n})();\nPromise.resolve().then(() => {\n  console.log('5');\n  setTimeout(() => console.log('6'));\n});\nqueueMicrotask(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-134","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n4\n5\n3\n7\n8\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nPromise.resolve()\n  .then(() => console.log('2'))\n  .then(() => console.log('3'));\n(async () => {\n  await Promise.resolve();\n  console.log('4');\n})();\nPromise.resolve().then(() => {\n  console.log('5');\n  setTimeout(() => console.log('6'));\n});\nsetTimeout(() => {\n  console.log('7');\n  Promise.resolve().then(() => console.log('8'));\n});","origin":"runtime"},{"id":"js-async/event-loop-135","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n3\n1\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'), 50);\nconsole.log('2');\nPromise.resolve().then(() => console.log('3'));","origin":"runtime"},{"id":"js-async/event-loop-136","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"6\n1\n3\n4\n7\n8\n2\n9\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\nPromise.resolve().then(() => console.log('3'));\nqueueMicrotask(() => console.log('4'));\nsetTimeout(() => console.log('5'), 50);\nnew Promise((resolve) => {\n  console.log('6');\n  resolve();\n}).then(() => console.log('7'));\nPromise.resolve()\n  .then(() => console.log('8'))\n  .then(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-137","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n9\n2\n3\n4\n6\n5\n7\n8\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nPromise.resolve().then(() => console.log('3'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('4'))\n  .finally(() => console.log('5'));\nPromise.resolve()\n  .then(() => console.log('6'))\n  .then(() => console.log('7'));\nsetTimeout(() => console.log('8'));\nconsole.log('9');","origin":"runtime"},{"id":"js-async/event-loop-138","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"queueMicrotask(() => console.log('1'));\nqueueMicrotask(() => console.log('2'));\nqueueMicrotask(() => console.log('3'));","origin":"runtime"},{"id":"js-async/event-loop-139","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n5\n6\n7\n2\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => {\n  console.log('1');\n  setTimeout(() => console.log('2'));\n});\nPromise.resolve().then(() => {\n  console.log('3');\n  setTimeout(() => console.log('4'));\n});\n(async () => {\n  await Promise.resolve();\n  console.log('5');\n})();\nPromise.reject(new Error('x'))\n  .catch(() => console.log('6'))\n  .finally(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-140","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n1\n3\n4\n5\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\n(async () => {\n  console.log('2');\n  await null;\n  console.log('3');\n})();\n(async () => {\n  await Promise.resolve();\n  console.log('4');\n})();\nqueueMicrotask(() => console.log('5'));\nPromise.resolve().then(() => console.log('6'));","origin":"runtime"},{"id":"js-async/event-loop-141","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n4\n5\n7\n6\n8\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nPromise.resolve().then(() => console.log('2'));\nsetTimeout(() => console.log('3'));\nPromise.resolve().then(() => console.log('4'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('5'))\n  .finally(() => console.log('6'));\nPromise.resolve()\n  .then(() => console.log('7'))\n  .then(() => console.log('8'));","origin":"runtime"},{"id":"js-async/event-loop-142","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n5\n6\n7\n4\n2\n8\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => {\n  console.log('1');\n  setTimeout(() => console.log('2'));\n});\nPromise.resolve().then(() => console.log('3'));\nsetTimeout(() => console.log('4'));\nPromise.resolve().then(() => console.log('5'));\nPromise.resolve()\n  .then(() => console.log('6'))\n  .then(() => console.log('7'));\nsetTimeout(() => console.log('8'), 50);","origin":"runtime"},{"id":"js-async/event-loop-143","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n2\n4\n5\n6\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nPromise.resolve().then(() => console.log('2'));\nnew Promise((resolve) => {\n  console.log('3');\n  resolve();\n}).then(() => console.log('4'));\nqueueMicrotask(() => console.log('5'));\nPromise.resolve().then(() => {\n  console.log('6');\n  setTimeout(() => console.log('7'));\n});","origin":"runtime"},{"id":"js-async/event-loop-144","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n4\n6\n5\n7\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nsetTimeout(() => console.log('3'), 50);\nPromise.reject(new Error('x'))\n  .catch(() => console.log('4'))\n  .finally(() => console.log('5'));\nPromise.resolve().then(() => {\n  console.log('6');\n  setTimeout(() => console.log('7'));\n});","origin":"runtime"},{"id":"js-async/event-loop-145","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n1\n3\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => console.log('1'));\nnew Promise((resolve) => {\n  console.log('2');\n  resolve();\n}).then(() => console.log('3'));\nsetTimeout(() => console.log('4'), 50);","origin":"runtime"},{"id":"js-async/event-loop-146","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n4\n1\n2\n5\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\nnew Promise((resolve) => {\n  console.log('3');\n  resolve();\n}).then(() => console.log('4'));\nsetTimeout(() => {\n  console.log('5');\n  Promise.resolve().then(() => console.log('6'));\n});","origin":"runtime"},{"id":"js-async/event-loop-147","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n5\n3\n4\n6\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nconsole.log('2');\nqueueMicrotask(() => console.log('3'));\nPromise.resolve().then(() => console.log('4'));\nnew Promise((resolve) => {\n  console.log('5');\n  resolve();\n}).then(() => console.log('6'));\nsetTimeout(() => console.log('7'), 50);","origin":"runtime"},{"id":"js-async/event-loop-148","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n1\n3\n5\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\n(async () => {\n  await Promise.resolve();\n  console.log('3');\n})();\nnew Promise((resolve) => {\n  console.log('4');\n  resolve();\n}).then(() => console.log('5'));","origin":"runtime"},{"id":"js-async/event-loop-149","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n1\n4\n5\n6\n2\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\n(async () => {\n  console.log('3');\n  await null;\n  console.log('4');\n})();\nqueueMicrotask(() => console.log('5'));\nPromise.resolve()\n  .then(() => console.log('6'))\n  .then(() => console.log('7'));","origin":"runtime"},{"id":"js-async/event-loop-150","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n1\n2\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\nqueueMicrotask(() => console.log('3'));\nsetTimeout(() => console.log('4'), 50);","origin":"runtime"},{"id":"js-async/event-loop-151","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n4\n5\n6\n8\n3\n7\n9\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\nPromise.reject(new Error('x'))\n  .catch(() => console.log('2'))\n  .finally(() => console.log('3'));\nPromise.resolve().then(() => console.log('4'));\n(async () => {\n  await Promise.resolve();\n  console.log('5');\n})();\nPromise.reject(new Error('x'))\n  .catch(() => console.log('6'))\n  .finally(() => console.log('7'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('8'))\n  .finally(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-152","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n7\n1\n5\n8\n3\n4\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => console.log('1'));\nconsole.log('2');\nsetTimeout(() => {\n  console.log('3');\n  Promise.resolve().then(() => console.log('4'));\n});\nPromise.resolve().then(() => {\n  console.log('5');\n  setTimeout(() => console.log('6'));\n});\n(async () => {\n  console.log('7');\n  await null;\n  console.log('8');\n})();","origin":"runtime"},{"id":"js-async/event-loop-153","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n2\n5\n7\n3\n1\n8\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'));\nPromise.resolve()\n  .then(() => console.log('2'))\n  .then(() => console.log('3'));\n(async () => {\n  console.log('4');\n  await null;\n  console.log('5');\n})();\nsetTimeout(() => console.log('6'), 50);\nPromise.resolve().then(() => {\n  console.log('7');\n  setTimeout(() => console.log('8'));\n});","origin":"runtime"},{"id":"js-async/event-loop-154","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n3\n1\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'), 50);\nPromise.resolve().then(() => console.log('2'));\nPromise.resolve().then(() => console.log('3'));","origin":"runtime"},{"id":"js-async/event-loop-155","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n3\n4\n5\n1\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'), 50);\nPromise.reject(new Error('x'))\n  .catch(() => console.log('2'))\n  .finally(() => console.log('3'));\nsetTimeout(() => {\n  console.log('4');\n  Promise.resolve().then(() => console.log('5'));\n});\nsetTimeout(() => console.log('6'), 50);","origin":"runtime"},{"id":"js-async/event-loop-156","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n4\n2\n5\n6\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nsetTimeout(() => console.log('3'));\nconsole.log('4');\nPromise.resolve()\n  .then(() => console.log('5'))\n  .then(() => console.log('6'));","origin":"runtime"},{"id":"js-async/event-loop-157","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"5\n9\n1\n3\n6\n8\n2\n7\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\nPromise.resolve().then(() => {\n  console.log('3');\n  setTimeout(() => console.log('4'));\n});\nnew Promise((resolve) => {\n  console.log('5');\n  resolve();\n}).then(() => console.log('6'));\nsetTimeout(() => console.log('7'));\nPromise.resolve().then(() => console.log('8'));\nconsole.log('9');","origin":"runtime"},{"id":"js-async/event-loop-158","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"4\n1\n3\n5\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"queueMicrotask(() => console.log('1'));\nsetTimeout(() => console.log('2'));\nPromise.resolve().then(() => console.log('3'));\nnew Promise((resolve) => {\n  console.log('4');\n  resolve();\n}).then(() => console.log('5'));","origin":"runtime"},{"id":"js-async/event-loop-159","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n4\n3\n5\n1\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('2'))\n  .finally(() => console.log('3'));\nPromise.resolve()\n  .then(() => console.log('4'))\n  .then(() => console.log('5'));","origin":"runtime"},{"id":"js-async/event-loop-160","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n2\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nconsole.log('3');\nPromise.resolve().then(() => console.log('4'));","origin":"runtime"},{"id":"js-async/event-loop-161","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\n(async () => {\n  await Promise.resolve();\n  console.log('2');\n})();\n(async () => {\n  await Promise.resolve();\n  console.log('3');\n})();","origin":"runtime"},{"id":"js-async/event-loop-162","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n8\n2\n3\n7\n5\n6\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nPromise.resolve().then(() => {\n  console.log('3');\n  setTimeout(() => console.log('4'));\n});\nsetTimeout(() => {\n  console.log('5');\n  Promise.resolve().then(() => console.log('6'));\n});\nqueueMicrotask(() => console.log('7'));\nconsole.log('8');","origin":"runtime"},{"id":"js-async/event-loop-163","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n5\n6\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nPromise.resolve().then(() => {\n  console.log('3');\n  setTimeout(() => console.log('4'));\n});\nPromise.resolve()\n  .then(() => console.log('5'))\n  .then(() => console.log('6'));","origin":"runtime"},{"id":"js-async/event-loop-164","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n4\n6\n7\n5\n8\n1\n2\n9\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\nconsole.log('3');\nPromise.resolve()\n  .then(() => console.log('4'))\n  .then(() => console.log('5'));\n(async () => {\n  await Promise.resolve();\n  console.log('6');\n})();\nPromise.reject(new Error('x'))\n  .catch(() => console.log('7'))\n  .finally(() => console.log('8'));\nsetTimeout(() => console.log('9'));","origin":"runtime"},{"id":"js-async/event-loop-165","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n4\n6\n8\n9\n7\n2\n3\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"queueMicrotask(() => console.log('1'));\nsetTimeout(() => {\n  console.log('2');\n  Promise.resolve().then(() => console.log('3'));\n});\nPromise.resolve().then(() => {\n  console.log('4');\n  setTimeout(() => console.log('5'));\n});\nPromise.reject(new Error('x'))\n  .catch(() => console.log('6'))\n  .finally(() => console.log('7'));\nPromise.resolve().then(() => console.log('8'));\n(async () => {\n  await Promise.resolve();\n  console.log('9');\n})();","origin":"runtime"},{"id":"js-async/event-loop-166","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n4\n5\n6\n3\n7\n8\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nsetTimeout(() => console.log('3'));\nqueueMicrotask(() => console.log('4'));\nPromise.reject(new Error('x'))\n  .catch(() => console.log('5'))\n  .finally(() => console.log('6'));\nsetTimeout(() => {\n  console.log('7');\n  Promise.resolve().then(() => console.log('8'));\n});","origin":"runtime"},{"id":"js-async/event-loop-167","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n3\n1\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'));\nqueueMicrotask(() => console.log('2'));\n(async () => {\n  await Promise.resolve();\n  console.log('3');\n})();","origin":"runtime"},{"id":"js-async/event-loop-168","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nnew Promise((resolve) => {\n  console.log('2');\n  resolve();\n}).then(() => console.log('3'));\nsetTimeout(() => {\n  console.log('4');\n  Promise.resolve().then(() => console.log('5'));\n});","origin":"runtime"},{"id":"js-async/event-loop-169","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n5\n1\n4\n6\n7\n8\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\nsetTimeout(() => console.log('2'));\n(async () => {\n  console.log('3');\n  await null;\n  console.log('4');\n})();\nnew Promise((resolve) => {\n  console.log('5');\n  resolve();\n}).then(() => console.log('6'));\nqueueMicrotask(() => console.log('7'));\nqueueMicrotask(() => console.log('8'));","origin":"runtime"},{"id":"js-async/event-loop-170","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n9\n4\n5\n8\n6\n1\n2\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\n(async () => {\n  console.log('3');\n  await null;\n  console.log('4');\n})();\nPromise.reject(new Error('x'))\n  .catch(() => console.log('5'))\n  .finally(() => console.log('6'));\nsetTimeout(() => console.log('7'), 50);\nqueueMicrotask(() => console.log('8'));\nconsole.log('9');","origin":"runtime"},{"id":"js-async/event-loop-171","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"console.log('1');\nqueueMicrotask(() => console.log('2'));\nconsole.log('3');","origin":"runtime"},{"id":"js-async/event-loop-172","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n3\n5\n4\n1\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'));\n(async () => {\n  await Promise.resolve();\n  console.log('2');\n})();\nPromise.reject(new Error('x'))\n  .catch(() => console.log('3'))\n  .finally(() => console.log('4'));\n(async () => {\n  await Promise.resolve();\n  console.log('5');\n})();","origin":"runtime"},{"id":"js-async/event-loop-173","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n4\n5\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  console.log('1');\n  await null;\n  console.log('2');\n})();\nqueueMicrotask(() => console.log('3'));\nqueueMicrotask(() => console.log('4'));\nPromise.resolve().then(() => console.log('5'));","origin":"runtime"},{"id":"js-async/event-loop-174","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n8\n2\n4\n6\n9\n7\n5\n3\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\nsetTimeout(() => console.log('3'), 50);\nPromise.resolve().then(() => {\n  console.log('4');\n  setTimeout(() => console.log('5'));\n});\nPromise.resolve()\n  .then(() => console.log('6'))\n  .then(() => console.log('7'));\n(async () => {\n  console.log('8');\n  await null;\n  console.log('9');\n})();","origin":"runtime"},{"id":"js-async/event-loop-175","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n4\n5\n6\n2\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.resolve().then(() => {\n  console.log('1');\n  setTimeout(() => console.log('2'));\n});\nqueueMicrotask(() => console.log('3'));\nqueueMicrotask(() => console.log('4'));\nPromise.resolve().then(() => console.log('5'));\nsetTimeout(() => console.log('6'));","origin":"runtime"},{"id":"js-async/event-loop-176","topic":"js-async","level":"junior","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"2\n1\n3\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => console.log('1'));\nPromise.resolve().then(() => console.log('2'));\nsetTimeout(() => {\n  console.log('3');\n  Promise.resolve().then(() => console.log('4'));\n});","origin":"runtime"},{"id":"js-async/event-loop-177","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n2\n3\n5\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"(async () => {\n  await Promise.resolve();\n  console.log('1');\n})();\n(async () => {\n  await Promise.resolve();\n  console.log('2');\n})();\nPromise.resolve().then(() => {\n  console.log('3');\n  setTimeout(() => console.log('4'));\n});\n(async () => {\n  await Promise.resolve();\n  console.log('5');\n})();","origin":"runtime"},{"id":"js-async/event-loop-178","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"7\n3\n5\n8\n6\n1\n2\n4\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"setTimeout(() => {\n  console.log('1');\n  Promise.resolve().then(() => console.log('2'));\n});\nPromise.resolve().then(() => console.log('3'));\nsetTimeout(() => console.log('4'), 50);\nPromise.reject(new Error('x'))\n  .catch(() => console.log('5'))\n  .finally(() => console.log('6'));\n(async () => {\n  console.log('7');\n  await null;\n  console.log('8');\n})();","origin":"runtime"},{"id":"js-async/event-loop-179","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"1\n3\n5\n2\n4\n6\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"new Promise((resolve) => {\n  console.log('1');\n  resolve();\n}).then(() => console.log('2'));\n(async () => {\n  console.log('3');\n  await null;\n  console.log('4');\n})();\nconsole.log('5');\nPromise.resolve().then(() => console.log('6'));","origin":"runtime"},{"id":"js-async/event-loop-180","topic":"js-async","level":"middle","concept":"event-loop","q":"В каком порядке выведутся числа?","a":"3\n4\n8\n1\n5\n2\n6\n7\n\nСначала весь синхронный код, затем ВСЯ очередь микрозадач (then/catch/finally, await, queueMicrotask), и только потом одна макрозадача (setTimeout) — после каждой макрозадачи снова опустошаются микрозадачи.","code":"Promise.reject(new Error('x'))\n  .catch(() => console.log('1'))\n  .finally(() => console.log('2'));\nconsole.log('3');\n(async () => {\n  console.log('4');\n  await null;\n  console.log('5');\n})();\nsetTimeout(() => {\n  console.log('6');\n  Promise.resolve().then(() => console.log('7'));\n});\nconsole.log('8');","origin":"runtime"},{"id":"js-async/promise-all-001","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.all?","a":"rejected Error a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.all([p('a', 70, false), p('b', 10), p('c', 100)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-allsettled-002","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.allSettled?","a":"fulfilled [ 'rejected: a', 'fulfilled: b' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.allSettled([p('a', 40, false), p('b', 10)])\n  .then((r) => console.log('fulfilled', r.map((s) => s.status + ': ' + (s.value ?? s.reason.message))))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-race-003","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.race?","a":"fulfilled a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.race([p('a', 70), p('b', 100, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-any-004","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.any?","a":"fulfilled b\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.any([p('a', 100), p('b', 10), p('c', 70)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-all-005","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.all?","a":"rejected Error b\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.all([p('a', 70, false), p('b', 40, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-allsettled-006","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.allSettled?","a":"fulfilled [ 'fulfilled: a', 'fulfilled: b', 'fulfilled: c' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.allSettled([p('a', 70), p('b', 100), p('c', 10)])\n  .then((r) => console.log('fulfilled', r.map((s) => s.status + ': ' + (s.value ?? s.reason.message))))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-race-007","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.race?","a":"rejected Error a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.race([p('a', 10, false), p('b', 70), p('c', 100)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-any-008","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.any?","a":"fulfilled d\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.any([p('a', 70), p('b', 100, false), p('c', 40, false), p('d', 10)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-all-009","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.all?","a":"fulfilled [ 'a', 'b' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.all([p('a', 70), p('b', 10)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-allsettled-010","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.allSettled?","a":"fulfilled [ 'fulfilled: a', 'rejected: b', 'fulfilled: c' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.allSettled([p('a', 10), p('b', 100, false), p('c', 40)])\n  .then((r) => console.log('fulfilled', r.map((s) => s.status + ': ' + (s.value ?? s.reason.message))))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-race-011","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.race?","a":"rejected Error a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.race([p('a', 10, false), p('b', 40, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-any-012","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.any?","a":"fulfilled b\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.any([p('a', 10, false), p('b', 40), p('c', 100)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-all-013","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.all?","a":"fulfilled [ 'a', 'b', 'c' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.all([p('a', 40), p('b', 70), p('c', 10)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-allsettled-014","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.allSettled?","a":"fulfilled [ 'fulfilled: a', 'rejected: b' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.allSettled([p('a', 10), p('b', 70, false)])\n  .then((r) => console.log('fulfilled', r.map((s) => s.status + ': ' + (s.value ?? s.reason.message))))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-race-015","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.race?","a":"fulfilled c\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.race([p('a', 100), p('b', 70), p('c', 10)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-any-016","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.any?","a":"fulfilled a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.any([p('a', 100), p('b', 70, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-all-017","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.all?","a":"rejected Error a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.all([p('a', 10, false), p('b', 100, false), p('c', 40), p('d', 70)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-allsettled-018","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.allSettled?","a":"fulfilled [ 'fulfilled: a', 'fulfilled: b', 'fulfilled: c', 'fulfilled: d' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.allSettled([p('a', 40), p('b', 100), p('c', 10), p('d', 70)])\n  .then((r) => console.log('fulfilled', r.map((s) => s.status + ': ' + (s.value ?? s.reason.message))))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-race-019","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.race?","a":"rejected Error a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.race([p('a', 10, false), p('b', 70)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-any-020","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.any?","a":"fulfilled c\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.any([p('a', 10, false), p('b', 40, false), p('c', 100)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-all-021","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.all?","a":"fulfilled [ 'a', 'b', 'c' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.all([p('a', 40), p('b', 10), p('c', 70)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-allsettled-022","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.allSettled?","a":"fulfilled [ 'fulfilled: a', 'fulfilled: b' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.allSettled([p('a', 40), p('b', 100)])\n  .then((r) => console.log('fulfilled', r.map((s) => s.status + ': ' + (s.value ?? s.reason.message))))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-race-023","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.race?","a":"rejected Error b\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.race([p('a', 100), p('b', 10, false), p('c', 40), p('d', 70)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-any-024","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.any?","a":"fulfilled b\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.any([p('a', 70), p('b', 10), p('c', 40), p('d', 100)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-all-025","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.all?","a":"rejected Error a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.all([p('a', 10, false), p('b', 100, false), p('c', 40, false), p('d', 70, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-allsettled-026","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.allSettled?","a":"fulfilled [ 'fulfilled: a', 'rejected: b', 'rejected: c', 'rejected: d' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.allSettled([p('a', 70), p('b', 10, false), p('c', 100, false), p('d', 40, false)])\n  .then((r) => console.log('fulfilled', r.map((s) => s.status + ': ' + (s.value ?? s.reason.message))))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-race-027","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.race?","a":"rejected Error b\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.race([p('a', 70), p('b', 40, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-any-028","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.any?","a":"fulfilled a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.any([p('a', 70), p('b', 10, false), p('c', 40, false), p('d', 100, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-all-029","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.all?","a":"fulfilled [ 'a', 'b', 'c', 'd' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.all([p('a', 10), p('b', 70), p('c', 40), p('d', 100)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-allsettled-030","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.allSettled?","a":"fulfilled [ 'rejected: a', 'rejected: b' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.allSettled([p('a', 10, false), p('b', 100, false)])\n  .then((r) => console.log('fulfilled', r.map((s) => s.status + ': ' + (s.value ?? s.reason.message))))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-race-031","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.race?","a":"rejected Error c\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.race([p('a', 40, false), p('b', 100, false), p('c', 10, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-any-032","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.any?","a":"fulfilled a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.any([p('a', 40), p('b', 10, false), p('c', 70, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-all-033","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.all?","a":"rejected Error b\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.all([p('a', 70), p('b', 100, false), p('c', 40)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-allsettled-034","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.allSettled?","a":"fulfilled [ 'fulfilled: a', 'fulfilled: b' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.allSettled([p('a', 10), p('b', 40)])\n  .then((r) => console.log('fulfilled', r.map((s) => s.status + ': ' + (s.value ?? s.reason.message))))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-race-035","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.race?","a":"fulfilled b\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.race([p('a', 70, false), p('b', 10), p('c', 100), p('d', 40)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-any-036","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.any?","a":"fulfilled b\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.any([p('a', 70, false), p('b', 10)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-all-037","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.all?","a":"rejected Error b\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.all([p('a', 100, false), p('b', 70, false), p('c', 10)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-allsettled-038","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.allSettled?","a":"fulfilled [ 'fulfilled: a', 'fulfilled: b', 'fulfilled: c', 'rejected: d' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.allSettled([p('a', 10), p('b', 40), p('c', 100), p('d', 70, false)])\n  .then((r) => console.log('fulfilled', r.map((s) => s.status + ': ' + (s.value ?? s.reason.message))))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-race-039","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.race?","a":"fulfilled a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.race([p('a', 40), p('b', 100), p('c', 70, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-any-040","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.any?","a":"fulfilled d\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.any([p('a', 100, false), p('b', 70, false), p('c', 40), p('d', 10)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-all-041","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.all?","a":"rejected Error d\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.all([p('a', 70), p('b', 10), p('c', 100, false), p('d', 40, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-allsettled-042","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.allSettled?","a":"fulfilled [ 'fulfilled: a', 'fulfilled: b', 'rejected: c', 'rejected: d' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.allSettled([p('a', 100), p('b', 10), p('c', 70, false), p('d', 40, false)])\n  .then((r) => console.log('fulfilled', r.map((s) => s.status + ': ' + (s.value ?? s.reason.message))))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-race-043","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.race?","a":"fulfilled a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.race([p('a', 10), p('b', 40)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-any-044","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.any?","a":"fulfilled a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.any([p('a', 40), p('b', 10, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-all-045","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.all?","a":"rejected Error b\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.all([p('a', 100), p('b', 70, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-allsettled-046","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.allSettled?","a":"fulfilled [ 'fulfilled: a', 'fulfilled: b', 'rejected: c', 'fulfilled: d' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.allSettled([p('a', 40), p('b', 70), p('c', 10, false), p('d', 100)])\n  .then((r) => console.log('fulfilled', r.map((s) => s.status + ': ' + (s.value ?? s.reason.message))))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-race-047","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.race?","a":"rejected Error b\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.race([p('a', 100), p('b', 10, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-any-048","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.any?","a":"rejected AggregateError All promises were rejected\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.any([p('a', 70, false), p('b', 40, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-all-049","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.all?","a":"rejected Error c\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.all([p('a', 70), p('b', 40), p('c', 10, false), p('d', 100)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-allsettled-050","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.allSettled?","a":"fulfilled [ 'fulfilled: a', 'rejected: b', 'fulfilled: c' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.allSettled([p('a', 40), p('b', 10, false), p('c', 70)])\n  .then((r) => console.log('fulfilled', r.map((s) => s.status + ': ' + (s.value ?? s.reason.message))))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-race-051","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.race?","a":"rejected Error a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.race([p('a', 10, false), p('b', 40), p('c', 100, false), p('d', 70, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-any-052","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.any?","a":"fulfilled d\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.any([p('a', 40, false), p('b', 70), p('c', 100), p('d', 10)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-all-053","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.all?","a":"fulfilled [ 'a', 'b' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.all([p('a', 100), p('b', 40)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-allsettled-054","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.allSettled?","a":"fulfilled [ 'fulfilled: a', 'fulfilled: b', 'fulfilled: c' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.allSettled([p('a', 10), p('b', 70), p('c', 40)])\n  .then((r) => console.log('fulfilled', r.map((s) => s.status + ': ' + (s.value ?? s.reason.message))))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-race-055","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.race?","a":"fulfilled c\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.race([p('a', 100, false), p('b', 40, false), p('c', 10)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-any-056","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.any?","a":"fulfilled c\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.any([p('a', 40), p('b', 70), p('c', 10)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-all-057","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.all?","a":"rejected Error a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.all([p('a', 100, false), p('b', 10), p('c', 40)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-allsettled-058","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.allSettled?","a":"fulfilled [ 'fulfilled: a', 'rejected: b' ]\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.allSettled([p('a', 100), p('b', 40, false)])\n  .then((r) => console.log('fulfilled', r.map((s) => s.status + ': ' + (s.value ?? s.reason.message))))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-race-059","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.race?","a":"rejected Error a\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.race([p('a', 40, false), p('b', 70), p('c', 100, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"js-async/promise-any-060","topic":"js-async","level":"junior","concept":"promises","q":"Что выведет Promise.any?","a":"rejected AggregateError All promises were rejected\n\nall — первый reject или все значения; allSettled — всегда массив {status}; race — первый settled; any — первый fulfilled или AggregateError, если все rejected.","code":"const p = (v, ms, ok = true) =>\n  new Promise((res, rej) => setTimeout(() => (ok ? res(v) : rej(new Error(v))), ms));\n\nPromise.any([p('a', 10, false), p('b', 40, false)])\n  .then((r) => console.log('fulfilled', r))\n  .catch((e) => console.log('rejected', e.constructor.name, e.message));","origin":"runtime"},{"id":"practice/practice-001","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"[ 1, 2, [ 3, [ 4 ] ] ]\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"console.log([1, [2, [3, [4]]]].flat());","origin":"runtime"},{"id":"practice/practice-002","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"[ 1, 2, 3, [ 4 ] ]\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"console.log([1, [2, [3, [4]]]].flat(2));","origin":"runtime"},{"id":"practice/practice-003","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"[ 1, 2, 3, 4 ]\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"console.log([1, [2, [3, [4]]]].flat(Infinity));","origin":"runtime"},{"id":"practice/practice-004","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"[ 1, 3 ]\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"console.log([1, , 3].flat());","origin":"runtime"},{"id":"practice/practice-005","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"[ 1, 2, 3, 4 ]\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"const flatten = (a) => a.reduce((acc, x) => acc.concat(Array.isArray(x) ? flatten(x) : x), []);\nconsole.log(flatten([1, [2, [3, [4, []]]]]));","origin":"runtime"},{"id":"practice/practice-006","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"[ 1, '1', NaN, 0 ]\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"console.log([...new Set([1, '1', 1, NaN, NaN, 0, -0])]);","origin":"runtime"},{"id":"practice/practice-007","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"2\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"console.log([...new Set([{}, {}])].length);","origin":"runtime"},{"id":"practice/practice-008","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"[ 1 ]\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"const uniq = (a) => a.filter((x, i) => a.indexOf(x) === i);\nconsole.log(uniq([NaN, NaN, 1, 1]));","origin":"runtime"},{"id":"practice/practice-009","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"[ { id: 1, v: 'b' } ]\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"const uniqBy = (a, k) => [...new Map(a.map((x) => [x[k], x])).values()];\nconsole.log(uniqBy([{ id: 1, v: 'a' }, { id: 1, v: 'b' }], 'id'));","origin":"runtime"},{"id":"practice/practice-010","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"true false\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"const isPal = (s) => { const t = s.toLowerCase().replace(/[^a-zа-яё0-9]/g, ''); return t === [...t].reverse().join(''); };\nconsole.log(isPal('А роза упала на лапу Азора'), isPal('abc'));","origin":"runtime"},{"id":"practice/practice-011","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"false true\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"const isPal = (s) => s === s.split('').reverse().join('');\nconsole.log(isPal('Abba'), isPal('abba'));","origin":"runtime"},{"id":"practice/practice-012","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"false\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"console.log('😀a'.split('').reverse().join('') === [...'😀a'].reverse().join(''));","origin":"runtime"},{"id":"practice/practice-013","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"2 3\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"const deepClone = (v) => {\n  if (v === null || typeof v !== 'object') return v;\n  const out = Array.isArray(v) ? [] : {};\n  for (const k of Object.keys(v)) out[k] = deepClone(v[k]);\n  return out;\n};\nconst src = { a: [1, { b: 2 }] };\nconst c = deepClone(src);\nc.a[1].b = 3;\nconsole.log(src.a[1].b, c.a[1].b);","origin":"runtime"},{"id":"practice/practice-014","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"false\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"const deepClone = (v) => (v === null || typeof v !== 'object' ? v : Array.isArray(v) ? v.map(deepClone) : Object.fromEntries(Object.entries(v).map(([k, x]) => [k, deepClone(x)])));\nconsole.log(deepClone({ d: new Date(0) }).d instanceof Date);","origin":"runtime"},{"id":"practice/practice-015","topic":"practice","level":"middle","concept":"practice-algorithms","q":"Что выведет код?","a":"true false\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"const o = { name: 'loop' };\no.self = o;\nconst c = structuredClone(o);\nconsole.log(c.self === c, c.self === o);","origin":"runtime"},{"id":"practice/practice-016","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"[ [ 1, 2 ], [ 3, 4 ], [ 5 ] ]\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"const chunk = (a, n) => Array.from({ length: Math.ceil(a.length / n) }, (_, i) => a.slice(i * n, i * n + n));\nconsole.log(chunk([1, 2, 3, 4, 5], 2));","origin":"runtime"},{"id":"practice/practice-017","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"{ a: 2, b: 1, c: 1 }\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"const count = (s) => [...s].reduce((m, c) => ({ ...m, [c]: (m[c] ?? 0) + 1 }), {});\nconsole.log(count('abca'));","origin":"runtime"},{"id":"practice/practice-018","topic":"practice","level":"junior","concept":"practice-algorithms","q":"Что выведет код?","a":"[Object: null prototype] { odd: [ 1, 3 ], even: [ 2, 4 ] }\n\nПроверь контракт: пустой вход, вложенность, порядок результата. На собеседовании назови и встроенную замену, если она есть (flat, Object.groupBy, structuredClone).","code":"const groupBy = (a, f) => Object.groupBy(a, f);\nconsole.log(groupBy([1, 2, 3, 4], (x) => (x % 2 ? 'odd' : 'even')));","origin":"runtime"},{"id":"practice/practice-019","topic":"practice","level":"middle","concept":"debounce-throttle","q":"Что выведет код?","a":"3\n4\n\nDebounce перезапускает таймер на каждом вызове и срабатывает один раз после паузы. Throttle пропускает вызов, если с прошлого прошло меньше интервала.","code":"const debounce = (fn, ms) => { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), ms); }; };\nconst log = debounce((x) => console.log(x), 20);\nlog(1); log(2); log(3);\nsetTimeout(() => log(4), 60);","origin":"runtime"},{"id":"practice/practice-020","topic":"practice","level":"middle","concept":"debounce-throttle","q":"Что выведет код?","a":"1\n\nDebounce перезапускает таймер на каждом вызове и срабатывает один раз после паузы. Throttle пропускает вызов, если с прошлого прошло меньше интервала.","code":"const throttle = (fn, ms) => { let last = -Infinity; return (...a) => { const now = Date.now(); if (now - last >= ms) { last = now; fn(...a); } }; };\nconst log = throttle((x) => console.log(x), 1000);\nlog(1); log(2); log(3);","origin":"runtime"},{"id":"practice/practice-021","topic":"practice","level":"middle","concept":"currying-purity","q":"Что выведет код?","a":"6 6 6\n\ncurry вызывает fn, когда набралось fn.length аргументов, иначе возвращает функцию для остальных. compose применяет функции справа налево. memo кэширует результат по аргументу — это безопасно только для чистых функций.","code":"const curry = (fn) => function c(...a) { return a.length >= fn.length ? fn(...a) : (...b) => c(...a, ...b); };\nconst add3 = curry((a, b, c) => a + b + c);\nconsole.log(add3(1)(2)(3), add3(1, 2)(3), add3(1)(2, 3));","origin":"runtime"},{"id":"practice/practice-022","topic":"practice","level":"junior","concept":"currying-purity","q":"Что выведет код?","a":"11\n\ncurry вызывает fn, когда набралось fn.length аргументов, иначе возвращает функцию для остальных. compose применяет функции справа налево. memo кэширует результат по аргументу — это безопасно только для чистых функций.","code":"const compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x);\nconsole.log(compose((x) => x + 1, (x) => x * 2)(5));","origin":"runtime"},{"id":"practice/practice-023","topic":"practice","level":"junior","concept":"currying-purity","q":"Что выведет код?","a":"2\n\ncurry вызывает fn, когда набралось fn.length аргументов, иначе возвращает функцию для остальных. compose применяет функции справа налево. memo кэширует результат по аргументу — это безопасно только для чистых функций.","code":"const memo = (fn) => { const m = new Map(); return (n) => (m.has(n) ? m.get(n) : (m.set(n, fn(n)), m.get(n))); };\nlet calls = 0;\nconst sq = memo((n) => (calls++, n * n));\nsq(3); sq(3); sq(4);\nconsole.log(calls);","origin":"runtime"},{"id":"practice/practice-024","topic":"practice","level":"middle","concept":"polyfills","q":"Что выведет код?","a":"6\n\nПолифилл повторяет контракт стандарта: bind сохраняет this и частичные аргументы; Promise.all сохраняет порядок результатов и отклоняется на первой ошибке.","code":"Function.prototype.myBind = function (ctx, ...pre) { const fn = this; return function (...a) { return fn.apply(ctx, [...pre, ...a]); }; };\nfunction f(a, b) { return this.x + a + b; }\nconsole.log(f.myBind({ x: 1 }, 2)(3));","origin":"runtime"},{"id":"practice/practice-025","topic":"practice","level":"middle","concept":"polyfills","q":"Что выведет код?","a":"[ 1, 2, 3 ]\n\nПолифилл повторяет контракт стандарта: bind сохраняет this и частичные аргументы; Promise.all сохраняет порядок результатов и отклоняется на первой ошибке.","code":"const all = (ps) => new Promise((res, rej) => { const out = []; let left = ps.length; if (!left) res(out); ps.forEach((p, i) => Promise.resolve(p).then((v) => { out[i] = v; if (--left === 0) res(out); }, rej)); });\nall([Promise.resolve(1), 2, new Promise((r) => setTimeout(() => r(3), 10))]).then(console.log);","origin":"runtime"},{"id":"ng-rxjs/switchmap-001","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 1: a1\nкадр 3: a2\nкадр 4: b1\nкадр 6: b2\nкадр 7: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) x-y|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-002","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 1: a1\nкадр 3: a2\nкадр 4: b1\nкадр 6: b2\nкадр 7: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) x-y|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-003","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 1: a1\nкадр 3: a2\nкадр 4: b1\nкадр 6: b2\nкадр 7: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) x-y|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-004","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 1: a1\nкадр 3: a2\nкадр 7: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) x-y|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-005","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 1: a1\nкадр 4: b1\nкадр 7: b2\nкадр 8: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) x--y|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-006","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 1: a1\nкадр 4: a2\nкадр 4: b1\nкадр 7: b2\nкадр 8: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) x--y|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-007","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 1: a1\nкадр 4: a2\nкадр 5: b1\nкадр 8: b2\nкадр 9: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) x--y|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-008","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 1: a1\nкадр 4: a2\nкадр 7: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) x--y|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-009","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 2: a1\nкадр 5: b1\nкадр 7: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) -x|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-010","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 2: a1\nкадр 5: b1\nкадр 7: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) -x|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-011","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 2: a1\nкадр 5: b1\nкадр 7: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) -x|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-012","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 2: a1\nкадр 5: b1\nкадр 7: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) -x|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-013","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 1: a1\nкадр 2: a2\nкадр 4: b1\nкадр 5: b2\nкадр 7: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) xy|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-014","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 1: a1\nкадр 2: a2\nкадр 4: b1\nкадр 5: b2\nкадр 7: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) xy|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-015","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 1: a1\nкадр 2: a2\nкадр 4: b1\nкадр 5: b2\nкадр 7: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) xy|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-016","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 1: a1\nкадр 2: a2\nкадр 4: b1\nкадр 5: b2\nкадр 7: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a--b--|\ninner(v) xy|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-017","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 1: a1\nкадр 3: b1\nкадр 5: b2\nкадр 7: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) x-y|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-018","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 1: a1\nкадр 3: a2\nкадр 3: b1\nкадр 5: b2\nкадр 7: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) x-y|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-019","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 1: a1\nкадр 3: a2\nкадр 4: b1\nкадр 6: b2\nкадр 7: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) x-y|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-020","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 1: a1\nкадр 3: a2\nкадр 7: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) x-y|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-021","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 1: a1\nкадр 3: b1\nкадр 6: b2\nкадр 7: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) x--y|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-022","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 1: a1\nкадр 3: b1\nкадр 4: a2\nкадр 6: b2\nкадр 7: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) x--y|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-023","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 1: a1\nкадр 4: a2\nкадр 5: b1\nкадр 8: b2\nкадр 9: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) x--y|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-024","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 1: a1\nкадр 4: a2\nкадр 7: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) x--y|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-025","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 2: a1\nкадр 4: b1\nкадр 7: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) -x|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-026","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 2: a1\nкадр 4: b1\nкадр 7: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) -x|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-027","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 2: a1\nкадр 4: b1\nкадр 7: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) -x|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-028","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 2: a1\nкадр 7: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) -x|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-029","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 1: a1\nкадр 2: a2\nкадр 3: b1\nкадр 4: b2\nкадр 7: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) xy|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-030","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 1: a1\nкадр 2: a2\nкадр 3: b1\nкадр 4: b2\nкадр 7: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) xy|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-031","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 1: a1\nкадр 2: a2\nкадр 3: b1\nкадр 4: b2\nкадр 7: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) xy|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-032","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 1: a1\nкадр 2: a2\nкадр 7: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -a-b---|\ninner(v) xy|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-033","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 0: a1\nкадр 2: a2\nкадр 3: b1\nкадр 5: b2\nкадр 6: c1\nкадр 8: c2\nкадр 9: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) x-y|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-034","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 0: a1\nкадр 2: a2\nкадр 3: b1\nкадр 5: b2\nкадр 6: c1\nкадр 8: c2\nкадр 9: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) x-y|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-035","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 0: a1\nкадр 2: a2\nкадр 3: b1\nкадр 5: b2\nкадр 6: c1\nкадр 8: c2\nкадр 9: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) x-y|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-036","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 0: a1\nкадр 2: a2\nкадр 6: c1\nкадр 8: c2\nкадр 9: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) x-y|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-037","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 0: a1\nкадр 3: b1\nкадр 6: c1\nкадр 9: c2\nкадр 10: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) x--y|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-038","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 0: a1\nкадр 3: a2\nкадр 3: b1\nкадр 6: b2\nкадр 6: c1\nкадр 9: c2\nкадр 10: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) x--y|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-039","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 0: a1\nкадр 3: a2\nкадр 4: b1\nкадр 7: b2\nкадр 8: c1\nкадр 11: c2\nкадр 12: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) x--y|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-040","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 0: a1\nкадр 3: a2\nкадр 6: c1\nкадр 9: c2\nкадр 10: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) x--y|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-041","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 1: a1\nкадр 4: b1\nкадр 7: c1\nкадр 8: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) -x|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-042","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 1: a1\nкадр 4: b1\nкадр 7: c1\nкадр 8: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) -x|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-043","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 1: a1\nкадр 4: b1\nкадр 7: c1\nкадр 8: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) -x|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-044","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 1: a1\nкадр 4: b1\nкадр 7: c1\nкадр 8: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) -x|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-045","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 0: a1\nкадр 1: a2\nкадр 3: b1\nкадр 4: b2\nкадр 6: c1\nкадр 7: c2\nкадр 8: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) xy|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-046","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 0: a1\nкадр 1: a2\nкадр 3: b1\nкадр 4: b2\nкадр 6: c1\nкадр 7: c2\nкадр 8: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) xy|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-047","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 0: a1\nкадр 1: a2\nкадр 3: b1\nкадр 4: b2\nкадр 6: c1\nкадр 7: c2\nкадр 8: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) xy|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-048","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 0: a1\nкадр 1: a2\nкадр 3: b1\nкадр 4: b2\nкадр 6: c1\nкадр 7: c2\nкадр 8: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  a--b--c|\ninner(v) xy|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-049","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 1: a1\nкадр 2: b1\nкадр 4: b2\nкадр 7: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) x-y|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-050","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 1: a1\nкадр 2: b1\nкадр 3: a2\nкадр 4: b2\nкадр 7: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) x-y|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-051","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 1: a1\nкадр 3: a2\nкадр 4: b1\nкадр 6: b2\nкадр 7: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) x-y|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-052","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 1: a1\nкадр 3: a2\nкадр 7: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) x-y|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-053","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 1: a1\nкадр 2: b1\nкадр 5: b2\nкадр 7: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) x--y|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-054","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 1: a1\nкадр 2: b1\nкадр 4: a2\nкадр 5: b2\nкадр 7: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) x--y|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-055","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 1: a1\nкадр 4: a2\nкадр 5: b1\nкадр 8: b2\nкадр 9: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) x--y|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-056","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 1: a1\nкадр 4: a2\nкадр 7: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) x--y|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-057","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 3: b1\nкадр 7: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) -x|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-058","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 2: a1\nкадр 3: b1\nкадр 7: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) -x|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-059","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 2: a1\nкадр 4: b1\nкадр 7: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) -x|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-060","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 2: a1\nкадр 7: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) -x|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-061","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 1: a1\nкадр 2: b1\nкадр 3: b2\nкадр 7: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) xy|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-062","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 1: a1\nкадр 2: a2\nкадр 2: b1\nкадр 3: b2\nкадр 7: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) xy|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-063","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 1: a1\nкадр 2: a2\nкадр 3: b1\nкадр 4: b2\nкадр 7: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) xy|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-064","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 1: a1\nкадр 2: a2\nкадр 7: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  -ab----|\ninner(v) xy|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-065","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 2: a1\nкадр 4: a2\nкадр 6: b1\nкадр 8: b2\nкадр 9: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) x-y|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-066","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 2: a1\nкадр 4: a2\nкадр 6: b1\nкадр 8: b2\nкадр 9: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) x-y|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-067","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 2: a1\nкадр 4: a2\nкадр 6: b1\nкадр 8: b2\nкадр 9: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) x-y|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-068","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 2: a1\nкадр 4: a2\nкадр 6: b1\nкадр 8: b2\nкадр 9: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) x-y|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-069","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 2: a1\nкадр 5: a2\nкадр 6: b1\nкадр 9: b2\nкадр 10: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) x--y|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-070","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 2: a1\nкадр 5: a2\nкадр 6: b1\nкадр 9: b2\nкадр 10: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) x--y|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-071","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 2: a1\nкадр 5: a2\nкадр 6: b1\nкадр 9: b2\nкадр 10: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) x--y|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-072","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 2: a1\nкадр 5: a2\nкадр 7: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) x--y|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-073","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 3: a1\nкадр 7: b1\nкадр 8: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) -x|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-074","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 3: a1\nкадр 7: b1\nкадр 8: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) -x|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-075","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 3: a1\nкадр 7: b1\nкадр 8: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) -x|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-076","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 3: a1\nкадр 7: b1\nкадр 8: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) -x|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/switchmap-077","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст switchMap?","a":"кадр 2: a1\nкадр 3: a2\nкадр 6: b1\nкадр 7: b2\nкадр 8: complete\n\nswitchMap отписывается от предыдущего inner при каждом новом значении outer — остаётся только последний.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) xy|\n\nouter$.pipe(switchMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/mergemap-078","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст mergeMap?","a":"кадр 2: a1\nкадр 3: a2\nкадр 6: b1\nкадр 7: b2\nкадр 8: complete\n\nmergeMap держит все inner параллельно и смешивает их значения.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) xy|\n\nouter$.pipe(mergeMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/concatmap-079","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст concatMap?","a":"кадр 2: a1\nкадр 3: a2\nкадр 6: b1\nкадр 7: b2\nкадр 8: complete\n\nconcatMap ставит inner в очередь: следующий стартует только после complete предыдущего.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) xy|\n\nouter$.pipe(concatMap((v) => inner(v)))","origin":"runtime"},{"id":"ng-rxjs/exhaustmap-080","topic":"ng-rxjs","level":"middle","concept":"rxjs-flattening","q":"Какие значения и в каких кадрах выдаст exhaustMap?","a":"кадр 2: a1\nкадр 3: a2\nкадр 6: b1\nкадр 7: b2\nкадр 8: complete\n\nexhaustMap игнорирует новые значения outer, пока активен текущий inner.","code":"// 1 символ = 1 кадр; x → v1, y → v2\nouter$  --a---b|\ninner(v) xy|\n\nouter$.pipe(exhaustMap((v) => inner(v)))","origin":"runtime"},{"id":"cs-basics/out-numbers-001","topic":"cs-basics","level":"intern","concept":"cs-numbers","q":"Что выведет код?","a":"3\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(7 / 2);","origin":"runtime"},{"id":"cs-basics/out-numbers-002","topic":"cs-basics","level":"intern","concept":"cs-numbers","q":"Что выведет код?","a":"3.5\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(7 / 2.0);","origin":"runtime"},{"id":"cs-basics/out-numbers-003","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"-3\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(-7 / 2);","origin":"runtime"},{"id":"cs-basics/out-numbers-004","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"1\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(7 % -3);","origin":"runtime"},{"id":"cs-basics/out-numbers-005","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"-1\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(-7 % 3);","origin":"runtime"},{"id":"cs-basics/out-numbers-006","topic":"cs-basics","level":"intern","concept":"cs-numbers","q":"Что выведет код?","a":"3\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine((int)3.99);","origin":"runtime"},{"id":"cs-basics/out-numbers-007","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"-3\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine((int)-3.99);","origin":"runtime"},{"id":"cs-basics/out-numbers-008","topic":"cs-basics","level":"intern","concept":"cs-numbers","q":"Что выведет код?","a":"9\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(10 / 3 * 3);","origin":"runtime"},{"id":"cs-basics/out-numbers-009","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"4\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(5 / 2 * 2.0);","origin":"runtime"},{"id":"cs-basics/out-numbers-010","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"2\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(Math.Round(2.5));","origin":"runtime"},{"id":"cs-basics/out-numbers-011","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"4\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(Math.Round(3.5));","origin":"runtime"},{"id":"cs-basics/out-numbers-012","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"3\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(Math.Round(2.5, MidpointRounding.AwayFromZero));","origin":"runtime"},{"id":"cs-basics/out-numbers-013","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"False\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(0.1 + 0.2 == 0.3);","origin":"runtime"},{"id":"cs-basics/out-numbers-014","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"True\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(0.1m + 0.2m == 0.3m);","origin":"runtime"},{"id":"cs-basics/out-numbers-015","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"Infinity\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(1 / 0.0);","origin":"runtime"},{"id":"cs-basics/out-numbers-016","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"False\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(double.NaN == double.NaN);","origin":"runtime"},{"id":"cs-basics/out-numbers-017","topic":"cs-basics","level":"intern","concept":"cs-numbers","q":"Что выведет код?","a":"123\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(\"1\" + 2 + 3);","origin":"runtime"},{"id":"cs-basics/out-numbers-018","topic":"cs-basics","level":"intern","concept":"cs-numbers","q":"Что выведет код?","a":"33\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(1 + 2 + \"3\");","origin":"runtime"},{"id":"cs-basics/out-numbers-019","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"98\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine('a' + 1);","origin":"runtime"},{"id":"cs-basics/out-numbers-020","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"b\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine((char)('a' + 1));","origin":"runtime"},{"id":"cs-basics/out-numbers-021","topic":"cs-basics","level":"intern","concept":"cs-numbers","q":"Что выведет код?","a":"b\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(\"abc\"[1]);","origin":"runtime"},{"id":"cs-basics/out-numbers-022","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"42\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(int.Parse(\" 42 \"));","origin":"runtime"},{"id":"cs-basics/out-numbers-023","topic":"cs-basics","level":"intern","concept":"cs-numbers","q":"Что выведет код?","a":"True\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(5 == 5.0);","origin":"runtime"},{"id":"cs-basics/out-numbers-024","topic":"cs-basics","level":"intern","concept":"cs-numbers","q":"Что выведет код?","a":"False\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"Console.WriteLine(new object() == new object());","origin":"runtime"},{"id":"cs-basics/out-basics-001","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"-2147483648\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"int x = int.MaxValue;\nConsole.WriteLine(x + 1);","origin":"runtime"},{"id":"cs-basics/out-basics-002","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"overflow\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"int x = int.MaxValue;\ntry { Console.WriteLine(checked(x + 1)); }\ncatch (OverflowException) { Console.WriteLine(\"overflow\"); }","origin":"runtime"},{"id":"cs-basics/out-basics-003","topic":"cs-basics","level":"junior","concept":"cs-numbers","q":"Что выведет код?","a":"44\n\nЦелые делятся нацело (дробь отбрасывается к нулю), знак % берётся от делимого. Приведение (int) обрезает к нулю. Math.Round по умолчанию — банковское округление (к чётному). double хранит двоичную дробь — 0.1 + 0.2 ≠ 0.3; decimal — десятичная, для денег. Переполнение int по умолчанию молча «заворачивается»; checked бросает OverflowException.","code":"int big = 300;\nConsole.WriteLine((byte)big);","origin":"runtime"},{"id":"cs-basics/out-basics-004","topic":"cs-basics","level":"intern","concept":"cs-value-reference-types","q":"Что выведет код?","a":"1\n\nStruct копируется целиком при присваивании и передаче; class копирует только ссылку на тот же объект. Упаковка (boxing) кладёт копию значения в object — дальнейшие изменения переменной её не трогают. Новый объект в параметре меняет только локальную ссылку.","code":"struct Point { public int X; }\n\nvar a = new Point { X = 1 };\nvar b = a;\nb.X = 2;\nConsole.WriteLine(a.X);","origin":"runtime"},{"id":"cs-basics/out-basics-005","topic":"cs-basics","level":"intern","concept":"cs-value-reference-types","q":"Что выведет код?","a":"2\n\nStruct копируется целиком при присваивании и передаче; class копирует только ссылку на тот же объект. Упаковка (boxing) кладёт копию значения в object — дальнейшие изменения переменной её не трогают. Новый объект в параметре меняет только локальную ссылку.","code":"class Point { public int X; }\n\nvar a = new Point { X = 1 };\nvar b = a;\nb.X = 2;\nConsole.WriteLine(a.X);","origin":"runtime"},{"id":"cs-basics/out-basics-006","topic":"cs-basics","level":"junior","concept":"cs-value-reference-types","q":"Что выведет код?","a":"1\n\nStruct копируется целиком при присваивании и передаче; class копирует только ссылку на тот же объект. Упаковка (boxing) кладёт копию значения в object — дальнейшие изменения переменной её не трогают. Новый объект в параметре меняет только локальную ссылку.","code":"int a = 1;\nobject boxed = a;\na = 2;\nConsole.WriteLine(boxed);","origin":"runtime"},{"id":"cs-basics/out-basics-007","topic":"cs-basics","level":"junior","concept":"cs-value-reference-types","q":"Что выведет код?","a":"0\n\nStruct копируется целиком при присваивании и передаче; class копирует только ссылку на тот же объект. Упаковка (boxing) кладёт копию значения в object — дальнейшие изменения переменной её не трогают. Новый объект в параметре меняет только локальную ссылку.","code":"struct Counter { public int N; public void Inc() => N++; }\n\nvar list = new List<Counter> { new Counter() };\nvar c = list[0];\nc.Inc();\nConsole.WriteLine(list[0].N);","origin":"runtime"},{"id":"cs-basics/out-basics-008","topic":"cs-basics","level":"junior","concept":"cs-value-reference-types","q":"Что выведет код?","a":"1\n5\n\nStruct копируется целиком при присваивании и передаче; class копирует только ссылку на тот же объект. Упаковка (boxing) кладёт копию значения в object — дальнейшие изменения переменной её не трогают. Новый объект в параметре меняет только локальную ссылку.","code":"class Box { public int V; }\n\nvoid Reset(Box b) { b = new Box { V = 0 }; }\nvoid Change(Box b) { b.V = 5; }\nvar box = new Box { V = 1 };\nReset(box);\nConsole.WriteLine(box.V);\nChange(box);\nConsole.WriteLine(box.V);","origin":"runtime"},{"id":"cs-basics/out-basics-009","topic":"cs-basics","level":"intern","concept":"cs-method-params","q":"Что выведет код?","a":"1\n2\n\nПо умолчанию аргумент передаётся по значению (копия). ref и out передают саму переменную: изменения видны снаружи. TryParse при неудаче пишет в out значение по умолчанию.","code":"void Inc(int x) { x++; }\nvoid IncRef(ref int x) { x++; }\nint n = 1;\nInc(n);\nConsole.WriteLine(n);\nIncRef(ref n);\nConsole.WriteLine(n);","origin":"runtime"},{"id":"cs-basics/out-basics-010","topic":"cs-basics","level":"junior","concept":"cs-method-params","q":"Что выведет код?","a":"False\n0\n\nПо умолчанию аргумент передаётся по значению (копия). ref и out передают саму переменную: изменения видны снаружи. TryParse при неудаче пишет в out значение по умолчанию.","code":"bool ok = int.TryParse(\"12a\", out int value);\nConsole.WriteLine(ok);\nConsole.WriteLine(value);","origin":"runtime"},{"id":"cs-basics/out-basics-011","topic":"cs-basics","level":"intern","concept":"cs-strings","q":"Что выведет код?","a":"hello\n\nstring неизменяем: методы возвращают новую строку, а исходная не меняется. == для типа string сравнивает содержимое, но для object — ссылки; Equals — содержимое. Сравнение без учёта регистра — через StringComparison. null при конкатенации даёт пустую строку.","code":"var s = \"hello\";\ns.ToUpper();\nConsole.WriteLine(s);","origin":"runtime"},{"id":"cs-basics/out-basics-012","topic":"cs-basics","level":"intern","concept":"cs-strings","q":"Что выведет код?","a":"a\n\nstring неизменяем: методы возвращают новую строку, а исходная не меняется. == для типа string сравнивает содержимое, но для object — ссылки; Equals — содержимое. Сравнение без учёта регистра — через StringComparison. null при конкатенации даёт пустую строку.","code":"var s = \"a\";\nvar t = s;\ns += \"b\";\nConsole.WriteLine(t);","origin":"runtime"},{"id":"cs-basics/out-basics-013","topic":"cs-basics","level":"junior","concept":"cs-strings","q":"Что выведет код?","a":"False\nTrue\nTrue\n\nstring неизменяем: методы возвращают новую строку, а исходная не меняется. == для типа string сравнивает содержимое, но для object — ссылки; Equals — содержимое. Сравнение без учёта регистра — через StringComparison. null при конкатенации даёт пустую строку.","code":"object a = \"x\";\nobject b = new string('x', 1);\nConsole.WriteLine(a == b);\nConsole.WriteLine(a.Equals(b));\nConsole.WriteLine((string)a == (string)b);","origin":"runtime"},{"id":"cs-basics/out-basics-014","topic":"cs-basics","level":"junior","concept":"cs-strings","q":"Что выведет код?","a":"False\nTrue\n\nstring неизменяем: методы возвращают новую строку, а исходная не меняется. == для типа string сравнивает содержимое, но для object — ссылки; Equals — содержимое. Сравнение без учёта регистра — через StringComparison. null при конкатенации даёт пустую строку.","code":"Console.WriteLine(string.Equals(\"Abc\", \"abc\"));\nConsole.WriteLine(string.Equals(\"Abc\", \"abc\", StringComparison.OrdinalIgnoreCase));","origin":"runtime"},{"id":"cs-basics/out-basics-015","topic":"cs-basics","level":"intern","concept":"cs-strings","q":"Что выведет код?","a":"[]\nTrue\n\nstring неизменяем: методы возвращают новую строку, а исходная не меняется. == для типа string сравнивает содержимое, но для object — ссылки; Equals — содержимое. Сравнение без учёта регистра — через StringComparison. null при конкатенации даёт пустую строку.","code":"string? s = null;\nConsole.WriteLine(\"[\" + s + \"]\");\nConsole.WriteLine(string.IsNullOrEmpty(s));","origin":"runtime"},{"id":"cs-basics/out-basics-016","topic":"cs-basics","level":"intern","concept":"cs-nullable","q":"Что выведет код?","a":"-1\n\n?. возвращает null, если слева null, а ?? подставляет запасное значение; ??= присваивает только когда переменная null. Арифметика с null для int? даёт null (lifted operators).","code":"string? s = null;\nConsole.WriteLine(s?.Length ?? -1);","origin":"runtime"},{"id":"cs-basics/out-basics-017","topic":"cs-basics","level":"junior","concept":"cs-nullable","q":"Что выведет код?","a":"True\nFalse\n0\n\n?. возвращает null, если слева null, а ?? подставляет запасное значение; ??= присваивает только когда переменная null. Арифметика с null для int? даёт null (lifted operators).","code":"int? x = null;\nConsole.WriteLine(x + 1 == null);\nConsole.WriteLine(x.HasValue);\nConsole.WriteLine(x.GetValueOrDefault());","origin":"runtime"},{"id":"cs-basics/out-basics-018","topic":"cs-basics","level":"junior","concept":"cs-nullable","q":"Что выведет код?","a":"guest\n\n?. возвращает null, если слева null, а ?? подставляет запасное значение; ??= присваивает только когда переменная null. Арифметика с null для int? даёт null (lifted operators).","code":"string? name = null;\nname ??= \"guest\";\nname ??= \"admin\";\nConsole.WriteLine(name);","origin":"runtime"},{"id":"cs-oop/out-oop-019","topic":"cs-oop","level":"intern","concept":"cs-oop-principles","q":"Что выведет код?","a":"Woof\n\nvirtual + override: метод выбирается по типу объекта во время выполнения. new только скрывает метод — выбор по типу переменной. Конструкторы выполняются от базового к производному; виртуальный вызов из базового конструктора видит производный объект, где инициализаторы полей уже выполнены, а тело конструктора — ещё нет.","code":"class Animal { public virtual string Speak() => \"...\"; }\nclass Dog : Animal { public override string Speak() => \"Woof\"; }\n\nAnimal a = new Dog();\nConsole.WriteLine(a.Speak());","origin":"runtime"},{"id":"cs-oop/out-oop-020","topic":"cs-oop","level":"junior","concept":"cs-oop-principles","q":"Что выведет код?","a":"Base\nDerived\n\nvirtual + override: метод выбирается по типу объекта во время выполнения. new только скрывает метод — выбор по типу переменной. Конструкторы выполняются от базового к производному; виртуальный вызов из базового конструктора видит производный объект, где инициализаторы полей уже выполнены, а тело конструктора — ещё нет.","code":"class Base { public string Name() => \"Base\"; }\nclass Derived : Base { public new string Name() => \"Derived\"; }\n\nBase b = new Derived();\nDerived d = new Derived();\nConsole.WriteLine(b.Name());\nConsole.WriteLine(d.Name());","origin":"runtime"},{"id":"cs-oop/out-oop-021","topic":"cs-oop","level":"middle","concept":"cs-oop-principles","q":"Что выведет код?","a":"derived/null\n\nvirtual + override: метод выбирается по типу объекта во время выполнения. new только скрывает метод — выбор по типу переменной. Конструкторы выполняются от базового к производному; виртуальный вызов из базового конструктора видит производный объект, где инициализаторы полей уже выполнены, а тело конструктора — ещё нет.","code":"class Base { public Base() { Console.WriteLine(Describe()); } public virtual string Describe() => \"base\"; }\nclass Derived : Base { private readonly string _name = \"derived\"; private readonly string? _late; public Derived() { _late = \"set\"; } public override string Describe() => _name + \"/\" + (_late ?? \"null\"); }\n\nnew Derived();","origin":"runtime"},{"id":"cs-oop/out-oop-022","topic":"cs-oop","level":"junior","concept":"cs-oop-principles","q":"Что выведет код?","a":"A\nB\nC\n\nvirtual + override: метод выбирается по типу объекта во время выполнения. new только скрывает метод — выбор по типу переменной. Конструкторы выполняются от базового к производному; виртуальный вызов из базового конструктора видит производный объект, где инициализаторы полей уже выполнены, а тело конструктора — ещё нет.","code":"class A { public A() { Console.WriteLine(\"A\"); } }\nclass B : A { public B() { Console.WriteLine(\"B\"); } }\nclass C : B { public C() { Console.WriteLine(\"C\"); } }\n\nnew C();","origin":"runtime"},{"id":"cs-oop/out-oop-023","topic":"cs-oop","level":"junior","concept":"cs-abstraction","q":"Что выведет код?","a":"hi\n\nМетод интерфейса с реализацией по умолчанию доступен только через ссылку типа интерфейса. Статический конструктор выполняется один раз, перед первым обращением к типу.","code":"interface IGreeter { string Greet() => \"hi\"; }\nclass Plain : IGreeter { }\n\nIGreeter g = new Plain();\nConsole.WriteLine(g.Greet());","origin":"runtime"},{"id":"cs-oop/out-oop-024","topic":"cs-oop","level":"middle","concept":"cs-abstraction","q":"Что выведет код?","a":"start\nstatic ctor\n2\n\nМетод интерфейса с реализацией по умолчанию доступен только через ссылку типа интерфейса. Статический конструктор выполняется один раз, перед первым обращением к типу.","code":"class Counter { public static int Created; static Counter() { Console.WriteLine(\"static ctor\"); } public Counter() { Created++; } }\n\nConsole.WriteLine(\"start\");\nnew Counter();\nnew Counter();\nConsole.WriteLine(Counter.Created);","origin":"runtime"},{"id":"cs-oop/out-oop-025","topic":"cs-oop","level":"intern","concept":"cs-records","q":"Что выведет код?","a":"True\nFalse\n\nrecord сравнивается по значениям свойств, class — по ссылке. with создаёт копию с изменениями, но копия поверхностная: ссылочные свойства (список) остаются общими. ToString у record показывает свойства.","code":"record Point(int X, int Y);\nclass PointClass { public int X; public int Y; }\n\nConsole.WriteLine(new Point(1, 2) == new Point(1, 2));\nConsole.WriteLine(new PointClass { X = 1, Y = 2 } == new PointClass { X = 1, Y = 2 });","origin":"runtime"},{"id":"cs-oop/out-oop-026","topic":"cs-oop","level":"junior","concept":"cs-records","q":"Что выведет код?","a":"Point { X = 1, Y = 2 }\nPoint { X = 1, Y = 5 }\n\nrecord сравнивается по значениям свойств, class — по ссылке. with создаёт копию с изменениями, но копия поверхностная: ссылочные свойства (список) остаются общими. ToString у record показывает свойства.","code":"record Point(int X, int Y);\n\nvar a = new Point(1, 2);\nvar b = a with { Y = 5 };\nConsole.WriteLine(a);\nConsole.WriteLine(b);","origin":"runtime"},{"id":"cs-oop/out-oop-027","topic":"cs-oop","level":"middle","concept":"cs-records","q":"Что выведет код?","a":"2\nTrue\n\nrecord сравнивается по значениям свойств, class — по ссылке. with создаёт копию с изменениями, но копия поверхностная: ссылочные свойства (список) остаются общими. ToString у record показывает свойства.","code":"record Order(string Id, List<string> Items);\n\nvar a = new Order(\"1\", new List<string> { \"x\" });\nvar b = a with { };\nb.Items.Add(\"y\");\nConsole.WriteLine(a.Items.Count);\nConsole.WriteLine(ReferenceEquals(a.Items, b.Items));","origin":"runtime"},{"id":"cs-basics/out-basics-028","topic":"cs-basics","level":"junior","concept":"dotnet-exceptions","q":"Что выведет код?","a":"finally\n1\n\nfinally выполняется всегда — и при return, и при исключении, до выхода из метода. catch-блоки проверяются сверху вниз: срабатывает первый подходящий по типу.","code":"int F() { try { return 1; } finally { Console.WriteLine(\"finally\"); } }\nConsole.WriteLine(F());","origin":"runtime"},{"id":"cs-basics/out-basics-029","topic":"cs-basics","level":"junior","concept":"dotnet-exceptions","q":"Что выведет код?","a":"InvalidOperationException\ndone\n\nfinally выполняется всегда — и при return, и при исключении, до выхода из метода. catch-блоки проверяются сверху вниз: срабатывает первый подходящий по типу.","code":"try { throw new InvalidOperationException(\"boom\"); }\ncatch (ArgumentException) { Console.WriteLine(\"argument\"); }\ncatch (Exception e) { Console.WriteLine(e.GetType().Name); }\nfinally { Console.WriteLine(\"done\"); }","origin":"runtime"},{"id":"cs-async/out-async-030","topic":"cs-async","level":"junior","concept":"cs-async-await","q":"Что выведет код?","a":"A\nB\nC\nD\nE\n\nasync-метод выполняется синхронно до первого await, который реально ждёт, и только тогда возвращает Task. Исключение внутри async сохраняется в Task и выбрасывается при await. Последовательные await складывают время; Task.WhenAll запускает работу параллельно и сохраняет порядок результатов.","code":"async Task Work() { Console.WriteLine(\"B\"); await Task.Delay(10); Console.WriteLine(\"D\"); }\nConsole.WriteLine(\"A\");\nvar t = Work();\nConsole.WriteLine(\"C\");\nawait t;\nConsole.WriteLine(\"E\");","origin":"runtime"},{"id":"cs-async/out-async-031","topic":"cs-async","level":"junior","concept":"cs-async-await","q":"Что выведет код?","a":"after call\nInvalidOperationException\n\nasync-метод выполняется синхронно до первого await, который реально ждёт, и только тогда возвращает Task. Исключение внутри async сохраняется в Task и выбрасывается при await. Последовательные await складывают время; Task.WhenAll запускает работу параллельно и сохраняет порядок результатов.","code":"async Task Fail() { await Task.Yield(); throw new InvalidOperationException(); }\nvar t = Fail();\nConsole.WriteLine(\"after call\");\ntry { await t; }\ncatch (Exception e) { Console.WriteLine(e.GetType().Name); }","origin":"runtime"},{"id":"cs-async/out-async-032","topic":"cs-async","level":"middle","concept":"cs-async-pitfalls","q":"Что выведет код?","a":"AggregateException\nInvalidOperationException\n\n.Result и .Wait() блокируют поток и заворачивают ошибку в AggregateException; await и GetAwaiter().GetResult() бросают исходное исключение. await Task.WhenAll бросает только первое исключение — все лежат в Task.Exception.InnerExceptions.","code":"async Task<int> Fail() { await Task.Yield(); throw new InvalidOperationException(); }\ntry { _ = Fail().Result; }\ncatch (Exception e) { Console.WriteLine(e.GetType().Name); }\ntry { Fail().GetAwaiter().GetResult(); }\ncatch (Exception e) { Console.WriteLine(e.GetType().Name); }","origin":"runtime"},{"id":"cs-async/out-async-033","topic":"cs-async","level":"middle","concept":"cs-async-pitfalls","q":"Что выведет код?","a":"ArgumentException\n2\n\n.Result и .Wait() блокируют поток и заворачивают ошибку в AggregateException; await и GetAwaiter().GetResult() бросают исходное исключение. await Task.WhenAll бросает только первое исключение — все лежат в Task.Exception.InnerExceptions.","code":"async Task Throw(string kind) { await Task.Yield(); throw kind == \"a\" ? new ArgumentException() : new InvalidOperationException(); }\nvar all = Task.WhenAll(Throw(\"a\"), Throw(\"b\"));\ntry { await all; }\ncatch (Exception e) { Console.WriteLine(e.GetType().Name); }\nConsole.WriteLine(all.Exception!.InnerExceptions.Count);","origin":"runtime"},{"id":"cs-async/out-async-034","topic":"cs-async","level":"junior","concept":"cs-async-await","q":"Что выведет код?","a":"1,2\n1,2\n\nasync-метод выполняется синхронно до первого await, который реально ждёт, и только тогда возвращает Task. Исключение внутри async сохраняется в Task и выбрасывается при await. Последовательные await складывают время; Task.WhenAll запускает работу параллельно и сохраняет порядок результатов.","code":"async Task<int> Slow(int v, int ms) { await Task.Delay(ms); return v; }\nvar sequential = new List<int> { await Slow(1, 30), await Slow(2, 10) };\nvar parallel = await Task.WhenAll(Slow(1, 30), Slow(2, 10));\nConsole.WriteLine(string.Join(\",\", sequential));\nConsole.WriteLine(string.Join(\",\", parallel));","origin":"runtime"}],"quotes":[{"id":"plutarch-kindling","author":"Плутарх","text":"Ум нуждается не в наполнении, как сосуд, а, как дрова, лишь в растопке — чтобы в нём зажглось стремление мыслить самостоятельно и горячее желание истины.","work":"Moralia, «О том, как слушать» (De auditu) 18 / 48C","translation":"Пересказ на русский по англ. переводу F. C. Babbitt, Loeb Classical Library, 1927","verified":true,"evidence":"https://quoteinvestigator.com/2013/03/28/mind-fire/","note":"Популярная форма «ум — не сосуд, а факел» — сокращение, не цитата."},{"id":"seneca-docent-discunt","author":"Сенека","text":"Обучая, мы учимся.","work":"Нравственные письма к Луцилию, 7.8 (homines dum docent discunt)","translation":"Пересказ на русский по англ. переводу R. M. Gummere (Loeb Classical Library)","verified":true,"evidence":"https://en.wikisource.org/wiki/Moral_letters_to_Lucilius/Letter_7"},{"id":"confucius-analects-2-15","author":"Конфуций","text":"Учиться и не размышлять — напрасный труд; размышлять и не учиться — опасно.","work":"Беседы и суждения (Лунь юй), 2.15","translation":"Пересказ на русский по англ. переводу J. Legge (The Chinese Classics)","verified":true,"evidence":"https://www.gutenberg.org/cache/epub/3330/pg3330.txt"},{"id":"aristotle-learn-by-doing","author":"Аристотель","text":"То, чему нужно научиться, чтобы делать, мы учимся, делая.","work":"Никомахова этика, II.1, 1103a","translation":"Пересказ на русский по англ. переводу H. Rackham (вариант из примечания), Loeb 1934","verified":true,"evidence":"https://www.perseus.tufts.edu/hopper/text?doc=Perseus:text:1999.01.0054:bekker%20page=1103a"},{"id":"epictetus-already-knows","author":"Эпиктет","text":"Невозможно научиться тому, что, как тебе кажется, ты уже знаешь.","work":"Беседы, 2.17.1","translation":"Пересказ на русский по англ. переводу W. A. Oldfather, Loeb Classical Library, 1925","verified":true,"evidence":"https://en.wikisource.org/wiki/Epictetus,_the_Discourses_as_reported_by_Arrian,_the_Manual,_and_Fragments/Book_2/Chapter_17"},{"id":"sheldon-previous-self","author":"Уильям Л. Шелдон","text":"Помни: нет ничего благородного в том, чтобы превосходить другого человека. Истинное благородство — в том, чтобы превзойти себя прежнего.","work":"«What to Believe: An Ethical Creed», Ethical Addresses, апрель 1897","translation":"Наш перевод с английского оригинала по цитате у Quote Investigator","verified":true,"evidence":"https://quoteinvestigator.com/2017/03/11/superior/","note":"Обычно приписывают Хемингуэю — по Quote Investigator, фраза была в печати до его рождения (1899)."},{"id":"seneca-reformed-transformed","author":"Сенека","text":"Я чувствую, Луцилий, что не только исправляюсь, но и преображаюсь.","work":"Нравственные письма к Луцилию, 6.1","translation":"Пересказ на русский по англ. переводу R. M. Gummere (Loeb Classical Library)","verified":true,"evidence":"https://en.wikisource.org/wiki/Moral_letters_to_Lucilius/Letter_6"},{"id":"seneca-learn-while-ignorant","author":"Сенека","text":"Учиться нужно до тех пор, пока чего-то не знаешь, — а если верить пословице, то до конца жизни.","work":"Нравственные письма к Луцилию, 76.3","translation":"Пересказ на русский по англ. переводу R. M. Gummere (Loeb Classical Library)","verified":true,"evidence":"https://en.wikisource.org/wiki/Moral_letters_to_Lucilius/Letter_76"},{"id":"confucius-analects-9-18-mound","author":"Конфуций","text":"Учение можно сравнить с тем, как насыпают холм. Если не хватает лишь одной корзины земли, а я остановился, — остановился я сам. Его можно сравнить и с тем, как насыпают землю на ровном месте: пусть за раз бросаешь лишь одну корзину, но продвигаюсь вперёд я сам.","work":"Беседы и суждения (Лунь юй), 9.18 (в части изданий — 9.19)","translation":"Пересказ на русский по англ. переводу J. Legge (The Chinese Classics)","verified":true,"evidence":"https://www.gutenberg.org/cache/epub/3330/pg3330.txt","note":"Популярное «Неважно, как медленно ты идёшь, главное — не останавливаться» в «Лунь юе» не найдено — это поздний пересказ."},{"id":"laozi-thousand-li","author":"Лао-цзы","text":"Дерево в обхват вырастает из крошечного ростка; башня в девять ярусов поднимается из горстки земли; путь в тысячу ли начинается с одного шага.","work":"Дао дэ цзин, гл. 64","translation":"Пересказ на русский по англ. переводу J. Legge","verified":true,"evidence":"https://www.gutenberg.org/cache/epub/216/pg216.txt","note":"Часто приписывают Конфуцию — это Лао-цзы. Буквально 始於足下: «начинается из-под ног»."},{"id":"epictetus-nothing-great-suddenly","author":"Эпиктет","text":"Ничто великое не возникает сразу — даже виноградная гроздь или смоква. Пусть дерево сначала зацветёт, потом даст плод, а потом плод созреет.","work":"Беседы, 1.15.7–8","translation":"Пересказ на русский по англ. переводу W. A. Oldfather, Loeb Classical Library, 1925","verified":true,"evidence":"https://en.wikisource.org/wiki/Epictetus,_the_Discourses_as_reported_by_Arrian,_the_Manual,_and_Fragments/Book_1/Chapter_15"},{"id":"epictetus-practise-small-things","author":"Эпиктет","text":"Упражняйся в малом и, начав с него, переходи к большему.","work":"Беседы, 1.18.18","translation":"Пересказ на русский по англ. переводу W. A. Oldfather, Loeb Classical Library, 1925","verified":true,"evidence":"https://en.wikisource.org/wiki/Epictetus,_the_Discourses_as_reported_by_Arrian,_the_Manual,_and_Fragments/Book_1/Chapter_18"},{"id":"epictetus-read-to-be-a-reader","author":"Эпиктет","text":"Всякая привычка и способность укрепляется соответствующими действиями: умение ходить — ходьбой, умение бегать — бегом. Хочешь хорошо читать — читай; хочешь хорошо писать — пиши.","work":"Беседы, 2.18.1–2","translation":"Пересказ на русский по англ. переводу W. A. Oldfather, Loeb Classical Library, 1925","verified":true,"evidence":"https://en.wikisource.org/wiki/Epictetus,_the_Discourses_as_reported_by_Arrian,_the_Manual,_and_Fragments/Book_2/Chapter_18"},{"id":"marcus-aurelius-be-such","author":"Марк Аврелий","text":"Хватит рассуждать о том, каким должен быть хороший человек, — будь им.","work":"Размышления (К самому себе), 10.16","translation":"Пересказ на русский по англ. переводу G. Long","verified":true,"evidence":"https://www.gutenberg.org/cache/epub/15877/pg15877.txt"},{"id":"marcus-aurelius-return-back","author":"Марк Аврелий","text":"Не досадуй, не падай духом и не отчаивайся, если не всё удаётся делать по верным правилам. Потерпев неудачу, возвращайся снова — и довольствуйся тем, что бо́льшая часть твоих дел достойна человека.","work":"Размышления (К самому себе), 5.9","translation":"Пересказ на русский по англ. переводу G. Long","verified":true,"evidence":"https://www.gutenberg.org/cache/epub/15877/pg15877.txt"},{"id":"chekhov-drop-by-drop","author":"Антон Чехов","text":"…напишите, как этот молодой человек выдавливает из себя по каплям раба и как он, проснувшись в одно прекрасное утро, чувствует, что в его жилах течет уже не рабская кровь, а настоящая человеческая...","work":"Письмо А. С. Суворину, 7 января 1889 г. (ПСС в 30 т., Письма, т. 3, М.: Наука, 1976, с. 131–133)","verified":true,"evidence":"https://chehov-lit.ru/chehov/letters/1888-1889/letter-579.htm","note":"Расхожее «Нужно по капле выдавливать из себя раба» — пересказ; у Чехова речь о герое предлагаемого рассказа."},{"id":"chekhov-every-hour-counts","author":"Антон Чехов","text":"Тут нужны беспрерывный дневной и ночной труд, вечное чтение, штудировка, воля... Тут дорог каждый час...","work":"Письмо Н. П. Чехову, март 1886 г. (ПСС в 30 т., Письма, т. 1, М.: Наука, 1974, с. 221–225)","verified":true,"evidence":"https://feb-web.ru/feb/chekhov/texts/sp0/pi1/pi1-2212.htm?cmd=p"}]}