Мир, в котором живут разработчики
Я разработчик, и большую часть жизни я провёл в мире, где x = 5 значит ровно "пять" — не "около пяти", не "вероятно пять", а именно "пять". Тест проходит или не проходит, деплой либо задеплоился, либо откатился, миграция либо выполнилась, либо нет. В коде нет полутонов, и это даёт приятное чувство контроля над тем, что ты делаешь.
Этот способ думать не остаётся в коде — он просачивается во всё остальное. Я произношу "проект займёт месяц", "этот кандидат подходит", "фреймворк X лучше, чем Y", "продукт взлетит" как утверждения, а не как оценки вероятностей. Разработчик привыкает жить в мире определённости, и дальше эта привычка бессознательно переносится на области, где она перестаёт работать — на оценку сроков, на решения о найме, на гипотезы продукта, на планирование запусков. Я не замечал этого годами, пока не начал заниматься робототехникой.
Дрон, который не знает, где он
Последний год я собираю автономные дроны и наземных роботов — это часть большого образовательного проекта, которым я занимаюсь. И для этого я погрузился в историю робототехники — где шестьдесят лет инженеры искали работающий подход и находили его в самых неожиданных местах.
Первые серьёзные попытки сделать автономного робота начались в 1960-70-х годах — классический пример того времени это Shakey, робот из Стэнфордского исследовательского института. Идея была прямая, инженерная: у робота есть карта мира, есть точное знание своей позиции, есть логический планировщик, который строит план действий от текущего состояния к целевому. План исполняется. Если всё точно — всё работает. В лаборатории с белыми стенами и чёрными метками на полу Shakey действительно работал. Минут пять. Потом начинал сбиваться, потому что реальный мир оказывался сложнее модели. Колёса проскальзывали чуть-чуть иначе, чем заложено, сенсоры возвращали не совсем то, что ожидалось, положение отклонялось, накапливалась ошибка — и робот, уверенный в том, что он в точке A, на самом деле уже был в точке B, и действовал неправильно. Этот подход доминировал десятилетиями и упирался в одну и ту же стену: как только робот выходил за пределы стерильной лаборатории, всё разваливалось.
В 1990-х появилась радикально другая идея. Родни Брукс из MIT предложил отказаться от внутренней модели мира вообще — дескать, не надо ничего моделировать в голове робота, мир сам по себе своя лучшая модель. Вместо плана — набор простых реактивных поведений: увидел препятствие — поверни, увидел свет — иди туда, почувствовал стену — отойди. Никакой карты, никакой логики, никакого планирования. Как у муравья или пчелы — простые правила, а сложное поведение получается само собой из их взаимодействия. Это тоже частично работало: первые роботы-пылесосы Roomba построены примерно по этой логике, и они действительно ездят по комнатам и убирают. Но у подхода обнаружился свой потолок: без модели мира невозможно ставить сложные цели. Муравей не может "пойти в точку A, потом в B, избегая зоны C". Для этого нужно где-то представлять эти точки и как между ними двигаться.
К середине 1990-х обе парадигмы упёрлись в тупик. Одни пытались знать всё точно — и ломались о реальный мир. Другие отказались знать что-либо вообще — и упёрлись в сложность задач.
И тут начинается самое интересное. Пока мобильная робототехника билась между этими двумя крайностями, в аэрокосмической индустрии уже тридцать лет работал совершенно другой подход — подход, в котором система изначально не пытается знать всё точно, а держит своё состояние в виде распределения вероятностей и постоянно обновляет это распределение по мере поступления новых измерений. В 1960 году американский инженер Рудольф Калман опубликовал работу о том, как оценивать состояние динамической системы по шумным измерениям — алгоритм, вошедший в историю как фильтр Калмана. Идея была в том, что на каждом шаге система держит оценку состояния вместе с оценкой своей неуверенности, и каждое новое измерение обновляет и то, и другое — с учётом того, насколько измерению можно доверять.
NASA подхватила эту математику почти сразу: именно фильтр Калмана управлял навигацией космических кораблей программы Apollo. Когда в 1969 году лунный модуль приземлился в Море Спокойствия в нескольких сотнях метров от расчётной точки после полёта в 380 тысяч километров, это было сделано алгоритмом, который непрерывно оценивал вероятностное распределение возможных позиций корабля по шумным данным от инерциальных датчиков, радаров слежения и наблюдений экипажа.
Позже к фильтру Калмана добавились более мощные методы — в частности, семейство particle filters, которые умеют работать с очень сложными, несимметричными распределениями вероятностей, с которыми обычный фильтр Калмана не справляется. Весь этот аппарат десятилетиями использовался в авиации, космосе, подводной навигации — но почему-то не применялся к мобильным роботам, которые продолжали биться об стену классического подхода.
И вот тогда небольшая группа исследователей — Себастьян Трун, Вольфрам Бургард, Дитер Фокс — сделала шаг, который кажется очевидным задним числом: взяла математику вероятностного оценивания, которая уже тридцать лет работала в аэрокосмической индустрии, и применила её к мобильным роботам. Идея была такая: робот должен иметь модель мира, но эта модель должна честно признавать свою неопределённость. Не "я нахожусь в точке A", а "я с такой-то вероятностью в точке A, с такой-то — в точке B, с такой-то — где-то ещё". И все решения принимаются на основе этих распределений, а не точных значений.
Подход назвали вероятностной робототехникой. В 2005 году Трун выиграл с этим подходом DARPA Grand Challenge — гонку автономных машин через пустыню, которую годом ранее никто не смог пройти даже наполовину. Его машина Stanley проехала 212 километров без участия человека. С этого момента всё, что сегодня движется автономно — роботы-пылесосы, складские роботы Amazon, беспилотники DJI, автомобили Tesla — построено на этой же идее. Точности нет нигде, но с неопределённостью можно работать системно.
Почему так вообще получилось
Дрон в принципе не знает, где он находится. Не в смысле "плохой GPS, нужен подороже", а именно в принципе. GPS ошибается на 2-5 метров, потому что ионосфера искажает сигналы, здания отражают, спутники двигаются — это физика. Гироскоп и акселерометр шумят на уровне молекул из-за теплового шума полупроводников, компас сбивается от магнитных помех любого мотора или силового провода рядом, камера ловит квантовый шум фотонов, лидар страдает от атмосферных флуктуаций скорости света.
Ни один сенсор на роботе не даёт число — каждый даёт распределение возможных чисел. Всегда, без исключений. Можно сузить это распределение более дорогими сенсорами, усреднением, фильтрами, но нельзя превратить его в точку. Это не техническая проблема, которую когда-нибудь решат. Это природа физического мира.
С действиями происходит то же самое. Сказал мотору "повернись на 90 градусов" — повернётся на 89 или 91, потому что трение переменное, напряжение питания скачет, нагрузка меняется. Сказал роверу "проехать метр" — проедет 0.98 или 1.03, потому что колёса проскальзывают, поверхность неровная, подшипники имеют люфт. Ошибка мотора на один шаг — сантиметр, на сто шагов — уже до десяти сантиметров, на километр — до метра. Это называется dead reckoning, и у него есть теоретический предел точности, связанный с математикой накопления случайных ошибок. Невозможно сделать моторы настолько точнее, чтобы эта проблема исчезла.
И мир сам по себе непредсказуемый: ветер меняется, человек выходит на траекторию, лужа появляется там, где вчера было сухо, дверь, открытая утром, закрыта вечером. Робот живёт не в модели — он живёт в реальности, где всегда есть то, что ты не учёл. Именно поэтому первый подход — точное знание и логический план — обречён: он предполагает мир, которого не существует. А второй подход — отказ от модели — обречён по другой причине: он оставляет робота без инструментов для постановки целей. Вероятностный подход оказался единственным, который не делает невыполнимых допущений о мире и при этом позволяет ставить и достигать сложные цели.
Робот-детектив
Устроено это так. Дрон не хранит в памяти "я в точке (5, 3, 2)" — он хранит распределение вероятностей: "с вероятностью 70% я около (5, 3, 2), с 20% могу быть в (5.2, 2.8, 2), с 10% — где-то ещё". Это называется belief — то, во что робот верит о своём положении в данный момент.
Когда дрон двигается, его belief размывается: узкое распределение становится шире, потому что моторы неточны и каждое движение добавляет неопределённости. Когда сенсор что-то измеряет, belief сужается в обратную сторону: из всех возможных точек более вероятными становятся те, что согласуются с показанием сенсора. Действия размывают знание, наблюдения его восстанавливают — и хороший автопилот держит этот баланс так, чтобы неопределённость не уходила в бесконечность.
Главное, что меня зацепило, — это то, как робот принимает решения. Он не думает "еду вперёд, потому что я в безопасном месте", потому что у него нет такого понятия, как "я в безопасном месте". Он думает "с какой вероятностью впереди препятствие? Если выше порога — не еду. Или сначала сделаю действие, которое уменьшит мою неопределённость, а уже потом двинусь". Робот может осознанно повернуться, посмотреть на стены, поискать ориентир — не чтобы приблизиться к цели, а чтобы сузить свой собственный belief о текущем положении, и только после этого принимать решение о движении. Робот работает не как калькулятор, который знает и действует, а скорее как детектив, который постоянно уточняет свою позицию по новым свидетельствам, не приходя к окончательному ответу.
Весь этот подход называется байесовским — по имени Томаса Байеса, английского священника XVIII века, сформулировавшего простую идею в основе: новая уверенность равна насколько данные соответствуют гипотезе, умножить на насколько я верил в гипотезу до. Именно эта идея, обёрнутая в современную математику фильтров и реализованная в виде работающих алгоритмов, сегодня стоит за навигацией смартфона в вашем кармане, автопилотом дрона в моей мастерской, и Tesla на шоссе.
Возвращение к нашей профессии
После того как я увидел, как это устроено у роботов, я посмотрел на рабочие решения, которые мы принимаем каждый день, другими глазами. Детерминизм оказался там, где его быть не должно — в местах, где реальность на самом деле была распределением, но мы работали с ней как с точкой.
Оценка сроков. Классическая история, которая повторяется у всех разработчиков. Клиент спрашивает "сколько займёт?", разработчик отвечает "три недели" — одно число, точка, уверенное утверждение. На самом деле правильный ответ — распределение: с какой-то вероятностью действительно три недели, с большей вероятностью — дольше, а в значительной доле сценариев — существенно дольше из-за вещей, которые сейчас не видны. Любой разработчик знает эмпирически, что реальные сроки растягиваются в полтора-два раза, но всё равно произносит одну цифру, потому что распределение не помещается в разговор — клиенту нужна точка, и эту точку ему дают, тихо понимая, что она ложь. Мы оцениваем задачу так, будто она существует в вакууме: без простоев на согласования, без багов в чужих системах, без того, что бизнес передумает в середине, без отпусков коллег, без праздников. А эти вещи — не исключения, это нормальное распределение рабочей реальности.
Найм. Кандидат прошёл технический скрининг, интервьюер говорит "он подходит", даётся оффер, и часть таких офферов через несколько месяцев оборачивается проблемой. Дело не в кандидате и не в интервьюере — дело в том, что один скрининг это одно наблюдение, слабое свидетельство, которое сдвигает распределение, но не даёт окончательного ответа. Лучшие команды найма, которые я видел, интуитивно работают в вероятностной логике: они делают много независимых срезов — техническое задание, парное программирование, системный дизайн, беседа о прошлых проектах, разговор с будущими коллегами, — именно потому что каждый срез в отдельности даёт слабый сигнал, и только их совокупность создаёт уверенную оценку. Процесс из пяти этапов — это не бюрократия, это грамотное накопление свидетельств. У этой же ошибки есть обратная сторона: когда кандидата отсеивают после одного неудачного алгоритмического вопроса, это тоже ошибка работы с распределением — один провал слишком слабый сигнал для отказа.
Гипотезы продукта. Предприниматель или PM спрашивает нескольких знакомых, получает "о, крутая идея" и вкладывает месяцы в разработку — это классический сценарий, через который проходит почти каждый, кто пытался запускать что-то своё. Ошибка не в том, что идея плохая, а в том, что несколько поддерживающих разговоров воспринимаются как сильное свидетельство. На самом деле это очень слабый сигнал: знакомые склонны поддерживать твои идеи, из "крутая идея" не следует "я буду этим пользоваться и платить", из "я буду пользоваться" не следует "и мои коллеги тоже". Между словами поддержки и реальными деньгами стоит цепочка из десятка фильтров, каждый с низкой вероятностью прохождения. Вероятностное мышление здесь подсказывает другой путь: три разговора сдвигают исходное убеждение с "рынка нет" на "возможно, рынок есть, вероятность процентов двадцать" — этого недостаточно, чтобы вкладывать три месяца разработки, но достаточно, чтобы за пару дней сделать лендинг, запустить рекламу и посмотреть, кто реально вводит номер карты, а не просто кивает головой.
Оценка технологий. На планёрке кто-то говорит "нам нужно переезжать с MongoDB на Postgres, потому что Postgres правильнее для транзакционных данных". Или "давайте перепишем на Rust, это будущее". Или "микросервисы — правильный путь, монолит нас скоро похоронит". Такие фразы произносят с интонацией окончательной истины: есть правильная технология и неправильная, и задача команды — перейти на правильную. На самом деле за каждой такой фразой стоит распределение сценариев. Да, Postgres лучше Mongo — в сценариях с определённой нагрузкой, определёнными требованиями к консистентности, определённой командой. В других сценариях — разница незначима или Mongo даже удобнее. Rust лучше Go в одних задачах, и наоборот — в других. Микросервисы спасают от одних проблем, и создают другие. Но когда идёт обсуждение, это всё схлопывается в одну фразу "X правильнее Y", и команда тратит месяцы на миграцию ради принципа, не понимая, что для их конкретной нагрузки в 200 запросов в секунду обе технологии работали бы одинаково хорошо. Вероятностное мышление здесь не про "X или Y", а про "в нашем конкретном сценарии разница такая-то, и стоит ли она трёх месяцев переписывания".
Собственные запуски. Это самый болезненный пункт, потому что здесь вероятностное мышление видно особенно чётко. Когда я запускал курс по ИИ для разработчиков, до запуска я говорил себе "в первый поток придёт человек тридцать" — это было моей оценкой, опорной точкой, вокруг которой я строил все приготовления. Пришло 36, но могло прийти 15, могло 70, могло 5. Я мыслил конкретным значением, хотя правильное мышление должно было быть распределением с очень широкими хвостами: новый продукт в новой теме, без истории запусков для калибровки — базовая неопределённость здесь огромная, любая конкретная цифра почти бессмысленна. Правильная подготовка к запуску должна учитывать распределение явно: несколько сценариев с грубыми вероятностями, план действий для каждого, пороги переключения между ними. Это занимает час работы, но убирает разочарование, когда события развиваются не по единственному сценарию, который ты держал в голове.
Везде, куда я смотрел в свою работу и в работу разработчиков вокруг, обнаруживалось одно и то же: детерминистские утверждения там, где реальность была распределением. И каждый раз, когда это игнорируется, за это приходится платить — временем, деньгами, качеством решений, нервами.
Что я поменял
Из этого выросло несколько рабочих привычек.
Оценки с вероятностями. Вместо "три недели" — "медианно три недели, в значимой доле сценариев до пяти, если вскроется А или Б". Непривычно для собеседника, но честно, и странным образом вызывает больше доверия: люди на уровне интуиции чувствуют, когда цифра соответствует реальности, а когда нарисована. То же касается бюджетирования — планировать надо на распределение, а не на медиану, иначе в каждом пятом проекте будешь проваливать сроки не из-за некомпетентности, а просто из-за математики.
Сигналы слабые по умолчанию. Один скрининг, одно пользовательское интервью, одна статья в Hacker News, один отзыв в Twitter — всё это слабые сигналы, не "плохие", а именно слабые. Для сильных выводов нужно либо много независимых слабых сигналов, либо один очень сильный, и пока их нет, большое решение на этом не строится.
Три-пять сценариев, а не один. Вместо "продукт взлетит, потому что X" — держать в голове несколько вариантов с грубыми вероятностями: взлетает, стагнирует, разбивается о конкурентов, выглядит странно сейчас и оказывается правильным через год. Это подсвечивает риски и ходы, которые при единственном сценарии просто не видны, потому что на них не смотришь.
Свои исходные убеждения — под вопрос. Перед оценкой новой идеи или технологии — спросить себя: что я думал об этом раньше и почему? Это из личного опыта, из прочитанного, из хайпа, или из чужого авторитетного мнения, принятого не задумываясь? Когда на это смотришь под таким углом, можно сдвинуть разговор с бесплодного "кто прав" на продуктивное "какие данные сдвинули бы нас с наших позиций".
Калибровка по своим прошлым прогнозам. Если несколько раз говорил "сделаю за две недели" и делал за четыре — это данные о систематическом смещении, и в следующий раз стоит прибавлять коэффициент. Люди крайне плохо калибруются без сознательного усилия: мы помним удачные прогнозы и забываем плохие, поэтому приходится заводить привычку записывать оценки и потом сверять с фактом.
Где детерминизм работает, а где нет
У детерминистского мышления есть своя область, и код — одна из немногих, где оно действительно работает. Больше того, есть целые инженерные дисциплины — компиляторы, криптография, формальная верификация — где система работает в замкнутом мире чётко определённых входов и выходов, и половина работы в том, чтобы сделать этот мир более детерминированным, исключить неопределённость туда, где её можно контролировать. Для них совет "живи с распределениями" был бы плохим.
Проблема не в детерминизме как таковом. Проблема в том, что эта привычка не остаётся в коде. Она выходит за его границы — в оценки сроков, в найм, в гипотезы продукта, в разговоры с клиентами, в решения о технологиях, в собственные запуски. И вот там она начинает врать, потому что эти области насквозь вероятностные и всегда были такими. Разработчик, который годами жил в мире точных значений, по инерции обращается с ними как с кодом — и каждый раз удивляется, почему "опять не успели", "опять не тот кандидат", "опять не взлетело".
Робототехника оказалась для меня полезной оптикой не потому что показала "как по-настоящему" устроена инженерия, а потому что подсветила разрыв: вот пространство, где точность возможна (мой код), вот пространство, где она невозможна в принципе (физический мир робота), и вот большое промежуточное пространство (вся моя работа за пределами кода), которое ближе ко второму, чем к первому, — хотя я годами обращался с ним как с первым.
Стоит сказать честно: язык распределений, слабых сигналов, исходных убеждений и калибровки — не моё открытие. Филип Тетлок в "Superforecasting", Канеман в "Thinking, Fast and Slow", работы рационалистского сообщества давно и системно разложили эти идеи. Я пришёл к ним через робототехнику и описываю свой путь — но если эссе резонирует, стоит идти дальше в эту литературу.
Навык, который я вынес, — не "думать иначе" в целом. Навык конкретнее: замечать момент, когда ты вышел из зоны, где детерминизм работает, и переключать режим. Для меня это переключение оказалось самым ценным, что я получил от робототехники, к которой в самом начале подходил как к "ещё одной технической теме для расширения кругозора".
P.S. На самом деле, искажение в сторону детерминизма касается не только нашей профессии — оно пронизывает всё наше общество. От того, как мы принимаем личные решения, до того, как оцениваем развитие политических и экономических процессов, мы склонны подменять распределения конкретными значениями, слабые свидетельства — сильными выводами, возможные сценарии — одним "правильным" прогнозом. Это отдельная большая тема, в которой гораздо больше последствий, чем кажется на первый взгляд.
P.P.S. Если вам интересно то, о чём я пишу, подписывайтесь на мой телеграм канал https://t.me/drimdev — дальше будет ещё интереснее.