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