Системное мышление Что считать системой: элементы, связи, назначение
0%

Что считать системой: элементы, связи, назначение

Что считать системой: элементы, связи, назначение

Слово «система» в инженерной речи почти ничего не значит. Системой называют монолит и набор микросервисов, процесс код-ревью и таблицу в Confluence, компанию и очередь сообщений. Когда слово применимо ко всему, оно перестаёт различать — а различение и есть единственная причина заводить термин.

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

Карта трека — в обзоре. Здесь важна одна разграничительная линия. Трек про логику занимается строгостью вывода: следует ли заключение из посылок. Системное мышление — тем, как ведёт себя целое во времени, когда все локальные шаги логичны, а результат противоположен намерению. Логика проверяет рассуждение, системная модель проверяется данными.

Определение: три составляющие

Определение, ставшее в дисциплине стандартным, дала Донелла Медоуз в «Thinking in Systems: A Primer» (2008):

Система — это набор элементов, связанных между собой так, что их взаимодействие порождает собственное устойчивое поведение.

  1. Элементы — то, из чего система состоит. Обычно их видно: сервисы, воркеры, таблицы, очереди, дежурные, тикеты, люди в команде.
  2. Взаимосвязи — кто на кого влияет, через что, с какой задержкой и по каким правилам. Видно хуже: половина связей не описана нигде, а живёт в привычках и конфигах.
  3. Назначение — что система устойчиво производит. Видно хуже всего: его почти никогда не проговаривают вслух, а декларации расходятся с фактом.

Три слоя системы: элементы, связи, назначение

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

Пример — асинхронная обработка заказов. Элементы: API-эндпоинт, брокер, пул из 20 воркеров, база, клиентское приложение, автоскейлер, алерт на длину очереди, дежурный. Связи: клиент шлёт запрос → эндпоинт кладёт сообщение в очередь → воркер забирает и пишет в базу; клиент ждёт 2 секунды и повторяет; автоскейлер добавляет воркеров, если очередь держится выше порога 5 минут; алерт будит дежурного через 15 минут. Назначение: заявленное — «обработать все заказы»; фактическое — «не потерять заказ ценой любой задержки», потому что ретраи бесконечны, TTL сообщения не задан, таймаута обработки нет.

Заметьте: фактическое назначение мы прочитали не из документа, а из конфигурации. Это общий приём.

Тест на систему: три вопроса

Куча деталей — не система. Отличить можно тремя грубыми, но работающими проверками.

1. Заменим элемент — поведение сохранится? Если да, система существует и элемент в ней не определяющий. Замените все 20 воркеров на другие инстансы — очередь ведёт себя так же. Замените половину команды — процесс релиза в первый месяц будет тем же, потому что его держат правила и связи. Если поведение исчезает при замене одного элемента, вы смотрели не на систему, а на этот элемент с окружением.

2. Переставим связи, оставив элементы — поведение изменится? Тот же набор сервисов, но ретрай перенесён с клиента на шлюз с бюджетом попыток — и метастабильного отказа больше нет. Ничего не добавили и не убрали. Если перестановка связей ничего не меняет, элементы независимы: перед вами набор, а не система.

3. Есть ли что-то, что целое устойчиво производит? Если объект не производит ничего повторяемого, у него нет назначения, и говорить о его «поведении» бессмысленно. Ящик с болтами не система: болты не взаимодействуют.

Сведём в картинку: по горизонтали — насколько части влияют друг на друга, по вертикали — есть ли устойчиво воспроизводимый результат.

Правый нижний квадрант — самый неприятный. Части плотно связаны, работы много, устойчивого результата нет: планёрки, дашборды, отчётность, которая никуда не ведёт. Формально система есть, но её назначение — воспроизводить саму себя. Это не риторика, а проверяемое утверждение: отмените ритуал на два спринта и посмотрите, изменится ли хоть одна наблюдаемая метрика.

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

Назначение: самое незаметное и самое сильное

С элементами и связями инженеры работают каждый день. Назначение пропускают — и именно из-за него системные разборы чаще всего расходятся с реальностью.

Полезная формулировка принадлежит Стаффорду Биру: назначение системы — это то, что она делает (POSIWID, «The Heart of Enterprise», 1979). С ней надо быть аккуратным: прочитанная буквально, она тавтологична и потому неопровержима — что бы система ни делала, объявим это её целью. Как аргумент она ничего не стоит. Как эвристика поиска работает: не спрашивайте людей о намерениях, смотрите, что система защищает, когда ресурсов не хватает на всё.

Ситуация конфликта Что выбрали Фактическое назначение
Очередь растёт, ретраи усугубляют Не отбрасывать сообщения, терпеть задержку «Не потерять заказ любой ценой»
Релиз готов, упал флейки-тест Выкатили, тест замьютили «Не задерживать релиз»
Инцидент закрыт, постмортема нет Закрыли задачу, разошлись «Восстановить сервис», не «понять причину»
Ревью висит 3 дня, автор торопится Апрув «на доверии» «Не блокировать поток», не «найти дефекты»
Дежурный завален алертами Порог поднят, алерт замьючен «Спать ночью», не «замечать деградации»

Ни одна строка сама по себе не приговор: «не задерживать релиз» — законное назначение, если выбрано осознанно. Проблема начинается, когда декларируется одно, а система настроена на другое. Тогда улучшения бьются о невидимое ограничение: вы поднимаете качество ревью, а система устроена так, чтобы ревью не задерживало поток, — и она вернёт всё обратно.

Отсюда первое проверяемое утверждение главы:

Гипотеза. Если декларируемое назначение расходится с фактическим, локальные улучшения под декларируемое назначение затухают за 1–3 итерации. Проверка. Возьмите три инициативы последнего года («ускорить ревью», «чинить флейки», «писать постмортемы»), найдите момент внедрения в истории репозитория и метрику, которую они должны были двигать; постройте её на 6 месяцев вперёд. Возврат к прежнему уровню — гипотеза выдержала проверку на ваших данных. Что опровергнет. Устойчивое улучшение без изменения правил приоритизации.

Это ещё не доказательство: выборка мала, объяснений может быть несколько. Но это уже измерение, а не рассуждение, — и в этом вся разница между системной моделью и красивой схемой.

Первый разбор: очередь, которая не восстанавливается

Средняя нагрузка 90 запросов в секунду, пул держит 100 — запас 10%. Всплеск на 30 секунд поднимает нагрузку до 160, потом всё возвращается к 90. Восстановится ли сервис сам? Интуиция говорит «конечно, нагрузка же вернулась». Системная модель отвечает: зависит от того, есть ли петля ретраев.

Здесь две петли. Q → S → Q со знаком минус — уравновешивающая: чем длиннее очередь, тем больше работы из неё уходит, очередь сама себя укорачивает. A → Q → W → T → R → A — все связи положительные, петля усиливающая: рост порождает рост. Правило чтения знаков: чётное число минусов в контуре — усиливающая петля, нечётное — уравновешивающая. Подробно петли — в главе 03, различие «запас против потока» — в главе 04; здесь достаточно того, что очередь — это запас, а не поток, и она помнит прошлое.

Проверим численно. Модель намеренно грубая: шаги по секунде, ожидание оценивается по закону Литтла как W = L / λ, клиент делает один повтор, если ожидание превысило таймаут.

from dataclasses import dataclass


@dataclass
class Params:
    arrival: float = 90.0        # запросов в секунду в норме
    capacity: float = 100.0      # запросов в секунду, которые вытягивает пул
    timeout: float = 2.0         # секунд ждёт клиент до повтора
    retries: float = 1.0         # сколько повторов делает клиент
    burst_from: float = 60.0     # начало всплеска, секунда
    burst_to: float = 90.0       # конец всплеска, секунда
    burst_arrival: float = 160.0 # нагрузка во время всплеска


def simulate(p: Params, seconds: int = 600, with_retries: bool = True):
    """Дискретная модель очереди с петлёй клиентских ретраев.

    Возвращает историю (t, длина очереди, оценка ожидания).
    Сложность: O(seconds) по времени и памяти; O(1) по памяти, если не копить историю.
    """
    queue = 0.0
    history = []
    for t in range(seconds):
        base = p.burst_arrival if p.burst_from <= t < p.burst_to else p.arrival
        wait = queue / p.capacity                   # закон Литтла: W = L / λ
        # клиент повторяет запрос, только если уже ждал дольше таймаута
        retry = base * p.retries if (with_retries and wait > p.timeout) else 0.0
        inflow = base + retry                       # поток «в» запас
        outflow = min(queue + inflow, p.capacity)   # поток «из», ограничен пулом
        queue = queue + inflow - outflow            # запас = запас + приток − отток
        history.append((t, queue, wait))
    return history
Момент Без ретраев С ретраями
t = 89 с (конец всплеска) очередь 1800, ожидание 17 с очередь 5960, ожидание 57 с
t = 120 с очередь 1490, спадает очередь 8440, растёт
t = 269 с очередь 0, сервис в норме очередь ~20 400, растёт
t = 599 с очередь 0 очередь ~46 800, ожидание 467 с

Без ретраев система восстанавливается за 269 секунд после тридцатисекундного всплеска — уже интересно: причина исчезла за три минуты до того, как исчезло следствие. С ретраями система не восстанавливается никогда, хотя внешняя нагрузка вернулась к 90 из 100. Причина видна прямо в модели: 90 исходных плюс 90 повторных — это 180 против ёмкости 100, очередь растёт на 80 в секунду. Петля ретраев стала источником нагрузки, которая поддерживает сама себя.

Это не выдумка ради примера: явление известно как метастабильный отказ и описано на реальных системах в Bronson et al., «Metastable Failures in Distributed Systems» (HotOS 2021, PDF). Средства против него — бюджеты ретраев, jitter, circuit breaker, load shedding — разобраны в паттернах устойчивости и в моделях отказов.

Что здесь сделала системная модель: не назвала новое лекарство, а объяснила, почему восстановление не наступает само, и указала, куда смотреть — на контур, а не на элемент. Инстинкт «добавим воркеров» лечит симптом: при ёмкости 200 очередь рассосётся, но петля останется и сработает при всплеске побольше.

Переход Meta → Meta — главное на диаграмме. У системы два устойчивых режима при одной и той же входной нагрузке, и в какой из них она попадёт, зависит от истории. Это уже про нелинейность и пороги, но эффект виден уже из трёхкомпонентного определения: у запаса есть память, у петли — знак.

Элементы менять легко, связи трудно, назначение почти невозможно

Замена элементов почти не меняет поведение. Заменили брокер, переписали сервис на другом языке, поменяли половину команды — если связи и назначение те же, система возвращается к прежнему поведению. Отсюда знакомое «переписали с нуля, а через год те же проблемы».

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

Это утверждение доводимо до проверяемого вида. Гипотеза Конвея — структура системы повторяет структуру коммуникаций создавшей её организации («How Do Committees Invent?», 1968, текст) — десятилетиями жила как афоризм, пока MacCormack, Rusnak и Baldwin не сравнили парные продукты, сделанные распределёнными сообществами и колокированными командами, и не нашли систематическую разницу в модульности в предсказанную сторону (Research Policy, 2012, препринт HBS). Это не эксперимент, контроля над отбором нет, но это данные, а не афоризм.

Смена назначения меняет всё, но её почти никогда не делают явно. Фраза «мы оптимизируем время до восстановления, а не количество инцидентов» — это смена назначения, и из неё следуют другие алерты, другие постмортемы, другой найм. Организационная сторона такого выбора — в главе про техдолг.

Система, подсистема, надсистема

Одно и то же можно рассматривать как элемент или как систему — в зависимости от вопроса: воркер — элемент очереди, очередь — система для команды платформы, платформа — элемент продукта, продукт — элемент бизнеса.

Полезное свойство таких иерархий описал Герберт Саймон в «The Architecture of Complexity» (1962, JSTOR): устойчивые сложные системы, как правило, почти разложимы — взаимодействия внутри подсистемы сильнее и быстрее, чем между подсистемами. Поэтому можно анализировать очередь, не моделируя биржу труда, и поэтому граница по «сильным быстрым связям» обычно даёт рабочую модель. Идея та же, что за ограниченными контекстами в DDD, только сформулированная про поведение, а не про модель предметной области.

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

Что системой не является

Набор без взаимодействия. Список из 40 сервисов в вики — инвентарь. Он станет системой, когда вы добавите, кто кого вызывает и что происходит при отказе.

Поток без запаса. Если между частями ничего не накапливается (очередь, бэклог, долг, усталость, недоверие), интересной динамики не будет: выход мгновенно следует за входом. Запасы — источник инерции, а инерция — источник неожиданностей.

«Система» как синоним «сложно». Сложность не делает объект системой, а система не обязана быть сложной: термостат — система из трёх элементов, и он показывает всё интересное сразу.

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

Где модель проверяема, а где нет

Схема со стрелочками убедительна ровно настолько, насколько ничего не запрещает, — а неопровержимое объяснение бесполезно. Общая методология проверки гипотез разобрана в главе о научном и инженерном рассуждении; здесь — что можно требовать от системной модели.

Уровень 1. Предсказание знака. «При агрессивном ретрае без бюджета и всплеске выше 100% ёмкости очередь не вернётся к нулю после спада нагрузки». Проверяется нагрузочным тестом за час, запрещает наблюдаемый исход.

Уровень 2. Предсказание формы поведения. «После найма трёх инженеров скорость команды сначала упадёт и вернётся не раньше квартала». Форма — провал и восстановление — проверяется по трекеру; величина провала нет.

Уровень 3. Предсказание времени и величины. «Очередь восстановится за 269 секунд». В модели да, в реальности нет: числа зависят от распределения времени обработки, поведения клиентов, работы автоскейлера. Точное число здесь артефакт модели. Как отличать одно от другого — тема главы про моделирование и его пределы.

Уровень 0, непроверяемое. «Организация — живой организм, и техдолг — это её усталость». Метафора не запрещает ничего; из неё не следует ни одного наблюдения, которое могло бы её опровергнуть.

Рабочее правило: нарисовали схему — сформулируйте, какое наблюдение её опровергнет. Не нашли — вы нарисовали не модель, а иллюстрацию к своей интуиции; для разговора она полезна, за анализ выдавать нельзя. Второй способ проверки — сверка с уже измеренным: если модель очереди противоречит закону Литтла или базовой теории массового обслуживания, ошибка в модели, а не в теории (про измерения и их подводные камни — «Измерения»).

Честно про доказательную базу

Системная динамика выросла из работ Джея Форрестера в MIT: «Industrial Dynamics» (1961) — производственные цепочки, «Urban Dynamics» (1969) — города, «World Dynamics» (1971) — глобальные ресурсные ограничения. Донелла Медоуз сделала подход популярным: «The Limits to Growth» (1972, Club of Rome) и посмертный «Thinking in Systems» (2008). Учебник дисциплины — John Sterman, «Business Dynamics» (2000).

Есть воспроизводимый эксперимент. Beer Distribution Game — лабораторная цепочка поставок из четырёх звеньев с задержками. Стерман показал в «Modeling Managerial Behavior» (Management Science, 1989, DOI), что участники систематически переоценивают заказы, не учитывая уже отправленный, но не пришедший товар, — колебания возникают из структуры, а не из глупости игроков. Эксперимент повторяли десятилетиями на тысячах участников с устойчивым результатом. Это самое твёрдое, что есть в дисциплине, и это прямой аналог инженерных ситуаций с задержками — им посвящена глава 05.

Есть серьёзная критика моделей. Уильям Нордхаус в рецензии «World Dynamics: Measurement Without Data» (The Economic Journal, 1973, JSTOR) указал главное: уравнения содержат десятки коэффициентов, подобранных без эмпирической оценки, а поведение модели к ним чувствительно. Сборник «Models of Doom» (Cole, Freeman, Jahoda, Pavitt, 1973) показал, что при других допущениях о темпе технологического прогресса коллапс из модели исчезает. Отдельная претензия — что «Urban Dynamics» давала рекомендации по городской политике на модели, которая с городскими данными не сверялась.

Есть попытки сверки с реальностью, и они спорны. Грэм Тёрнер (Global Environmental Change, 2008, DOI) сопоставил сценарий «business as usual» с данными 1970–2000 и нашёл согласие по ряду агрегатов. Критики отвечают, что согласие на тридцатилетнем горизонте по медленно меняющимся агрегатам — слабый тест: сценарии расходятся позже, а сама книга подчёркивала, что это сценарии, а не прогнозы.

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

Отрезвляющее чтение с инженерной стороны: Richard Cook, «How Complex Systems Fail» (текст) — почему сложные системы всегда работают деградированными; и «Systemantics» Джона Голла (1975), откуда закон «сложная работающая система неизменно оказывается развитием простой работающей системы». Оба текста — наблюдения, а не исследования, и читать их надо соответственно.

Типовые ловушки

Подробно трек разбирает их в главах об архетипах, локальной оптимизации и причинных диаграммах — здесь важно узнавать их уже на этапе «что мы вообще описываем».

Описание вместо модели. Нарисовали все сервисы и все стрелки — получили карту архитектуры, а не модель поведения. Признак: на схеме нет ни одного запаса и ни одной петли. Модель отвечает на «что будет, если», а не на «что где лежит».

Оптимизация части в ущерб целому. Каждая команда улучшила свою метрику, сквозное время выросло. Классика — локальная утилизация: загрузили каждый узел на 90%, очереди перед узлами съели весь выигрыш. Опасность в том, что каждый шаг локально подтверждён данными.

Перенос проблемы. Симптом лечится обходным решением, обход обрастает процессом, процесс становится зависимостью. Инженерный вид: скрипт «перезапустить сервис при росте памяти» → он в кроне → на него алерт → на алерт регламент дежурного → через год про утечку никто не помнит, а в системе живёт подсистема обслуживания костыля. Признак: у обхода появился владелец и SLA.

Эскалация. Две стороны отвечают на действия друг друга усилением: команда A ставит ретраи, команда B — рейт-лимит, A увеличивает параллелизм, B ужесточает лимит. Каждый шаг рационален, контур усиливающий.

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

Схема как аргумент. Самая коварная. Диаграмма выглядит убедительно, все кивают, решение принято — и никто не спросил, какое наблюдение её опровергло бы. Особенно опасно, когда схему рисует самый старший в комнате: разные участники видят разные системы, об этом — глава 08.

Практика: описать систему за 30 минут

В машиночитаемом виде — заготовка, которую удобно держать рядом с runbook:

system: "Асинхронная обработка заказов"
purpose:
  declared: "Обработать все заказы за 5 секунд p99"
  actual: "Не потерять заказ ценой любой задержки"     # прочитано из конфигурации
  evidence: "retries=infinite, ttl=none, dlq=отсутствует"
stocks:                                                # то, что накапливается
  - {name: "Очередь заказов", unit: "сообщений", normal: 0, observed_max: 46000}
  - {name: "Незакрытые постмортемы", unit: "штук"}
flows:
  - {from: "Клиент", to: "Очередь", rate: "90/с, всплески до 160/с"}
  - {from: "Очередь", to: "База", rate: "100/с, ограничение пула"}
loops:
  - {id: R1, kind: reinforcing, path: "очередь -> ожидание -> таймауты -> ретраи", trigger: "ожидание > 2 с"}
  - {id: B1, kind: balancing, path: "очередь -> обработка -> очередь", limit: "ёмкость пула"}
delays:
  - {what: "Автоскейлер", lag: "5 минут"}
  - {what: "Пробуждение дежурного", lag: "15 минут"}
prediction: "После всплеска >100% ёмкости очередь не вернётся к нулю без вмешательства"
falsifier: "Очередь опустела сама в течение 10 минут после спада нагрузки"
check: "Нагрузочный тест 30 секунд на 160% ёмкости, наблюдать 20 минут"

Два поля важнее остальных — prediction и falsifier. Не удаётся заполнить — описание не превратилось в модель, и честнее это признать, чем нести схему на архитектурное ревью; заполненные превращают спор в эксперимент на час.

Порядок работы с нуля: (1) назовите поведение, которое хотите объяснить, — не «опишем систему», а «почему очередь не рассасывается»; (2) найдите запасы: очередь, бэклог, долг, незакрытые алерты, усталость — они скелет модели; (3) проведите границу — что внутри, что снаружи, что считаем константой (этому посвящена следующая глава); (4) ищите петли, а не стрелки — стрелка без обратного пути не объясняет динамику; (5) отметьте задержки: любая связь длиннее нескольких минут — кандидат в источник неожиданного поведения; (6) сформулируйте предсказание и опровержение и проверьте на данных, которые у вас уже есть. Полноразмерные разборы — инцидент, техдолг, найм, очередь — в главе 14; с чего начать практику и что читать дальше — в главе 15.

Мини-итог

  • Система = элементы + связи + назначение; определение рабочее, потому что из него следуют проверяемые ожидания о поведении.
  • Элементы менять почти бесполезно; связи менять трудно и это меняет поведение резко; назначение обсуждают редко, а определяет оно всё — и читается оно из выбора в конфликте ресурсов, а не из документов.
  • Куча — не система: нужен и обмен влиянием между частями, и устойчиво воспроизводимый результат.
  • Запас даёт системе память, петля даёт знак; вместе они объясняют, почему причина исчезает раньше следствия.
  • Модель считается моделью, если сформулировано, какое наблюдение её опровергнет. Иначе это иллюстрация.
  • Доказательная база неоднородна: лабораторные эффекты вроде beer game воспроизводимы, крупные модели вроде «Пределов роста» десятилетиями оспариваются по калибровке. Это язык гипотез, а не машина прогнозов.

Что дальше

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

Границы системы: как выбор границы меняет вывод

Источники

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов