name: SOS_AL
description: "Success-Oriented Software Architecture & Logic. Methodology for designing architectures and control logic that optimize for the success path first, then layer defensive rigor around it without burying the happy path. Use when planning a new system, reviewing an architecture that feels over-engineered or fragile, designing request/state/error flows, or untangling nested failure-handling code."


Success-Oriented Software Architecture & Logic

SOS_AL — это способ мышления о структуре системы, а не исполняемая команда. Применяй его как линзу при проектировании, ревью и рефакторинге: когда планируешь новую систему, разбираешь «переусложнённую» или хрупкую архитектуру, проектируешь потоки запросов/состояний/ошибок, или распутываешь вложенный код обработки отказов.

Core principle

Оптимизируй для успеха, защищай от провала — но не путай порядок.

Большинство систем 95% времени работают в happy path. Архитектура, которая проектируется «от ошибок» (defensive-first), размывает успех за слоями guard'ов, валидаций и fallback'ов, пока основная логика не станет нечитаемой. Успех должен быть виден на поверхности. Защита — слоем ниже, не наоборот.

Признаки нарушения:

  • Чтобы понять «что система делает в норме», нужно прочитать 200 строк валидаций и try/catch раньше самой логики.
  • Каждое изменение требует трассировки через 5 уровней абстракции, где 4 — обработка «а вдруг».
  • Тесты happy path писать сложно, потому что путь замаскирован.

The three layers

Проектируй систему как три явных слоя, сверху вниз:

1. Success layer (surface)

Главная логика. То, ради чего система существует. Должна читаться как прямолинейный сценарий: «получили запрос → применили правило → записали результат → ответили». Минимум ветвлений. Если для описания нормы нужен if — это уже второй слой.

  • На этом уровне нет try/catch вокруг каждой строки.
  • Нет проверок «а вдруг данные невалидны» — контракт на входе уже гарантирует валидность (см. слой 3).
  • Имена функций описывают бизнес-действие, а не защиту: chargeCard, не safeChargeCardWithFallback.

2. Boundary layer (contracts)

Точка, где внешний мир встречается с внутренним. Здесь данные превращаются из «недоверенные» в «достоверные по контракту». Валидация, парсинг, нормализация, auth. Одно место, один проход.

  • Контракт = «после этого слоя downstream-код имеет право считать данные корректными».
  • Никакой валидации повторно в глубине системы. Если пришлось — контракт сломан, чинить контракт, не плодить проверки.
  • Ошибки здесь — это про отказ во входе, не про ветвление логики.

3. Failure layer (defense)

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

  • Изолирован от success layer: retries, circuit breakers, dead-letter queues, fallbacks живут здесь.
  • Не инлайнится в бизнес-логику. Оборачивает её.
  • Каждая защита отвечает на вопрос: «какой класс отказа покрываем и что система делает, когда он случился?» Если ответа нет — защита декоративная, убирай.

Design sequence

Когда проектируешь новую систему, иди в этом порядке — не наоборот:

  1. Опиши happy path словами. Один абзац. «Пользователь нажал X → мы делаем Y → результат Z». Если не можешь описать просто — ты не понимаешь систему, и никакая архитектура не спасёт.
  2. Нарисуй success layer. Компоненты и поток для нормы. Здесь видно основную форму.
  3. Определи контракты на границах. Что входит, какие гарантии. Что считается валидным, что — отказом.
  4. Перечисли классы отказов. Не «все возможные ошибки», а категории: ввод, зависимости, инфраструктура, состояние.
  5. Для каждого класса — выбери стратегию защиты. Reject, retry, degrade, fail-fast. Обоснуй, почему именно эта.
  6. Размести защиту в failure layer, не в success. Убедись, что success layer остался читаемым.

Если на шаге 6 success layer перестал быть прямолинейным — ты переносишь защиту не туда. Возвращайся к шагу 4.

Logic principles

Явные состояния, не скрытые флаги

Состояние системы — конечный набор именованных состояний, не набор булев-флагов, комбинации которых никто не помнит. Если isActive && !isPaused && isVerified && !flagX определяет «готов к работе» — это скрытое состояние. Вынеси в enum Status { PENDING, VERIFIED, ACTIVE, PAUSED, ARCHIVED }.

Guard clauses раньше, не глубже

Ранний возврат на невалидный ввод — на границе. Внутри функции — никаких «если не наш случай, возвращаем null». Если функция вызвана, её контракт уже выполнен. Гнездование if/else if/else глубже двух уровней — запах.

Решения принимаются один раз

Не пере-валидируй то, что уже проверено выше по стеку. Не пере-парсь то, что уже распарсено. Каждое повторное решение — источник расхождения: верх проверил по одним правилам, низ — по другим, и они разъехались.

Side effects на краю

Чистые функции в центре (success layer), ввод-вывод и мутации — на границах. Логика, принимающая решения, не должна одновременно писать в БД и слать HTTP. Раздели: «решили» → «применили».

Пессимизм на границе, оптимизм внутри

Внешнему миру не доверяем: валидируем, лимитируем, таймаутим. Внутри системы доверяем контрактам своих же компонентов. Внутренний паранойя — признак сломанных контрактов, не надёжности.

Failure strategies — pick deliberately

Не каждая ошибка требует одинаковой реакции. Выбирай осознанно:

Стратегия Когда Когда НЕ
Reject fast Неверный ввод, нарушение контракта Когда отказ внешнего сервиса можно пережить
Retry with backoff Транзиентные сетевые сбои, таймауты Когда операция неидемпотентна и нет компенсации
Circuit breaker Зависимость стабильно больна Для единичных сбоев
Degrade Есть смысл вернуть частичный результат Когда частичный результат введёт в заблуждение
Fail loud Несовместимое состояние, bug-class Никогда не молча проглатывать
Compensate Распределённые транзакции, saga Если есть более простой локальный вариант

Антипаттерн: catch-all try { ... } catch (e) { log(e); return null; }. Это не защита — это сокрытие. Система продолжает «работать» в сломанном состоянии, и проблема всплывёт через неделю в данных.

Review checklist

При ревью архитектуры или кода проверяй:

  • Happy path читается сверху вниз как обычный сценарий?
  • Есть ли явная граница контрактов, после которой данные считаются валидными?
  • Повторяется ли валидация в глубине? (должна быть только на границе)
  • Состояние выражено именованными состояниями, не булевыми комбо?
  • Каждая защита в failure layer отвечает «какой класс отказа и что делаем»?
  • Есть ли catch-all-проглатыватели ошибок?
  • Сайд-эффекты отделены от логики принятия решений?
  • Решение принимается один раз, не пере-проверяется на каждом уровне?

When to use this lens

  • Новая система. Проектируешь с нуля — иди по Design sequence выше.
  • Ревью архитектуры. Чувствуешь, что «переусложнено» или «хрупко» — проверь через checklist. Часто диагноз: защита в success layer, контракты размыты, состояния скрыты.
  • Рефакторинг «спагетти». Вложенные try/catch и if-guard'ы — это почти всегда defensive-first дизайн. Вытащи норму на поверхность, защиту собери в слой.
  • Дизайн API/протокола. Контракт на границе — самое важное решение. Что валидно, что нет, какие гарантии. От него зависит, насколько чистым будет downstream.
  • Потоки запросов и состояний. Машина состояний, оркестрация, saga — здесь success-ориентированность особенно ценна, потому что сложность отказа растёт комбинаторно.

When NOT to apply

  • Скрипт-одноразовка. Защита не нужна, успех — единственный путь. Не нагружай слоями.
  • Система, где отказ = катастрофа. Safety-critical, финансы с двойной проводкой, medical. Здесь defensive-first оправдан — успех должен быть обёрнут гарантиями. Но успех всё равно должен быть виден; просто граница между слоями сдвигается.
  • Уже простая система. Не вводи слои ради слоёв. Трёхслойная модель — инструмент, не догма. Если система влезает в 50 строк — оставь как есть.

Smells to recognize

  • «Чтобы понять, что делает функция, нужно пропустить первые 40 строк проверок.» → защита в success layer.
  • «Я не уверен, что вернёт этот вызов — может null, может пустой список, может бросит.» → сломанный контракт.
  • «Добавил флаг useNewPath, теперь в трёх местах if на него.» → скрытое состояние, нужно явное.
  • «Ошибку логируем и продолжаем.» → catch-all-проглатыватель.
  • «Эта валидация продублирована в контроллере, сервисе и репозитории.» → контракт не несёт гарантии, его никто не уважает.

Output discipline

Когда применяешь эту линзу в работе (дизайн-док, ревью-комментарий, рефакторинг-план):

  • Сначала норма. Описываешь систему — начни с happy path одним абзацем. Потом границы, потом защита. Порядок отражает приоритет.
  • Каждый компонент — одной фразой. Что он делает в успехе. Если фраза не складывается — компонент делает слишком много.
  • Защита с обоснованием. Не «добавим retry», а «retry на шаге 3, потому что зависимость транзиентна, backoff экспоненциальный до 4 попыток, после — circuit breaker».
  • Честно помечай пробелы. «Контракт на поле X не определён — пока считаем валидным всё, кроме null». Не маскируй незнание под «на всякий случай добавим проверку».
Edit

Pub: 28 Aug 2026 21:10 UTC

Views: 6