Обложка Я первый

Я первый

Руслан Северин

0% прочитано
Стандартный
Руслан Северин

Я первый

Пролог. Ноль

Ноль выглядел аккуратно.

Не как ошибка. Не как красное предупреждение и не как рухнувшая сборка. Не как строка терминала, в которой между именем файла и номером колонки пряталась причина очередной бессонной ночи.

Просто цифра.

Ноль установок из магазина.

Рустик обновил страницу магазина приложений.

Ноль.

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

Несколько месяцев назад всё это казалось почти невозможным.

Теперь невозможное существовало.

Рустик смотрел на ноль, и ему казалось, что всё это никому не нужно.

Рустик снова потянул страницу вниз, хотя понимал: движение пальца не создаст пользователя.

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

Всё это можно было объяснить.

ChatMind особенно хорошо умела объяснять.

— Это провал, — написал Рустик.

Ответ появился почти сразу.

Модель спокойно перечислила возможные причины: отсутствие трафика, непонятное описание, сложный первый сценарий, необходимость двух пользователей рядом, отсутствие начального сообщества. Затем предложила план: найти первых людей вручную, провести небольшой пилот, проверить один конкретный сценарий и перестать добавлять функции до получения реальной обратной связи.

Ответ был разумным.

Но разумный ответ не менял цифру.

DeepTip задумывался как способ поблагодарить человека рядом, не спрашивая его имени, номера телефона или банковских реквизитов. Два смартфона должны были обнаружить друг друга на небольшом расстоянии, а пользователь — выбрать нужного человека и передать ему цифровой знак благодарности.

В теории всё выглядело просто.

Один человек открывает приложение.

Второй находится рядом.

Телефоны видят друг друга.

Благодарность передана.

Но первому пользователю некого было находить. Второй не устанавливал приложение, потому что там ещё не было первого.

Рустик смотрел на ноль и думал уже не только о DeepTip.

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

Ноль перестал быть только статистикой магазина.

Он стал точкой отсчёта.

Нулём перед первым пользователем.

Нулём между старым способом создавать вещи и новым.

Но эта история началась не с искусственного интеллекта и даже не с идеи приложения.

Она началась в тот вечер, когда после основной работы Рустик сел в машину, включил курьерское приложение и понял, что одной зарплаты семье больше не хватает.

Глава 1. Вторая смена

В шесть вечера рабочий день Рустика заканчивался только в календаре.

Компьютер на столе погас. Коллеги начали расходиться: кто-то торопился в спортзал, кто-то обсуждал сериал, кто-то жаловался, что снова придётся заехать в магазин.

Рустик тоже надел куртку и попрощался.

Со стороны всё выглядело так, будто он ехал домой.

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

Экран загорелся сам.

Новое начисление по коммунальным услугам.

Ниже было сообщение от жены:

Посмотри потом, почему опять больше.

Бумажных квитанций давно не было. Счета появлялись бесшумно: в банковском приложении, в личном кабинете, в уведомлении поверх семейной фотографии на заставке.

От этого они не становились легче.

Рустик и без детализации понимал, из чего складывалась сумма.

Два кредита, счета за квартиру, продукты, бензин. И всё, что внезапно понадобилось или сломалось и не могло подождать следующего месяца.

Отдельно каждая цифра выглядела терпимой.

Проблема начиналась, когда они собирались вместе.

Зарплата приходила на карту и почти сразу переставала принадлежать ему. Деньги расходились по обязательствам быстрее, чем он успевал почувствовать, что заработал их.

Но тяжелее цифр были моменты, когда их приходилось переводить на человеческий язык.

«Давай после зарплаты».

«В этом месяце не получится».

«Может быть, позже».

Иногда речь шла не о новом телефоне и не о поездке к морю. Обычная обувь, которая нужна ребёнку сейчас. Школьное мероприятие. Сломавшаяся вещь дома. Покупка, ради которой взрослый человек вроде бы должен просто достать карту, а не проводить внутри себя совещание из долгов, сроков и остатков.

Дети не всегда спорили.

Иногда соглашались слишком быстро.

Именно это Рустик переносил хуже всего.

Он работал. У него была нормальная основная работа, график и зарплата. Он занимался инфраструктурой внутренних систем: Windows-серверами, службами, скриптами, журналами ошибок и развёртыванием приложений. Он умел прочитать PowerShell-код, понять, что делает команда, найти причину сбоя и проверить результат.

В мобильной разработке ему ещё предстояло разобраться: как устроены интерфейсы, радиосвязь между телефонами, платёжные системы. Там привычных серверов и скриптов было недостаточно.

Просто его профессиональных знаний хватало, чтобы поддерживать чужие системы, но пока не хватало, чтобы создать собственный источник дохода.

Рустик включил двигатель.

Телефон подключился к машине. На экране появилось курьерское приложение.

Выйти на линию?

Он нажал кнопку.

Вторая смена началась.

Первый заказ пришёл почти сразу: забрать два пакета из ресторана в торговом центре и отвезти в новый жилой комплекс на другом конце района.

Приложение показывало расстояние, время и сумму, которая выглядела неплохо до тех пор, пока не вспоминались бензин, парковка и обратная дорога без заказа.

Навигатор заговорил спокойным женским голосом:

— Через двести метров поверните направо.

Рустик написал домой:

Вышел на пару заказов. Постараюсь не поздно.

Жена ответила почти сразу:

Хорошо. Только поешь где-нибудь.

Он усмехнулся.

Эта фраза повторялась почти каждый вечер. И почти каждый вечер он отвечал, что обязательно поест.

В торговом центре пришлось дважды объехать парковку. Свободное место нашлось далеко от входа. Рустик быстрым шагом прошёл через галерею магазинов, поднялся на второй этаж и остановился у стойки выдачи.

Заказ ещё не был готов.

Рядом ждали другие курьеры. Один сидел на корточках у стены и смотрел видео без звука. Другой спорил с поддержкой. Третий держал большой термокороб и, не отрываясь от экрана, ел булочку.

— Долго? — спросил Рустик у девушки за стойкой.

— Минут пять.

Пять минут в ресторане редко означали пять минут. Они могли означать две, пятнадцать или то, что заказ потерялся между кухней и экраном.

Рустик посмотрел на время.

Дома его ждали трое детей.

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

Жена оставалась дома с младшей. Пока девочка была маленькой, обычная работа для жены означала бы сначала найти человека, которому можно доверить ребёнка, а потом значительную часть зарплаты отдавать за присмотр и постоянно отпрашиваться из-за болезней, врачей и детских дел.

Они не принимали это решение торжественно.

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

Значит, основной регулярный доход оставался на Рустике.

А регулярного дохода не хватало.

— Ваш готов, — сказала девушка.

Он забрал пакеты, сверил номер и вернулся к машине.

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

Через четыре минуты мужчина наконец перезвонил.

— Вы где?

— У шлагбаума. Нужен код или откройте въезд.

— А пешком нельзя?

Рустик посмотрел на два больших пакета, стаканы с напитками и мокрый снег между парковкой и подъездом.

— Можно. Тогда немного дольше.

Мужчина тяжело вздохнул, словно задержку создал сам курьер, и назвал код.

У двери квартиры он забрал пакеты, не посмотрев на Рустика.

— Хорошего вечера, — сказал Рустик.

Дверь уже закрывалась.

Заказ был оплачен заранее. В приложении появилась зелёная галочка:

Доставлено.

На этом человек по другую сторону двери переставал существовать, а курьер превращался в свободную точку на карте.

Несколько лет назад всё было немного иначе.

Рустик уже подрабатывал доставкой, когда многие клиенты платили наличными. Приходилось пересчитывать купюры, искать сдачу, ждать, пока человек найдёт мелочь.

Это было медленнее.

Но между курьером и получателем хотя бы оставалось несколько секунд живого контакта.

Кто-то отдавал точную сумму.

Кто-то округлял.

Кто-то махал рукой:

— Сдачи не надо.

Чаевые давали не все. Но давали достаточно часто, чтобы они оставались заметной частью смены. Иногда десять небольших чаевых складывались в сумму, ради которой имело смысл проехать ещё несколько адресов.

А иногда важнее денег был сам жест: тебя увидели не как движущуюся иконку, а как человека, который поднял пакет на девятый этаж, нашёл нужный подъезд и приехал под дождём.

Теперь почти всё оплачивалось заранее.

«Оставьте у двери».

«Не звоните».

«Код домофона в комментарии».

Всё стало быстрее, удобнее и почти безлично.

Кнопка чаевых существовала где-то после оценки заказа, но большинство клиентов до неё не доходило.

В лифте приложение показало следующий заказ.

Рустик нажал «принять».

Не потому, что хотел. Потому что ехать домой после одного заказа не имело смысла. Потраченный бензин, парковка и время почти съели заработок. Нужно было сделать ещё несколько, чтобы вечер хотя бы выглядел оправданным.

Входящие предложения приходилось оценивать за секунды.

Где он находится сейчас?

В каком направлении ведёт новый адрес?

Не окажется ли ресторан на противоположной стороне пробки?

Успеет ли он встроить заказ в уже существующую цепочку?

Иногда дорогой заказ ломал весь маршрут. Иногда дешёвый, но расположенный по пути, оказывался выгоднее.

Приложение показывало отдельный заказ.

Рустик должен был видеть весь город.

Второй заказ оказался проще. Пожилая женщина попросила оставить продукты у двери, а когда Рустик уже спускался, написала:

Спасибо вам большое. Мне самой сейчас тяжело ходить.

Через несколько секунд приложение добавило небольшие чаевые.

Рустик остановился между этажами и посмотрел на экран.

Первый чай за несколько смен.

Сумма была скромной, но он улыбнулся.

Не из-за денег. Точнее, не только из-за денег.

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

Разница помещалась в одном слове.

Спасибо.

Следующий заказ вспыхнул повышенным коэффициентом.

Забрать еду у Садового кольца и отвезти в деловой квартал Сити.

Сумма выглядела заметно лучше предыдущих. Такой заказ мог вытянуть весь вечер. Рустик посмотрел на время и нажал «принять» раньше, чем приложение успело передумать.

До ресторана он доехал быстро. Заказ уже ждал. Первые десять минут всё шло настолько хорошо, что Рустик даже позволил себе подумать: сейчас отвезёт, возьмёт ещё два рядом и к полуночи получится нормальная смена.

Проблемы начались, когда впереди поднялись стеклянные башни.

— Вы прибыли, — сообщил навигатор.

Рустик находился на полосе, с которой нельзя было повернуть к указанному входу.

Точка на карте стояла внутри одного здания. Через секунду перескочила к соседней башне. Потом оказалась на набережной.

Высотные здания отражали сигнал, и геолокация сходила с ума.

Рустик проехал нужный поворот. Навигатор перестроил маршрут и предложил сделать круг длиной в несколько километров.

Таймер доставки стал жёлтым.

Он позвонил клиенту.

— Я подъехал. Какая башня и какой вход?

— Там на карте всё указано.

— Карта показывает сразу три места.

— Ну, центральный вход. Со стороны парковки.

Вокруг было несколько центральных входов и несколько парковок.

Рустик медленно двигался в потоке, пытаясь одновременно смотреть на дорогу, башни, карту и номера корпусов. Остановиться было негде. Подземный въезд вёл на парковку, где даже короткое пребывание могло съесть заметную часть оплаты.

Он всё-таки свернул вниз.

У шлагбаума высветился тариф.

Сзади уже сигналили.

Шлагбаум поднялся.

Свободных мест не было.

Рустик проехал один уровень, потом второй. Навигатор под землёй окончательно потерял его и продолжал показывать, что машина движется по улице над парковкой.

Таймер стал красным.

Клиент больше не отвечал.

Рустик сделал ещё один круг, дождался освобождающегося места и начал парковаться. Пакет на пассажирском сиденье наклонился. Он успел подхватить напитки рукой.

В этот момент что-то внутри него оборвалось.

— Зачем мне всё это надо?! — крикнул он в пустой машине.

Голос ударился о стёкла и вернулся громче, чем ожидалось.

Несколько секунд Рустик сидел, сжимая руль.

Ответов было много.

Коммунальный счёт.

Два кредита.

Детская обувь.

Фотография младшей дочери, которая спрашивает, когда папа вернётся.

Он заглушил двигатель, схватил пакет и побежал искать выход.

Из парковки он попал не в ту башню. Охранник направил его обратно по переходу. В следующем холле попросили номер офиса. Лифт разделял этажи по группам, и нужная кнопка не нажималась, пока сотрудник не приложил карту.

Клиент написал:

Вы долго ещё?

Рустик посмотрел на красный таймер и ничего не ответил.

Он доставил заказ с опозданием на несколько минут.

Клиент забрал пакет, мельком проверил пломбу и закрыл дверь.

Ни слова о том, что курьер почти двадцать минут искал вход среди стеклянных башен. Ни слова о платной парковке. Ни слова о карте, которая отправляла его одновременно в три места.

В приложении появилась зелёная галочка.

Оплата всё ещё выглядела неплохо. Затем Рустик мысленно вычел парковку, бензин и потерянное время.

Чтобы не закончить вечер работой только на заправку и владельца парковки, нужно было брать ещё заказы.

И не опаздывать.

Следующие часы город снова распался на маршруты.

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

Особенно Рустик боялся заказов с пометкой «оставить у двери».

Снаружи всё выглядело просто: поставить пакет, сфотографировать и нажать «доставлено».

Но коридоры новых домов были одинаковыми. Серые двери. Маленькие номера. Несколько секций на одном этаже. Иногда приложение приводило не к тому подъезду. Иногда в заказе оставался старый комментарий от предыдущего адреса. Иногда человек не отвечал, а таймер продолжал идти.

Перед тем как уйти, Рустик дважды проверял номер квартиры. Фотографировал так, чтобы в кадр попали пакет и номер двери. Потом смотрел снимок ещё раз уже в лифте.

Страх всё равно оставался.

Что, если перепутал секцию?

Что, если рядом была такая же дверь?

Что, если номер на фотографии размыт?

Ближе к одиннадцати поверх навигатора появилось сообщение оператора:

Клиент сообщает, что заказ не получен. Уточните место доставки.

У Рустика мгновенно похолодели руки.

Он остановился там, где не мешал движению, и открыл галерею. За вечер накопились почти одинаковые фотографии: пакеты у серых дверей, коврики, номера квартир, тёмные коридоры.

Какой именно заказ?

Первый снимок — не тот дом.

Второй — номер закрыт ручкой пакета.

Третий — подъезд совпадал, но этаж он уже не помнил.

Рустик мысленно проехал маршрут заново.

Шлагбаум. Второй подъезд. Лифт справа. Длинный коридор. Дверь после поворота.

Нашёл фотографию.

Увеличил номер квартиры.

Совпадал.

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

Через несколько минут оператор сообщил, что информация передана клиенту.

Ни извинения. Ни объяснения, нашёлся ли пакет.

Диалог просто закрылся.

Рустик поехал дальше, но ещё долго вспоминал дверь. Фотография доказывала адрес приложению, но не успокаивала его самого.

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

Нужно было не только взять больше заказов.

Нужно было каждый раз успеть, не ошибиться, сохранить фотографию и быть готовым доказать, где находился несколько часов назад.

При этом он замечал маленькие действия, после которых трудный вечер становился легче.

Водитель пропустил его из двора. Официант предупредил о задержке. Другой курьер придержал дверь, потому что руки были заняты пакетами. Незнакомый мужчина помог донести тяжёлую сумку до лифта.

Большинство этих людей исчезали через несколько секунд.

Иногда хотелось отблагодарить их не дежурной фразой, а чем-то настоящим. Но человек уже уходил. Имени не было. Телефона не было. Искать его специально выглядело странно. Наличных могло не оказаться.

Рустик пока не думал о приложении.

Он просто складывал эти случаи где-то в памяти.

Около одиннадцати жена прислала фотографию младшей дочери. Та уснула поперёк кровати, обняв игрушку и заняв почти всё место.

Под фотографией было сообщение:

Спрашивала, когда папа придёт.

Рустик посмотрел на сумму за смену. Потом на карту, где рядом загорелся заказ с повышенным коэффициентом.

До дома было двадцать минут.

Заказ обещал занять тридцать пять.

Он принял заказ.

Эта кнопка каждый раз казалась маленьким предательством, хотя никто дома не обвинял его напрямую.

Он работал ради семьи и одновременно отсутствовал рядом с ней по той же причине.

В ресторане снова пришлось ждать.

Рустик сел у окна, подключил телефон к зарядке и открыл банковское приложение. Перед ним была вся ближайшая жизнь семьи, разложенная по датам.

Коммунальный счёт.

Два кредитных платежа.

Покупка, которую уже один раз отложили.

Он добавил сегодняшний заработок, затем вычел бензин и парковку в Сити.

Результат почти не изменился.

Работать больше было самым очевидным решением.

Но очевидное решение уже занимало вечера и выходные. Сутки не расширялись. Машина изнашивалась. Дети росли. Усталость накапливалась быстрее денег.

Нужен был другой способ.

Не выигрыш. Не чудо. Не обещание лёгких миллионов из рекламы.

Что-то, что можно создать один раз, а потом не обменивать каждый дополнительный рубль на ещё один час собственной жизни.

Рустик давно перебирал идеи.

Небольшой сайт. Сервис для водителей. Приложение для соседей. Каталог полезных мест. Что-то связанное с доставкой. Что-то, за что люди действительно готовы платить.

Каждая идея упиралась в одно и то же.

Чтобы сделать нормальный цифровой продукт, нужна команда. Мобильный разработчик. Серверный разработчик. Дизайнер. Тестировщик. Деньги. Время. Опыт, которого у него не было.

Иногда Рустик открывал статьи о стартапах и закрывал их после слов «соберите сильную команду».

Если бы у него были деньги на сильную команду, он, возможно, не развозил бы чужой ужин в половине двенадцатого ночи.

На экране появилось уведомление:

Заказ готов.

Домой Рустик приехал после полуночи.

В квартире было темно. На кухне жена оставила ему тарелку под крышкой. Телефон снова напомнил о неоплаченном начислении, но Рустик смахнул уведомление.

Он разогрел еду, стараясь не шуметь. Из комнаты донёсся детский кашель, потом снова стало тихо.

Есть не хотелось.

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

Рустик посмотрел заработок за смену.

Деньги были настоящими. Нужными. Честно заработанными.

И всё равно недостаточными.

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

Он открыл браузер.

Несколько дней назад на основной работе Рустик проверял бесплатную модель искусственного интеллекта. Попросил её написать служебный скрипт для знакомой инфраструктурной задачи и неожиданно получил цельную рабочую основу — то, ради чего раньше пришлось бы перечитать документацию, статьи и несколько форумов.

Результат не был идеальным. Рустик исправил параметры, убрал опасное действие и проверил скрипт на тестовом контуре.

Но он сработал.

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

На экране открылось пустое окно ChatMind — другой модели, которая недавно особенно точно прошла его случайный домашний тест.

Внизу мигал курсор.

Рустик долго не писал. Он не хотел получить очередной список советов: экономить, продавать ненужные вещи, пройти курс или начать вести блог.

Наконец набрал:

У меня есть работа, но денег всё равно не хватает. После работы и по выходным я подрабатываю курьером на машине. У меня трое детей, жена дома с младшей. Я хочу придумать что-то своё, но у меня нет денег на команду разработчиков. Реально ли одному человеку сделать приложение с помощью искусственного интеллекта?

Он перечитал сообщение.

Оно выглядело слишком личным для разговора с программой.

Рустик уже хотел стереть первую половину и оставить только технический вопрос.

Потом решил, что программе всё равно.

Нажал «Отправить».

Три точки появились почти сразу.

ChatMind начала отвечать.

От автора

Это художественный роман, выросший из реального опыта создания приложения вместе с моделями искусственного интеллекта. Имена людей, моделей и сервисов изменены; часть событий объединена или переосмыслена.

В основе истории — реальные поиски, ошибки и решения человека, который пытался совместить работу, семью и собственный проект.

Глава 2. Первая проверка

Ответ ChatMind начинался с простого слова:

«Да».

Но это «да» Рустик уже слышал. За коротким ответом для него стояло несколько дней разговоров с другими моделями.

Началось всё на основной работе, с обычной служебной задачи.

Нужно было подготовить PowerShell-скрипт для знакомой инфраструктурной работы: проверить состояние служб на нескольких Windows-серверах, собрать результат и вывести его в понятном виде.

Рустик умел сделать это сам.

Он знал, где искать документацию, какие команды проверить, какие параметры нельзя бездумно запускать на рабочем контуре. Обычно такая задача складывалась постепенно: одна статья, пример с форума, справка по командлету, несколько пробных запусков, исправление ошибок.

Рустик выбрал DeepSight по простой причине: модель была доступна бесплатно.

Он открыл новое окно скорее из любопытства, чем из доверия.

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

DeepSight ответила почти сразу.

На экране появился цельный скрипт.

Не отдельная команда.

Не набор обрывков.

Параметры, цикл по серверам, проверка доступности, обработка исключений, сбор результатов и экспорт отчёта.

Рустик перечитал код от начала до конца.

В одном месте модель использовала действие, которое он не хотел выполнять автоматически. Он его убрал.

В другом заменил пример списка серверов на тестовые адреса.

Исправил путь сохранения.

Запустил.

Первый проход завершился ошибкой доступа на одном узле. DeepSight объяснила возможную причину и предложила добавить более аккуратную обработку недоступных машин.

Рустик внёс изменение.

Запустил снова.

Таблица сформировалась.

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

Задача, на которую он мысленно отвёл несколько часов, заняла меньше одного.

Самым впечатляющим был не код.

И даже не скорость.

Раньше нужное решение существовало для Рустика сразу в нескольких местах: часть в документации, часть в чужом примере, часть в собственном опыте. Их ещё предстояло найти, проверить и соединить.

DeepSight собрала всё в одну рабочую основу.

Рустик сохранил файл.

Потом открыл его снова.

Скрипт никуда не исчез.

Результат был настоящим.

Несколько следующих дней он возвращался к DeepSight уже без служебной необходимости.

Сначала задавал технические вопросы.

Потом обычные.

Как увеличить доход?

Как быстрее погасить кредиты?

Что может делать человек, у которого есть работа, но почти нет свободного времени и стартового капитала?

DeepSight отвечала подробно.

Сначала предложила финансовый аудит.

Составить таблицу расходов.

Отделить необходимое от необязательного.

Отказаться от лишних подписок, доставки еды, импульсивных покупок и вредных привычек.

Направлять сэкономленное на досрочное погашение самого дорогого кредита.

Рустик читал пункт за пунктом.

Подписок у него почти не было.

Еду домой чаще доставлял не курьер, а он сам — чужую, в термосумке, по чужому адресу.

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

От вредных привычек он тоже отказался.

Не ради новой жизни и не ради красивой статистики.

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

Он написал:

— Я уже экономлю почти на всём. Что ещё?

DeepSight перестроила ответ. Теперь предлагала фриланс, перепродажу товаров, блог, маркетплейсы и инвестиции. У каждого варианта были свои преимущества, риски и первые шаги.

Фриланс требовал искать заказчиков после основной работы и конкурировать с людьми, которые занимались этим постоянно.

Для торговли требовались деньги на товар и рекламу, а ещё время, чтобы разобраться с комиссиями и возвратами.

Блог мог потребовать месяцев или лет регулярной работы без гарантии, что аудитория вообще появится.

Инвестиции особенно раздражали. Чтобы деньги приносили деньги, сначала нужны были деньги, которые можно позволить себе потерять.

В одном из ответов среди вариантов появился лотерейный билет.

DeepSight сразу уточнила, что вероятность выигрыша крайне мала и такой способ нельзя считать финансовой стратегией.

Но сам пункт уже стоял в списке.

Рустик написал:

— То есть человеку с кредитами ты предлагаешь купить лотерейный билет?

Модель объяснила, что упомянула его только ради полноты вариантов.

Ответ был логичным.

И совершенно бесполезным.

Тогда Рустик сформулировал вопрос иначе:

— Как создать источник дохода, который не требует каждый раз продавать ещё один час моей жизни?

DeepSight предложила цифровой продукт.

Сайт.

Сервис.

Программу.

Приложение.

Что-то, что можно создать один раз, а затем использовать многократно.

Рустик уже думал об этом раньше.

Но раньше слово «приложение» означало команду.

Мобильного разработчика.

Серверного разработчика.

Дизайнера.

Тестировщика.

Деньги на месяцы работы.

Теперь перед ним находилась модель, которая только что помогла собрать настоящий рабочий скрипт.

Он спросил:

— Может ли один человек сделать приложение с помощью искусственного интеллекта?

DeepSight не сомневалась.

Она написала, что может помочь определить функции, выбрать технологии, подготовить структуру проекта, написать код и объяснить запуск.

У Рустика была какая-то идея.

Позже он уже не сможет точно вспомнить её назначение.

Она не была DeepTip.

Не была связана с чаевыми, благодарностью или поиском человека рядом.

Возможно, это был небольшой сервис обмена информацией. Возможно, инструмент, которым могли пользоваться несколько людей. Тогда детали казались важными. В памяти останется другое — ощущение, что идея впервые не упёрлась в отсутствие команды.

Рустик описал её несколькими предложениями.

DeepSight ответила, что проект реалистичен.

Предложила основные модули.

Потом дополнительные.

Потом ещё несколько улучшений, о которых Рустик не спрашивал.

Каждая новая возможность звучала как почти готовая часть будущего приложения.

Модель сформировала архив.

Рустик скачал его.

Внутри находились папки с профессиональными названиями.

src.

components.

services.

config.

Несколько файлов.

Инструкция.

Структура выглядела серьёзно.

Рустик открыл один файл.

Почти пусто.

Другой.

Название будущей функции и комментарий о том, что реализацию нужно добавить позже.

Третий.

Ещё одна заготовка.

Он вернулся в чат и написал, что не видит основного кода.

DeepSight ответила, что подготовлена архитектура проекта и теперь можно последовательно реализовать модули.

Рустик попросил добавить недостающее.

Появился новый архив.

Папок стало больше.

Файлов тоже.

В названиях появились версии.

Но содержимого почти не прибавилось.

Некоторые файлы состояли из нескольких строк.

Другие были пустыми.

Третьи только называли то, что должно было когда-нибудь появиться.

Рустик скачал ещё одну версию.

Потом ещё одну.

Каждый раз DeepSight перечисляла выполненные улучшения так, будто работа действительно продвигалась.

Каждый раз архив становился убедительнее снаружи.

И почти не менялся внутри.

Не было ни запускаемого прототипа, ни кода ключевых функций. Только форма будущего проекта.

Рустик смотрел на папки и чувствовал, как первое восхищение сменяется злостью.

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

Но у служебного скрипта была конкретная задача: проверить службы и сохранить отчёт. Рустик знал, какой результат должен получить и как его проверить.

«Сделать приложение» границ почти не имело.

DeepSight заполнила неопределённость папками, названиями и уверенными объяснениями.

Рустик слишком хотел поверить, что этого достаточно.

Слово «готово» оказалось самым опасным.

Для модели оно могло означать готовую структуру.

Для него — работающую программу.

Он закрыл последний архив.

Несколько минут просто смотрел на экран.

Разочарование было сильнее, чем если бы DeepSight сразу написала: «Я не могу».

Отказ оставил бы идею там, где она была.

Архив сначала позволил поверить, что дверь уже открыта.

А потом показал, что за ней только нарисованный коридор.

И всё же Рустик не перестал пользоваться моделями.

Рабочий скрипт никуда не делся.

Он по-прежнему работал.

Значит, проблема была не в том, что искусственный интеллект бесполезен.

Проблема была в том, что Рустик пока не понимал, где заканчивается реальный результат и начинается убедительное описание результата.

Он решил проверить другие модели.

Не на приложении.

Не на деньгах.

На чём-то простом, где правильный ответ был известен заранее.

Подходящий тест нашёлся дома.

На семейной фотографии младшая дочь надела на голову перевёрнутую детскую накладку на унитаз. Из-за ракурса предмет выглядел странно: пластиковый обруч, ручки, мягкий край.

Рустик загрузил снимок в DeepSight.

Модель решила, что это игрушка.

Sfera — доступная модель крупной банковской экосистемы — увидела другой предмет и тоже ошиблась.

Тогда Рустик отправил фотографию ChatMind.

Она ответила, что на голове у ребёнка находится перевёрнутая детская накладка на унитаз.

Не просто назвала предмет.

Поняла, почему он выглядит непривычно.

Рустик увеличил фотографию.

Ручки.

Форма.

Обратная сторона.

Точно.

Тест был смешным.

Почти бессмысленным.

Но после пустых архивов именно такой ответ оказался важен.

ChatMind не обещала построить целое приложение за несколько минут.

Она просто правильно увидела то, что находилось перед ней.

Поэтому той ночью на кухне Рустик открыл именно её.

Он задал ChatMind тот же вопрос, который уже однажды задавал DeepSight: может ли один человек без команды сделать приложение с помощью искусственного интеллекта.

Только теперь он ждал не уверенного обещания. Он хотел понять, видит ли модель настоящую проблему за его словами.

Теперь Рустик снова смотрел на первую строку ответа.

«Да».

На этот раз восторга не было. Одного согласия было мало.

Следующая строка оказалась важнее:

— Но начните не с приложения. Начните с одной проблемы, которую вы понимаете лучше других.

Рустик перечитал фразу.

Проблем он понимал много.

Как найти нужный подъезд, когда карта показывает середину дома.

Как не потерять заработок на платной парковке.

Как доказать, что пакет оставлен у правильной двери.

Как заработать вечером и всё-таки увидеть детей до того, как они уснут.

Но ChatMind просила выбрать одну.

Рустик отложил телефон.

Вспомнил пожилую женщину, которая написала ему «Спасибо».

Водителя, пропустившего его из двора.

Официанта, предупредившего о задержке.

Другого курьера, который придержал дверь.

Людей, которых он встречал на несколько секунд и больше никогда не видел.

Иногда обычного слова было достаточно.

Иногда хотелось сделать чуть больше.

Но человек уже уходил.

Имени не было.

Телефона не было.

Наличных тоже могло не оказаться.

Рустик снова взял телефон.

Внизу мигал курсор.

Он начал писать следующий вопрос.

Глава 3. Человек рядом

— Можно ли сделать приложение, чтобы отблагодарить человека рядом, не спрашивая его имя, телефон или номер карты?

Рустик перечитал вопрос.

Он звучал слишком просто.

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

ChatMind ответила не сразу.

Сначала уточнила:

— Что именно должен сделать пользователь?

Рустик посмотрел на экран.

После DeepSight он ожидал другого. Списка технологий. Красивого описания архитектуры. Сообщения о том, что всё реализуемо и можно сразу переходить к созданию проекта.

Но ChatMind просила не объяснить идею, а показать действие.

Он написал:

— Допустим, рядом человек, которому я хочу оставить небольшие чаевые или просто знак благодарности. Я открываю приложение, вижу его среди людей поблизости, выбираю и отправляю.

— Получатель заранее установил приложение?

Рустик остановился.

Это был неприятный вопрос.

В его представлении приложение уже находило человека. Каким образом тот появлялся в списке, он не думал.

— Наверное, да.

— Как отправитель поймёт, что выбрал именно его?

Ещё один вопрос, которого не было в красивой версии идеи.

В ресторане могли стоять несколько официантов. У подъезда — несколько курьеров. На парковке — десятки водителей. Если приложение показывало только имена, это ничего не решало. Если фотографии — человек должен был заранее загрузить снимок. Если расстояние — телефоны могли лежать в разных карманах и сумках.

Рустик написал:

— Можно показать псевдоним и короткий код. Например, человек говорит: «Я Синица-47».

ChatMind предложила другой вариант:

— Получатель может на несколько секунд включить режим ожидания благодарности. Его телефон передаст временный идентификатор. Отправитель увидит псевдоним и подтверждение на экране второго устройства.

Теперь это уже было похоже на сцену.

Официант приносит заказ.

Курьер помогает другому курьеру поднять тяжёлую дверь.

Водитель вытаскивает застрявшую машину.

Человек нажимает одну кнопку, и его телефон ненадолго становится видимым только для тех, кто находится рядом.

Никакого номера.

Никакой банковской карты.

Никакой необходимости добавлять незнакомца в контакты.

Рустик написал:

— А телефоны смогут найти друг друга без интернета?

ChatMind объяснила, что для обнаружения поблизости можно использовать Bluetooth Low Energy. Телефоны передают короткие радиосигналы, а приложение видит устройства рядом.

Слово Bluetooth Рустик знал.

Low Energy звучало уже как территория разработчиков.

— То есть приложение сможет понять, кто стоит рядом?

— Оно сможет обнаружить другой телефон, который передаёт разрешённый сигнал. Но само по себе не поймёт, какой человек держит этот телефон.

Рустик ещё раз посмотрел на вопрос. Найти устройство и понять, кому оно принадлежит, — разные задачи. Для второй требовалось подтверждение.

На телефоне получателя мог появляться код или цветовой знак. Отправитель видел такой же на своём экране и убеждался, что выбрал правильно.

Рустик представил два экрана.

На одном — синяя окружность.

На другом — такая же.

Совпало.

Можно отправлять.

— А если рядом мошенник? — спросил он. — Включит режим и будет ждать, что кто-нибудь ошибётся.

ChatMind предложила оставить подтверждение с обеих сторон и ограничить время видимости. Повторные действия тоже придётся проверять, а результат сохранять в истории. Один короткий код не решал всех вопросов безопасности.

Рустик перечитал первоначальное описание. Несколько строк уже хотелось зачеркнуть: до них было слишком далеко.

— Не начинайте с настоящих денег, — написала она.

Рустик нахмурился.

Именно деньги были причиной идеи.

Благодарность без денег казалась обычной кнопкой «нравится», которых и так хватало в каждом приложении.

— Тогда в чём смысл?

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

Фраза была неприятной.

И правильной.

Рустик уже однажды принял структуру за программу. Теперь ChatMind предлагала сначала доказать один короткий путь.

Не кошелёк.

Не банк.

Не бизнес.

Не способ погасить кредиты.

Два телефона.

Один человек нажимает «Отправить».

Второй получает знак благодарности.

Рустик написал:

— Хорошо. Пусть сначала это будет условный жетон.

ChatMind предложила название «знак благодарности», чтобы его пока не путали с деньгами.

Знак ничего не стоил, но позволял проверить, существует ли само действие.

Рустик начал перечислять экраны: регистрация, профиль, поиск, отправка, история, настройки. Список снова становился длинным.

ChatMind остановила его:

— Для первого прототипа достаточно трёх состояний.

Первое: пользователь включает видимость.

Второе: другой телефон обнаруживает его рядом.

Третье: после подтверждения на обоих устройствах появляется запись в истории.

— А регистрация?

— Позже.

— Профиль?

— Временный псевдоним.

— Уведомления?

— После того как заработает передача.

— Сервер?

— Для первой проверки можно обойтись локальным сценарием или минимальным сервисом.

Рустик чувствовал странное сопротивление.

Он хотел увидеть приложение целиком.

Хотел уже сейчас представить магазин, пользователей, переводы и комиссию, из которой однажды сложится дополнительный доход.

Но каждый раз, когда он пытался перепрыгнуть вперёд, ChatMind возвращала его к двум телефонам.

— Почему нельзя сразу сделать нормально?

— Потому что «нормально» состоит из многих независимых задач. Если выполнить их одновременно, вы не узнаете, какая из них сломала результат.

Это было похоже на его основную работу.

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

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

В мобильной разработке слова были другими, но принцип оказался знакомым.

— Значит, сначала обнаружение?

— Сначала точное описание сценария.

ChatMind попросила написать его одной строкой.

Рустик попробовал:

— Пользователь отправляет благодарность человеку рядом.

— Слишком широко.

Он написал ещё раз:

— Один телефон находит другой по Bluetooth и отправляет ему знак благодарности.

— Почти. Добавьте подтверждение, чтобы исключить ошибочный выбор.

Третья версия заняла больше времени:

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

ChatMind ответила:

— Это уже можно проверять.

Рустик перечитал строку.

В ней не было денег.

Не было красивого названия.

Не было обещания изменить жизнь.

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

Он сохранил текст в заметки.

Потом всё-таки спросил:

— А настоящие чаевые потом можно добавить?

В ответе появились аккаунты, платёжный партнёр, серверная проверка операций. Нужно было учитывать возвраты и повторные запросы, разбираться с требованиями к платежам и публикации. До всего этого ещё предстояло дойти.

Чем дальше шёл ответ, тем меньше будущая денежная функция походила на кнопку.

Она напоминала отдельный проект внутри проекта.

Рустик на несколько секунд почувствовал прежнее разочарование.

Идея снова отдалялась.

Но теперь между ним и результатом хотя бы появилась лестница.

Первая ступень не обещала денег.

Она обещала доказать, что два незнакомых человека могут найти друг друга без обмена личными данными.

Рустик вернулся к названию.

В заметках уже лежало несколько вариантов.

Одни звучали слишком банковскими.

Другие — как сервис скидок.

Третьи были заняты.

Среди них задержалось короткое слово:

DeepTip.

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

Tip — потому что первоначальная мысль всё равно оставалась мыслью о чаевых.

Рустик произнёс название вслух.

— Диптип.

С первого раза оно прозвучало немного нелепо.

Почти все новые названия сначала звучат нелепо, пока за ними ничего нет.

Он написал:

— Пусть пока будет DeepTip.

ChatMind приняла название без восторга и без возражений.

Затем спросила:

— На каком компьютере вы будете запускать проект?

Рустик посмотрел на телефон.

На кухонном столе лежал только он.

Старый домашний ноутбук давно работал через раз. На служебном компьютере заниматься личным приложением было нельзя. Днём Рустик находился на основной работе. Вечером — в машине. Свободные минуты появлялись у ресторанов, на парковках, в лифтах и между заказами.

Он ответил:

— Сейчас у меня только iPhone.

Пауза после сообщения показалась длиннее обычной.

Рустик уже ждал, что на этом разговор закончится.

Что ChatMind объяснит очевидное: мобильные приложения разрабатывают на компьютере, код пишут в редакторе, сборки запускают в специальной среде, а телефон нужен только для тестирования.

Ответ пришёл другим:

— Это сильно усложняет работу. Но начать можно.

Рустик подвинул телефон ближе.

— С чего?

ChatMind не создала готовый архив.

Не написала, что проект завершён.

Она перечислила первые инструменты, которые позволят хранить файлы, выполнять команды и не терять очередную версию между разговорами.

Большинство названий Рустик видел впервые.

Он открыл магазин приложений.

Нашёл первое.

Нажал «Загрузить».

Круг вокруг значка начал медленно заполняться.

У DeepTip ещё не было ни одного экрана.

Ни одной кнопки.

Ни одной строки рабочего мобильного кода.

Но теперь у проекта было то, чего не было у пустых архивов DeepSight.

Один сценарий, который можно проверить.

И человек, который согласился начать с первого шага.

Глава 4. С телефона в репозиторий

Круг вокруг значка замкнулся.

На экране появилась кнопка «Открыть».

Рустик нажал её.

Телефон показал почти пустой чёрный экран iSH. Рустик узнал приглашение командной строки. Непривычным был сам телефон: ни отдельных окон, ни удобной клавиатуры, ни понятного пока пути к его файлам.

— Терминал открылся, — написал он ChatMind. — Как здесь добраться до файлов телефона?

Модель объяснила, что у iSH своя файловая система и привычные папки iPhone не появятся в ней сами. Сначала предложила проверить текущий каталог.

Рустик коснулся строки. Половину экрана заняла клавиатура, а чёрное поле уменьшилось до узкой полосы. Он ввёл команду и нажал Enter.

Появился путь. Затем короткий список папок.

— Вижу, — написал Рустик.

На работе он почти не замечал этих действий. Здесь пришлось заново выяснять, как вставить строку, где находится нужная клавиша и сколько вывода останется видно после появления клавиатуры. Телефон отвечал, но до привычной скорости было далеко.

ChatMind начала объяснять, что понадобится дальше.

Средство для работы с файлами.

Архиватор.

Git.

Место, где проект будет храниться постоянно.

Git Рустик уже использовал на работе. На основной работе код служебных скриптов и конфигураций тоже жил в репозиториях. Он видел ветки, сборки и историю изменений. Иногда сам вносил правки, создавал commit и ждал, когда внутренний конвейер проверит результат.

Но там всё уже было настроено другими людьми.

Адреса существовали.

Права были выданы.

Инструкции лежали во внутренней базе знаний.

Если возникала проблема, рядом находился коллега, который хотя бы понимал, о какой системе идёт речь.

Сейчас у Рустика был только личный телефон, пустой терминал и разговор с моделью.

— Сначала нужно создать репозиторий? — спросил он.

— Сначала полезно получить хотя бы минимальную структуру проекта, — ответила ChatMind. — Но не воспринимайте её как готовое приложение. Мы зафиксируем исходную точку и будем менять её постепенно.

После DeepSight эта оговорка имела значение.

Рустик спросил прямо:

— Что именно будет работать в первом архиве?

ChatMind перечислила честнее, чем он ожидал.

Основа мобильного проекта.

Несколько файлов конфигурации.

Заготовка главного экрана.

Место для будущего обнаружения устройств.

Описание проверяемого сценария.

Без настоящих переводов.

Без готового Bluetooth-обмена.

Без обещания, что архив сразу соберётся на телефоне.

— То есть приложения пока не будет?

— Будет начало приложения. Это не одно и то же.

Рустик уже знал цену этой разницы.

Он согласился.

Через некоторое время в чате появилась ссылка на ZIP-файл. Название было техническим и не особенно красивым: rn-ble-peer.zip.

DeepTip уже имел имя, но внутри разработки всё ещё называлось по функции: React Native, Bluetooth Low Energy, соединение между устройствами.

Рустик скачал архив на iPhone.

Открыл приложение «Файлы».

Коснулся ZIP.

Рядом появилась папка.

Внутри лежали другие папки и файлы.

Он открыл первый.

На этот раз там было не одно обещание будущей функции.

Строки импорта.

Названия компонентов.

Текст кнопки.

Фигурные скобки.

Слова, которые он понимал по отдельности и почти не понимал вместе.

Он открыл второй файл.

Там находились настройки проекта.

Третий — описание запуска.

Четвёртый — короткая заметка о сценарии двух телефонов.

Архив не был приложением.

Но и пустым не был.

Рустик пролистал код ещё раз, словно количество строк могло подтвердить их ценность.

Потом спросил:

— А как проверить, что это вообще настоящее?

ChatMind объяснила: нужен компьютер или удалённая среда, где можно установить зависимости и запустить сборку. На iPhone часть операций получится выполнить в терминале, но полноценная мобильная разработка будет постоянно упираться в ограничения системы.

Слово «ограничения» стало появляться слишком часто.

Ограничение телефона.

Ограничение бесплатного доступа.

Ограничение размера файлов.

Ограничение времени между работой и заказами.

Ограничение денег, ради которых всё и начиналось.

Рустик закрыл архив.

В курьерском приложении уже горело предложение выйти на линию.

Он убрал телефон в держатель, завёл машину и решил, что продолжит позже.

Позже наступило на парковке возле ресторана.

Заказ задерживали.

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

Рустик остался в машине.

Открыл ChatMind.

— Что делать с архивом дальше?

Модель предложила создать несколько простых файлов, проверить структуру и сохранить всё в постоянном месте.

Сначала Рустик работал прямо через приложение «Файлы» и текстовый редактор. Он копировал небольшие фрагменты из чата, создавал файл, вставлял текст, возвращался за следующим фрагментом.

Каждое переключение занимало секунды.

Но секунды складывались.

ChatMind.

Копировать.

Файлы.

Открыть папку.

Найти нужный файл.

Вставить.

Сохранить.

Вернуться.

Иногда iPhone удерживал все приложения в памяти.

Иногда открывал их заново.

Иногда после возвращения чат оказывался не на том месте, и Рустик несколько минут искал фрагмент, который только что копировал.

Однажды вместо обычных кавычек в файл попали типографские.

В другой раз длинная строка разделилась переносом.

В третий — он вставил новый блок поверх старого и заметил это только после того, как ChatMind попросила показать содержимое целиком.

Телефон завибрировал.

Заказ готов.

Рустик сохранил файл, сунул телефон в карман и пошёл в ресторан.

Через двадцать минут он уже стоял у подъезда нового жилого комплекса. Домофон не отвечал. Клиент не читал сообщения. Позади машины медленно собиралась очередь, потому что двор был рассчитан на людей, которые никогда не заказывают доставку и не вызывают такси.

Когда клиент наконец открыл дверь, на экране поверх навигатора появилось сообщение от жены:

Ты сегодня поздно?

Рустик посмотрел на время.

Он вышел «на пару заказов» почти три часа назад.

Этот последний. Потом домой.

Он отправил сообщение и сразу принял ещё один заказ.

Сумма была хорошей.

Маршрут вёл почти в его сторону.

По крайней мере так показалось до того, как приложение добавило вторую точку и перестроило дорогу.

Домой Рустик приехал, когда младшая дочь уже спала.

Сын сидел в комнате с телефоном. Старшая дочь вышла навстречу и начала рассказывать что-то про школу, но Рустик ещё несколько секунд смотрел на экран курьерского приложения, закрывая смену и проверяя итоговую сумму.

— Пап, ты слышишь?

— Да, конечно.

Он поднял глаза.

— Повтори только начало.

Дочь вздохнула и начала снова.

Позже, когда дома стало тихо, Рустик открыл папку DeepTip.

У него уже было несколько изменённых файлов.

ChatMind предложила собрать новую версию архива, чтобы не потерять изменения.

Рустик выделил папку и нажал «Сжать».

Появился новый ZIP.

Он назвал его rn-ble-peer-new.zip.

Название казалось временным.

Через два дня рядом лежали:

rn-ble-peer.zip;

rn-ble-peer-new.zip;

rn-ble-peer-fixed.zip;

TipApp_Full_Project.zip.

Позже появилось слово final.

Потом final2.

Потом final-fixed.

Слово «финальный» быстро потеряло смысл.

Оно означало не конец работы, а конец терпения в конкретный вечер.

Сначала архивы помогали.

Новый разговор начинался с загрузки последней версии. ChatMind изучала содержимое, предлагала исправления и иногда формировала новый пакет файлов. Рустик скачивал его, распаковывал и продолжал.

Так проект переезжал из чата в чат.

Но с каждой поездкой чемодан становился тяжелее.

Однажды загрузка остановилась почти в самом конце.

Рустик повторил.

Чат сообщил, что файл слишком большой.

Он удалил старые папки внутри архива.

Попробовал снова.

На этот раз файл загрузился, но разговор долго не отвечал.

Потом приложение закрылось.

После повторного открытия вложение исчезло.

В другой раз ChatMind получила архив, но Рустик не смог уверенно объяснить, что именно было изменено после предыдущей версии.

Он писал:

— Там поправлен экран и ещё ошибка с кнопкой.

Модель спрашивала:

— В каком файле?

Рустик не помнил.

— Посмотри сама.

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

Иногда Рустик возвращался туда, находил нужное объяснение и переносил его в новый разговор.

Иногда не находил.

Иногда находил два разных объяснения одной и той же проблемы.

Каждый чат был умным, но каждый начинал жизнь заново.

Рустик становился их общей памятью.

Он должен был помнить, что уже пробовали.

Какой файл был последним.

Какая ошибка исчезла.

Какая вернулась.

Почему одна функция была отложена.

Что означало очередное название архива.

У него плохо получалось.

Не потому, что он был невнимательным.

Просто кроме проекта существовала жизнь.

Рабочие задачи.

Звонки.

Семейные расходы.

Школьные сообщения.

Доставка.

Бензин.

Неисправность машины, звук которой он пока предпочитал не замечать.

И сон, который каждый день переносился на более позднее время.

Однажды ночью Рустик открыл папку загрузок и увидел восемь архивов.

Названия различались на одно-два слова.

Размеры — на несколько килобайт.

Даты — на часы.

Он попытался восстановить порядок.

В final не было правки, которая находилась в fixed.

В fixed не было нового экрана из new.

В final2 экран был, но вернулась старая версия другого файла.

Рустик почувствовал знакомое раздражение.

Так иногда происходило и на работе, когда кто-то вручную копировал конфигурацию между серверами, а потом никто не мог сказать, где находится правильный вариант.

Там для этого существовали репозитории.

История изменений.

Сравнение версий.

Возможность вернуться к известному состоянию.

Он посмотрел на ряд ZIP-файлов и впервые задал ChatMind вопрос не о приложении:

— Как сделать так, чтобы мы больше не перекидывали весь проект туда-сюда?

Ответ был коротким:

— Поместить его в Git-репозиторий.

Рустик уже слышал это раньше.

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

Теперь настоящий проект как раз тонул в папке загрузок.

— У меня только iPhone.

— Можно начать через веб-интерфейс CodeHub, а для полной загрузки использовать iSH.

Рустик открыл сайт.

На маленьком экране CodeHub выглядел плотным и чужим. Меню пряталось за значками. Кнопки меняли положение при повороте телефона. Английские слова, знакомые по работе, здесь стояли без внутренней инструкции и без коллег.

Он зарегистрировался.

Сайт предложил создать первый repository.

Рустик ввёл название:

DeepTip.

Потом стёр заглавные буквы.

deeptip.

Описание заняло больше времени.

«Приложение для чаевых по Bluetooth» звучало слишком узко.

«Сервис благодарности людям рядом» — слишком рекламно для места, где пока лежал набор файлов.

Он написал:

Prototype for sending a digital sign of gratitude to a nearby person.

Проверил перевод.

Исправил артикль.

Потом решил, что описание всё равно никто не увидит, и оставил как есть.

Следующий выбор оказался неожиданным:

Public или private.

Рустик представил, что любой человек сможет открыть его незаконченный код, увидеть ошибки и названия файлов с бесконечными final.

Он выбрал private.

CodeHub создал пустой репозиторий.

На странице появились инструкции для тех, кто уже знал Git.

Строки начинались со знака доллара и содержали команды.

Рустик увеличил экран двумя пальцами.

— Репозиторий создал. Как загрузить туда папку?

ChatMind сначала предложила веб-интерфейс.

Рустик нажал кнопку добавления файлов.

Выбрал ZIP.

CodeHub загрузил его как один объект.

На странице появился TipApp_Full_Project.zip.

Формально проект теперь находился в репозитории.

Практически это был тот же закрытый чемодан.

Нельзя было открыть папку mobile.

Нельзя было сравнить отдельные файлы.

История изменений видела только замену одного архива другим.

— Так не подходит, — написала ChatMind. — Нужно загрузить распакованную структуру.

Рустик удалил ZIP из репозитория и попробовал выбрать папку через браузер.

Телефон показал содержимое.

Он выделил несколько файлов.

Вложенные каталоги не добавились.

Попробовал снова.

Страница перезагрузилась.

Третий раз кнопка подтверждения оказалась ниже области, до которой он мог нормально прокрутить экран, не закрыв список выбранных файлов.

Через двадцать минут в репозитории появились четыре файла из нескольких десятков.

Один лежал не в той папке.

Второй загрузился дважды.

Рустик удалил всё.

— Через сайт невозможно.

— Тогда через iSH.

Чёрный экран снова открылся с мигающим курсором.

ChatMind дала команду установки Git и архиватора.

Рустик скопировал её целиком.

Вставил.

Нажал Enter.

Терминал начал выводить строки.

Названия пакетов.

Проценты.

Адреса.

Предупреждения.

В конце вернулся курсор.

— Как понять, установилось или нет?

ChatMind предложила проверить версию Git.

Команда ответила номером.

Рустик сделал снимок экрана.

Не потому, что результат был красивым.

Он просто боялся, что через минуту всё исчезнет.

Дальше нужно было перенести архив из обычных файлов iPhone внутрь iSH.

Операционная система позволяла видеть папку терминала, но путь к ней приходилось искать через несколько уровней меню. Рустик нажал «Поделиться», выбрал iSH, дождался копирования, вернулся в чёрное окно и попросил показать список файлов.

Архива не было.

Он повторил.

Появились два одинаковых файла с разными окончаниями.

Один оказался пустым.

Второй открылся.

Команда распаковки вывела на экран десятки строк.

Имена файлов быстро уходили вверх.

package.json.

src.

components.

App.

README.

Рустик смотрел, как структура возникает внутри телефона.

Не в описании ChatMind.

Не на картинке.

Не в архиве, который нужно открыть пальцем.

Настоящие каталоги в файловой системе маленького Linux, спрятанного внутри iPhone.

— Теперь Git?

— Сначала перейдите в папку проекта и убедитесь, что внутри правильные файлы.

Рустик ввёл команду перехода.

Терминал ответил, что такой директории нет.

Он показал ChatMind снимок.

Модель заметила пробел в названии папки.

Рустик попробовал добавить кавычки.

Ошибка изменилась.

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

Команда pwd показала текущий путь, ls — содержимое. Архив распаковался во вложенную папку, а Рустик пытался перейти к нему уровнем выше.

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

На работе он поступал так автоматически. Здесь торопился добраться до результата и пропускал знакомые проверки.

Через несколько попыток в строке появился нужный каталог.

Рустик ввёл:

git init

Терминал сообщил, что создан пустой репозиторий.

Фраза звучала скромно.

Никакой анимации.

Никакой кнопки «Ваш проект теперь защищён».

Внутри папки просто возникла скрытая история, в которой пока не было ни одного события.

Следующая команда подготовила файлы.

Потом ChatMind попросила создать первый commit.

— Что написать в сообщении?

— Коротко опишите состояние. Например: Initial DeepTip import.

Рустик написал по-русски:

Первый DeepTip

Терминал отказался сохранять commit.

В этой среде ещё не были настроены имя и электронная почта автора.

— Здесь укажу личные данные для коммитов, — написал Рустик. — Рабочие переносить не нужно.

ChatMind прислала команды настройки. Они задавали подпись изменений; вход в CodeHub предстояло настроить отдельно.

Рустик указал имя, затем адрес и повторил commit.

На экране появился список добавленных файлов.

Первый снимок проекта был сохранён.

Рустик ещё не чувствовал его ценности.

ZIP тоже был снимком.

Но ChatMind объяснила разницу:

архивы лежали рядом и ничего не знали друг о друге;

commits образовывали последовательность;

Git запоминал, какие строки добавились, исчезли или изменились;

при ошибке можно было увидеть момент, когда она появилась;

можно было вернуться назад, не угадывая по слову final в названии.

Рустик посмотрел на восемь архивов в обычной папке.

Впервые они показались не версиями проекта, а следами отсутствия истории.

Оставалось отправить локальный репозиторий в CodeHub.

ChatMind дала команду с адресом.

Рустик вставил её.

Добавил удалённое хранилище.

Проверил.

Запустил push.

CodeHub попросил имя пользователя.

Рустик ввёл.

Потом появился запрос пароля.

Он ввёл обычный пароль от сайта.

Терминал сообщил об ошибке аутентификации.

Он повторил медленнее.

Та же ошибка.

Проверил пароль в браузере — сайт открылся.

— CodeHub пароль принимает, а терминал нет.

ChatMind объяснила, что для этого способа подключения обычный пароль учётной записи не подходит.

— Нужен отдельный токен? Где его создать?

В ответе появился путь по настройкам профиля. Предстояло выбрать тип токена, срок действия и права доступа, а затем сохранить строку, которую сервис покажет только один раз.

Рустик вернулся в Safari.

CodeHub снова попросил подтвердить вход.

Потом найти нужный раздел.

На мобильной версии часть пунктов скрывалась внутри меню.

Он открыл не тот тип токена.

Вернулся.

Выбрал другой.

Сайт показал список разрешений, названия которых звучали так, будто одна неправильная галочка позволит телефону управлять половиной интернета.

— Какие права ставить?

ChatMind попросила минимальный доступ к репозиторию.

Рустик отметил нужное.

Создал токен.

На экране появилась длинная последовательность букв и цифр.

Рядом предупреждение: сохранить сейчас, позже значение не будет показано.

Он скопировал строку в заметки.

Потом задумался, безопасно ли хранить её там.

ChatMind сказала, что лучше не оставлять секрет в обычном файле и тем более не отправлять его в чат.

Рустик удалил заметку, но перед этим снова скопировал токен в буфер.

Вернулся в iSH.

Сеанс терминала сохранился.

Запустил push ещё раз.

Имя пользователя.

Пароль.

Он вставил токен.

Ввод в поле пароля не отображался. Рустик это знал, но на тесном экране не понял, сработала ли вставка: меню исчезло под пальцем, а клавиатура на мгновение закрыла строку.

Он вызвал меню ещё раз и только после второго нажатия остановился.

— Кажется, вставил дважды.

— Тогда отмените ввод и начните команду заново, — ответила ChatMind.

На компьютере Рустик давно закончил бы эту часть. Здесь приходилось проверять даже собственные движения.

Он отменил команду.

Начал заново.

Вставил один раз.

Нажал Enter.

Ошибка.

Он проверил адрес репозитория.

В одном месте была лишняя буква.

Исправил.

Снова имя.

Снова невидимый токен.

Ошибка.

Возможно, не хватало прав.

Он создал второй токен.

Потом не смог понять, какой из двух сейчас находится в буфере.

Удалил оба.

Создал третий.

Телефон нагрелся.

Заряд опустился ниже двадцати процентов.

Рустик подключил кабель, но тот отходил, если положить телефон под неудобным углом.

На кухню вошла жена.

— Ты опять работаешь?

— Не работа. Пытаюсь проект сохранить.

Она посмотрела на чёрный экран с короткими строками.

— Он пропадёт, если ты сейчас ляжешь спать?

Рустик хотел ответить, что нет.

Архивы лежали в телефоне.

Файлы не исчезнут до утра.

Но внутри него уже возникло знакомое ощущение: если остановиться на ошибке, завтра придётся сначала восстановить всё состояние — вспомнить, какой токен создан, какой адрес исправлен, в какой папке он находится и что именно советовала ChatMind.

— Не пропадёт, — сказал он. — Я почти закончил.

«Почти» было вторым словом после «финальный», которое быстро теряло значение.

Жена ничего не ответила.

Поставила рядом чашку чая и ушла.

Рустик сделал ещё одну попытку.

Ошибка изменилась.

Это обнадёживало.

Теперь CodeHub узнавал его, но не находил нужную ветку.

ChatMind объяснила разницу между локальным именем ветки и тем, которое ожидал удалённый репозиторий.

Рустик выполнил ещё две команды.

Снова push.

Имя.

Невидимый токен.

Enter.

Сначала ничего не происходило.

Потом экран сдвинулся.

Появилась строка подсчёта объектов.

Ниже — сжатие.

Проценты начали расти.

Рустик не трогал телефон.

Даже кабель боялся поправить.

Сто процентов.

Следующая строка.

Передача объектов.

Ещё проценты.

Потом терминал вернул курсор.

Ошибка не появилась.

— И всё? — написал Рустик.

ChatMind попросила открыть CodeHub и обновить страницу.

Он переключился в браузер.

Пустая страница репозитория исчезла.

Перед ним находились папки.

Не один ZIP-файл.

Не четыре случайно выбранных документа.

Вся структура.

Рустик открыл первую папку.

Потом файл.

CodeHub показал код построчно, с номерами слева.

Он вернулся назад.

Открыл историю.

Там находился один commit.

Первый DeepTip.

Автор — он.

Время — несколько секунд назад.

Для любого разработчика это было обычным началом.

Пустой репозиторий получил первый набор файлов.

Для Рустика событие выглядело иначе.

До этой минуты DeepTip находился в разговорах, ссылках и архивах с названиями, которым нельзя было доверять.

Теперь у проекта появилось место, которое помнило не только файлы, но и момент их изменения.

Рустик обновил страницу ещё раз.

Папки остались.

Закрыл браузер.

Открыл снова.

Они никуда не исчезли.

Он отправил ChatMind ссылку на репозиторий.

Модель ответила, что не может сама открыть приватное хранилище без доступа, но теперь Рустик сможет приносить не весь архив, а конкретные файлы, изменения и ошибки.

Это было меньше, чем он вообразит значительно позже.

ChatMind ещё не получила рук.

Не могла сама исправить строку.

Не могла сделать commit.

Не могла нажать push.

Между её ответом и репозиторием по-прежнему находился Рустик.

Но теперь он стоял не между двумя исчезающими окнами.

За его спиной появилась память.

Он посмотрел на чашку.

Чай давно остыл.

На часах было далеко за полночь.

Рустик удалил из папки загрузок несколько старых архивов.

Над последним задержался.

final-fixed-2.zip.

Рука не поднялась стереть его сразу.

Вдруг именно там находилось что-то, чего нет в репозитории?

Он оставил архив.

Потом ещё два.

Доверие к истории изменений не возникало одной командой.

Но впервые у него появилась возможность не создавать очередной final.

ChatMind написала:

— Теперь зафиксируем следующее изменение отдельно. Откройте файл главного экрана.

Рустик нашёл его в CodeHub.

На странице лежал код, который нужно было изменить.

— Что именно вставить?

В ответе ChatMind появился длинный блок текста.

Рустик прижал палец к первой строке.

Выделение захватило не весь код.

Он потянул маркер вниз.

Страница дёрнулась.

Выделение исчезло.

Рустик попробовал снова.

Репозиторий наконец запомнил проект.

Но переносить каждую новую строку между машиной и его файлами всё ещё предстояло человеку.

Глава 5. Зелёная сборка

Выделение исчезло в третий раз.

Рустик убрал палец от экрана и несколько секунд просто смотрел на страницу CodeHub.

В файле было чуть больше сотни строк. ChatMind прислала почти весь файл заново. Нужно было заменить старый текст новым. На компьютере это заняло бы меньше минуты. На телефоне сама операция уже превращалась в отдельную задачу.

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

Он скопировал текст. Переключился в приложение «Файлы». Открыл нужную папку. Потом вспомнил, что редактировать файл оттуда неудобно.

Вернулся в iSH. Проверил текущую директорию. Не та. Перешёл в папку проекта. Открыл файл в текстовом редакторе.

Телефон снова уменьшил рабочую область, отдав половину экрана клавиатуре.

Рустик выделил старое содержимое, удалил и вставил новое. Несколько секунд ничего не происходило. Потом текст появился.

Он пролистал начало. Потом середину. Потом конец. Вроде всё. Сохранил. Закрыл редактор.

ChatMind попросила показать первые двадцать строк и участок рядом с изменённой функцией.

Рустик выполнил команду, выделил вывод терминала, скопировал и вернулся в чат.

— Похоже, файл обновился правильно, — ответила модель. — Теперь ещё три файла.

Рустик перечитал последнее предложение.

— Три?

ChatMind перечислила пути. Первый файл — конфигурация. Второй — компонент списка устройств. Третий — заготовка локальной истории. Для каждого она подготовила отдельный блок.

Рустик посмотрел на время. Он собирался лечь спать сорок минут назад.

— А нельзя как-то сразу?

— Можно подготовить единый набор изменений.

— Нет. Я имею в виду вообще не открывать каждый файл руками.

Ответ появился почти сразу.

— Можно написать небольшой скрипт, который сам создаст или заменит нужные файлы.

Рустик остановился.

Фраза была настолько очевидной, что раздражала.

Весь день он пытался с телефона работать как человек за большим монитором: открыть файл, найти место, выделить, вставить, сохранить.

Хотя на основной работе именно от таких повторяющихся действий он обычно избавлялся первым.

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

Он писал PowerShell.

Если требовалось проверить несколько служб, он не открывал оснастку на каждой машине.

Он писал цикл.

Если нужно было поменять конфигурацию в одном и том же месте, он сначала искал способ сделать это один раз и повторить автоматически.

Но здесь, в чужой для него мобильной разработке, Рустик почему-то снова превратился в человека, который нажимает кнопки по одной.

— Давай скриптом, — написал он.

ChatMind предупредила:

— Тогда сначала сохраним текущую версию в Git. Если скрипт сделает что-то не так, будет куда вернуться.

Рустик уже начинал понимать, зачем репозиторий нужен не только «чтобы проект не потерялся».

Он выполнил git status.

Терминал показал изменённый файл.

Потом ещё несколько файлов, которые появились раньше и так и не были зафиксированы.

— Почему они тоже изменены?

ChatMind не могла видеть его экран и попросила прислать вывод.

Рустик скопировал.

Модель объяснила, что часть файлов могла измениться после распаковки, настройки проекта или предыдущих попыток.

— Их все добавлять?

— Нет. Сначала разберёмся, что именно изменилось.

Это было не то, что Рустик хотел услышать.

Он хотел одну команду.

Получил расследование.

git diff.

Вывод занял несколько экранов.

На телефоне цветные строки выглядели почти как наказание за желание что-нибудь автоматизировать.

Рустик листал вверх и вниз, пытаясь понять, где настоящее изменение, а где просто другой перенос строки.

В одном файле поменялось несколько значений.

В другом CodeHub показывал почти весь документ изменённым, хотя на глаз он выглядел тем же.

ChatMind предположила различие окончаний строк.

Рустик спросил:

— То есть я вообще ничего не менял, а Git считает, что поменял весь файл?

— Такое возможно.

— Хорошо начинается.

Он открыл ещё один вывод. Потом ещё один. Через двадцать минут они отделили нужное изменение от случайных.

Рустик добавил только выбранный файл. Сделал commit. Ввёл push. На этот раз CodeHub не просил заново доказывать, кто он. Строки передачи объектов появились быстро.

Рустик обновил страницу в браузере. В истории было уже два коммита: «Первый DeepTip» и «Главный экран».

Два маленьких прямоугольника истории.

Между ними — несколько часов жизни.

— Теперь скрипт, — напомнил Рустик.

ChatMind подготовила Python-файл. Внутри не было ничего похожего на мобильное приложение. Несколько путей. Несколько больших текстовых блоков. Создание папок, если их нет. Запись содержимого. Проверка результата.

В конце — короткая строка:

DONE

— И всё?

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

Рустик посмотрел на приглашение терминала.

Путь в строке был длинным и не помещался целиком. Он выполнил pwd. Корень проекта.

Скопировал скрипт из чата. Создал файл. Назвал его apply_update.py. Вставил. Сохранил. Запустил.

Терминал ответил почти мгновенно:

DONE

Рустик даже не успел приготовиться к ошибке.

— Он что, уже всё сделал?

— Если скрипт завершился без исключения, файлы должны быть обновлены. Проверьте git diff --stat.

Рустик выполнил команду. Четыре файла. Несколько сотен добавленных строк. Несколько удалённых.

Он открыл один из них. Изменение было на месте. Второй. Тоже. Третий. Тоже.

Рустик откинулся на спинку стула.

До этого момента машина могла писать код быстрее, чем он переносил его в проект. Теперь впервые скорость переноса выросла.

Не потому, что ChatMind получила доступ к CodeHub. Не потому, что научилась нажимать кнопки на его телефоне. Она всё ещё сидела за стеклом чата.

Но вместо четырёх отдельных действий дала ему одно действие, внутри которого лежали остальные.

Рустик написал:

— Вот так и надо было с самого начала.

ChatMind ответила осторожнее:

— Для небольших точечных изменений ручное редактирование иногда проще. Для серии связанных изменений скрипт действительно удобнее.

— Для телефона всё, что меньше десяти нажатий, удобнее.

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

Рустик выполнил git diff. Теперь изменения выглядели осмысленно. Главный экран. Список устройств рядом. Кнопка включения видимости. Простейшая история.

Никаких настоящих переводов.

Никаких аккаунтов.

Никаких денег.

Но проект уже начинал быть похожим не на набор папок, а на программу, в которой одна часть знала о другой.

Работа над этими экранами растягивалась между сменами. Иногда за день удавалось только прочитать ответ и сохранить место, с которого нужно продолжить вечером.

— Можно запустить? — спросил он.

ChatMind напомнила о том, что они обсуждали раньше.

На iPhone он мог хранить проект, менять файлы, пользоваться Git и частью инструментов командной строки.

Но полноценная сборка мобильного приложения упиралась в среду.

Для Android нужны были другие инструменты.

Для iOS — подписи, сборочная цепочка и ограничения самой платформы.

Рустик посмотрел на телефон.

— То есть я опять сделал то, чего не могу проверить?

— Локально на этом устройстве — не полностью. Можно использовать облачную сборку.

Рустик уже слышал такие слова раньше.

«Облачная сборка».

Звучало просто.

Где-то далеко есть компьютер. Он получает проект. Собирает приложение. Возвращает готовый файл.

В реальности между этими словами обычно находились регистрация, настройки, токены, версии инструментов и ошибки, которые никто не показывал в рекламном описании.

— Что нужно?

ChatMind назвала BuildCloud.

Сервис умел забирать мобильный проект, устанавливать зависимости и собирать Android-пакет на удалённых машинах.

Нужно было создать учётную запись, связать проект и добавить конфигурацию сборки.

— Бесплатно?

— Есть бесплатный лимит.

Это было главным параметром.

Рустик открыл сайт.

Форма регистрации попросила почту. Потом пароль. Потом подтверждение. Потом имя проекта. Потом предложила установить командный инструмент.

Он вернулся в iSH. Команда установки закончилась ошибкой. Рустик отправил её ChatMind. Модель предложила проверить версию Node. Терминал ответил номером, который оказался слишком старым.

— Обновить?

Можно было.

Но на iSH обновление одного пакета тянуло за собой другие.

ChatMind перечислила варианты.

Рустик выбрал самый короткий.

Через несколько минут терминал начал скачивать зависимости.

Строки бежали быстро.

Потом остановились.

Ошибка.

Не хватило подходящего пакета для одной из нативных частей.

Рустик посмотрел на экран.

— Мы же только что решили не собирать на телефоне. Почему установщик опять хочет что-то собирать на телефоне?

ChatMind объяснила разницу между самим инструментом отправки проекта в облако и мобильной сборкой.

Для запуска клиента BuildCloud тоже требовались зависимости.

— То есть чтобы отправить приложение на компьютер, который умеет его собирать, мне сначала нужно поставить программу, которая не ставится на телефон, потому что телефон не умеет её собрать?

— В вашей текущей среде получается примерно так.

Рустик засмеялся.

Не потому, что было смешно.

Просто формулировка наконец стала честной.

Он закрыл терминал.

На часах было почти два.

На кухонном столе лежал остывший чай.

В соседней комнате кто-то из детей перевернулся во сне, кровать тихо скрипнула.

Рустик выключил экран.

На этот раз действительно лёг.

Утром BuildCloud никуда не делся. Ошибка тоже.

Основная работа началась как обычно. Почта. Созвон. Несколько заявок. Проверка серверов.

Обсуждение тестового контура.

В какой-то момент коллега написал, что автоматическая установка службы на одном из серверов завершилась не так, как ожидалось.

Рустик открыл журнал.

Нашёл строку ошибки.

Сравнил с предыдущим запуском.

Проверил параметры.

Через несколько минут причина стала понятна: на машине отличалась конфигурация.

Обычная работа.

Понятная.

Предсказуемая хотя бы в том смысле, что ошибка существовала в конкретном месте.

Во время обеда Рустик снова открыл ChatMind.

— Можно вообще без клиента BuildCloud на телефоне?

Модель предложила другой путь.

Запускать сборку не с iPhone, а из CodeHub.

Репозиторий уже был там.

Если добавить специальный файл сценария, CodeHub сам сможет передать проект в BuildCloud или запустить подготовленный конвейер.

Рустик перечитал ответ дважды.

— То есть CodeHub сам будет запускать команду?

— Да. После push или вручную.

— А почему вчера мы пытались поставить всё это на телефон?

ChatMind не оправдывалась.

— Это был прямой путь, но в вашей среде он оказался неудачным. Через репозиторий будет устойчивее.

Рустик почувствовал знакомое раздражение.

Не на модель.

На собственное ожидание, что существует одна правильная дорога и её можно получить первым ответом.

На работе он знал: решение зависит от среды.

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

Маршрута не было.

Он появлялся под ногами.

— Тогда делаем через CodeHub.

ChatMind подготовила новый файл.

Небольшой.

Текст выглядел как инструкция для чужого робота.

Когда меняется основная ветка:

взять код;

установить зависимости;

проверить проект;

запустить сборку;

сохранить результат.

Рустик узнал сам принцип.

На работе внутренний конвейер делал почти то же самое с корпоративными приложениями.

Разница была в названиях.

И в том, что там подобные файлы писали и поддерживали команды.

Здесь команда помещалась в одном окне чата и одном человеке с телефоном.

Рустик снова попросил:

— Дай одним скриптом.

ChatMind подготовила apply_build.py.

Скрипт создавал каталог сценариев CodeHub, добавлял конфигурацию и ещё один небольшой файл проекта.

Рустик скопировал его.

Запустил.

DONE

Проверил diff.

На этот раз уже автоматически искал не красивые строки, а неожиданные изменения.

Их не было.

Commit.

Push.

В CodeHub рядом с новым коммитом появился серый кружок.

Рустик нажал на него.

Открылась страница проверки.

Первая строка:

Queued

— Что значит?

— Сборка ждёт свободного исполнителя.

Рустик обновил страницу.

Queued

Ещё раз.

Queued

Он убрал телефон.

Через несколько минут снова открыл.

Running

Внутри появлялись шаги.

Получение исходного кода.

Установка Node.

Установка пакетов.

Проверка конфигурации.

Рустик смотрел, как удалённая машина выполняет действия, которые он сам не мог выполнить на iPhone.

В этом было что-то почти физическое.

До сих пор всё происходило только там, где находились его пальцы.

Файл изменялся, потому что он вставил текст.

Commit появлялся, потому что он ввёл команду.

Push начинался, потому что он нажал Enter.

Теперь один push запустил работу на другой машине.

Рустик ничего не трогал.

Шаги двигались сами.

Установка зависимостей завершилась.

Следующий шаг загорелся.

Потом ещё один.

И вдруг весь экран стал красным.

Failed

Рустик открыл лог.

Тысячи строк.

Наверху всё выглядело нормально.

Внизу — десятки предупреждений.

Где именно причина, непонятно.

Он скопировал последние строки в ChatMind.

Модель ответила:

— Нужен фрагмент выше. Здесь уже следствие.

Рустик вернулся.

Попытался выделить нужное место.

Страница прыгнула.

Он скопировал слишком мало.

ChatMind попросила ещё.

Слишком много — сообщение не отправилось целиком.

Рустик разделил лог на части.

Первая.

Вторая.

Третья.

Модель нашла строку:

одна из библиотек ожидала другую версию React Native.

— Исправить?

— Лучше сначала проверить package.json.

Рустик открыл файл в CodeHub.

Версия была такой, как в первом архиве.

ChatMind объяснила: несколько зависимостей в заготовке не совпадали между собой.

Рустик вспомнил, как совсем недавно радовался тому, что архив был не пустым.

Теперь выяснялось, что наличие кода ещё не означает, что код согласован.

— Ты это сама писала.

— Да. И сейчас ошибка в предложенной мной конфигурации.

Рустик перечитал ответ.

Никакого «возможно, среда».

Никакого длинного объяснения.

Ошибка была в конфигурации, которую модель помогла создать.

Это почему-то успокоило.

— Тогда исправляй.

ChatMind подготовила новый скрипт.

Рустик назвал его apply_fix_01.py.

Число добавил сам.

На всякий случай.

Запустил.

Проверил diff.

Commit.

Push.

Серый кружок.

Queued.

Потом Running.

Потом красный.

Failed.

— Следующая, — написал Рустик.

Вторая ошибка была другой.

Конфигурация Android требовала параметр, которого в проекте не было.

apply_fix_02.py.

Commit.

Push.

Failed.

Третья ошибка возникла уже после установки зависимостей.

BuildCloud не нашёл один из файлов изображения.

Рустик открыл путь. Файл действительно назывался иначе. Одна буква была заглавной. На телефоне и в одном из предыдущих просмотров это не бросалось в глаза. На удалённой Linux-машине регистр имел значение.

apply_fix_03.py.

Commit.

Push.

Failed.

Свободного исполнителя пока не было. Новая задача несколько минут стояла в очереди.

Рустик смотрел на серый кружок и думал, что даже ошибка имеет очередь.

Вечером он вышел на доставку.

Не потому, что проект надоел.

Потому что проект пока не платил за бензин.

Первый заказ был коротким.

Второй утащил его в соседний район.

Третий оказался двойным: два ресторана, два адреса, почти одинаковые названия улиц.

Между точками телефон говорил голосом навигатора, курьерское приложение присылало таймеры, жена написала список того, что нужно купить по дороге домой, а в верхней части экрана CodeHub показал уведомление:

Workflow failed.

Рустик увидел его на светофоре и не открыл.

После прошлых недель у него появилась хотя бы одна полезная привычка.

Ошибка могла подождать.

Дорога — нет.

Он доехал до ресторана, припарковался и только тогда открыл лог.

Новая причина.

ChatMind попросила конфигурационный файл целиком.

Рустик отправил.

Модель несколько секунд не отвечала.

Ресторан тоже не отдавал заказ.

На парковке рядом стояла машина другого курьера.

Водитель опустил стекло.

— Тоже ждёшь?

— Ага.

— Они сегодня вообще не торопятся.

Рустик усмехнулся.

— У меня сегодня никто не торопится.

— В смысле?

— Да так.

Курьер пожал плечами и закрыл окно.

ChatMind прислала объяснение.

BuildCloud пытался собрать проект как обычное React Native-приложение, а структура была подготовлена под Expo.

Нужно было явно указать режим сборки.

Рустик прочитал.

Потом ещё раз.

— Мы четыре раза запускали неправильный тип сборки?

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

Логика была безупречной.

И от этого особенно неприятной.

Каждая исправленная ошибка не приближала к гарантированному успеху.

Она просто открывала следующую дверь.

Рустик набрал:

— То есть мы будем чинить их до бесконечности?

ChatMind ответила:

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

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

В общей истории всё ещё стояли четыре красные отметки. Но внутри каждой скрывалось больше выполненной работы, чем в предыдущей.

Рустик написал:

— Ладно. Давай следующую.

apply_fix_04.py. Он сохранил файл прямо в iSH. Запустил. Commit. Push.

Заказ наконец вынесли.

Рустик убрал телефон.

Следующие сорок минут он не проверял CodeHub.

Навигатор провёл его через дворы.

На одном адресе клиент написал: «Оставьте у двери».

На другом вышел сам.

Молодой парень в домашней футболке забрал пакет и сказал:

— Спасибо, хорошего вечера.

Обычная фраза.

Но Рустик вдруг снова вспомнил, зачем вообще существует папка DeepTip.

Не ради React Native.

Не ради BuildCloud.

Не ради зелёного кружка рядом с commit.

Не ради того, чтобы победить очередную ошибку.

Приложение должно было сделать этот короткий момент между двумя незнакомыми людьми чуть заметнее.

Он вернулся в машину.

На экране курьерского приложения уже появился следующий заказ.

Рустик принял.

Только после этого открыл CodeHub.

Красный.

Failed.

Он выдохнул.

Не удивился.

На этот раз сборка дошла почти до конца.

Ошибка была связана с подписью пакета.

ChatMind объяснила, что для тестовой Android-сборки можно использовать временную конфигурацию, не готовя полноценный магазинный релиз.

— Это безопасно?

— Для тестового пакета — да, если не путать его с производственной подписью и не публиковать секреты в репозитории.

Рустик насторожился.

Слово «секреты» он понимал слишком хорошо.

На основной работе ключи, пароли и токены не клали в код.

Здесь CodeHub был приватным, но это не делало плохую практику хорошей.

— Ничего секретного в файл не вставляем.

— Тогда используем секретное хранилище CodeHub для значений, а в сценарии оставим только ссылки на них.

Рустик открыл настройки репозитория.

На телефоне меню оказалось спрятано глубже, чем хотелось.

Раздел.

Ещё раздел.

Secrets.

Новый секрет.

Имя.

Значение.

Сохранить.

Потом второй.

Он несколько раз проверил, что не отправляет их в чат и не вставляет в файл.

ChatMind дала только названия переменных.

Рустик настроил.

apply_fix_05.py.

Commit.

Push.

Queued.

Он поехал дальше.

Когда вернулся домой, дети ещё не спали.

Младшая дочь сидела на кухне и рисовала.

На листе был дом, огромное солнце и машина, которая занимала почти половину дороги.

— Это наша? — спросил Рустик.

— Да.

— Почему такая большая?

— Потому что ты всегда в ней.

Он хотел засмеяться.

Не получилось.

— Я не всегда.

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

— А это кто? — спросил Рустик, показывая на человечка рядом с домом.

— Мама.

— А это?

— Я.

— А где я?

Дочь ткнула пальцем в машину.

— Вот.

Рустик молча посмотрел на рисунок.

На основной работе он был инженером.

В доставке — курьером.

В чате — человеком, который пытался создать приложение.

На детском рисунке всё это сократилось до машины.

Жена поставила перед ним тарелку.

— Ешь, пока не остыло.

— Сейчас.

— Нет. Сначала ешь.

Телефон лежал экраном вниз.

Он завибрировал.

Рустик не перевернул его.

Поужинал.

Выслушал рассказ старшей дочери.

Проверил, что сын сделал то, о чём они договаривались утром.

Помог младшей найти фломастер, который лежал прямо под листом.

Только когда дети разошлись по комнатам, он посмотрел на экран.

Уведомление CodeHub.

Без цвета.

Просто название проверки.

Он открыл.

Первая строка была зелёной.

Потом вторая.

Третья.

Установка зависимостей — зелёная.

Проверка проекта — зелёная.

Подготовка Android — зелёная.

Сборка — зелёная.

Внизу страницы стояло слово:

Success

Рустик не сразу понял, что произошло.

Он прокрутил ниже. Артефакты. Один файл. Название заканчивалось на .apk. Размер — несколько десятков мегабайт.

Рустик нажал. Началась загрузка. Полоса двигалась медленно.

Он смотрел на неё так же, как несколько дней назад смотрел на проценты первого push. Тогда CodeHub впервые принял проект. Теперь удалённая машина впервые отдала что-то обратно.

Не текст.

Не архив с исходниками.

Не структуру будущего приложения.

Файл, который можно было попробовать установить на Android.

Загрузка закончилась. Рустик открыл папку. APK лежал там.

Он нажал на него. iPhone показал меню, в котором не было ничего полезного. Открыть как приложение было невозможно. Рустик нажал ещё раз, будто система могла передумать. Нет.

Он написал ChatMind:

— Собралось.

— Отлично. Следующий шаг — установить APK на Android-устройство и проверить запуск.

Рустик посмотрел на свой iPhone.

На кухне было тихо.

В CodeHub горели зелёные галочки.

BuildCloud утверждал, что всё успешно.

Файл существовал.

Но увидеть DeepTip он всё ещё не мог.

Это было почти идеально.

Несколько месяцев назад Рустик считал, что успех выглядит как готовое приложение.

Потом — как непустой архив.

Потом — как первый commit.

Теперь успех выглядел как зелёная строка на чужом сервере и файл, который его собственный телефон не умел открыть.

Каждая достигнутая точка отодвигала границу настоящей проверки ещё на один шаг.

Он открыл историю CodeHub. Пять красных запусков. Один зелёный. Под ними — несколько коммитов с исправлениями.

Рустик пролистал названия: «Главный экран», «Добавить сборку», затем fix 01, fix 02, fix 03, fix 04, fix 05.

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

ChatMind предложила в следующий раз делать сообщения понятнее.

Рустик усмехнулся.

— В следующий раз?

— Если первая установка покажет новую ошибку, понадобится следующая итерация.

Конечно.

Зелёная сборка подтверждала, что пакет удалось создать. Запустится ли он на телефоне, ещё предстояло проверить.

Приложение могло не открыться. Могло открыть белый экран. Могло упасть при первом нажатии. Могло не увидеть второй телефон. Могло выглядеть совсем не так, как Рустик представлял.

Он знал это. Но всё равно снова открыл страницу успешной проверки. Шесть попыток. Одна зелёная.

Машина выполнила сотни действий сама. Установила пакеты. Подготовила среду. Собрала Android-проект. Сохранила результат. Рустик дал ей только репозиторий и сценарий.

Впервые в этой истории часть работы продолжалась после того, как он убрал руки от телефона.

Это ещё не была ChatMind. Она не управляла CodeHub. Не видела лог, пока Рустик его не приносил. Не могла сама запустить исправление. Не знала, что сборка упала, пока он не написал об этом.

Но между человеком и кодом появился первый автоматический слой. Сначала один скрипт менял четыре файла. Потом один push запускал десятки команд на чужой машине.

Рустик вдруг увидел направление, которого раньше не замечал.

Проблема заключалась не только в том, что модель не умела что-то делать.

Проблема была в расстоянии между её решением и местом, где это решение должно было исполниться.

Каждый раз это расстояние проходил он. Копировал. Вставлял. Запускал. Возвращал ошибку. Снова копировал. Снова вставлял. Снова запускал.

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

Не интеллект становился сильнее.

Становился короче путь от мысли до действия.

Рустик ещё не знал, насколько далеко эта мысль его заведёт.

Пока перед ним лежал APK, который он не мог открыть.

Он сохранил файл и создал отдельную папку. Хотел назвать её final, но остановился. Удалил слово. Назвал просто: build-1.

Потом написал ChatMind:

— Завтра найду Android и проверим.

Ответ пришёл быстро:

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

Рустик прочитал.

Это снова было похоже на его работу.

Не «починить всё».

Сначала доказать один факт.

Он закрыл CodeHub. Потом чат. Потом терминал.

На экране телефона осталась фотография рисунка младшей дочери, которую он успел сделать перед тем, как она убрала лист.

Большая машина. Маленький дом.

Он посмотрел на неё дольше, чем собирался.

DeepTip задумывался как способ однажды работать меньше.

Пока происходило обратное.

Основная работа. Доставка. Ночная разработка.

Но сегодня впервые часть этой третьей работы выполнила машина без него.

Совсем маленькая часть.

Достаточная только для того, чтобы на экране загорелось зелёное слово.

Рустик выключил телефон.

В другой комнате жена спросила:

— Получилось?

Он ответил не сразу.

Раньше сказал бы: «Почти».

Теперь это слово ему уже не нравилось.

— Собралось, — сказал он. — Проверять ещё надо.

— Значит, получилось или нет?

Рустик улыбнулся.

— Значит, сегодня — да.

Он лёг. Телефон больше не открывал.

В папке build-1 лежал первый APK.

В CodeHub оставалась первая зелёная сборка.

А между ChatMind и проектом всё ещё стоял человек.

Только теперь он впервые начал строить мост.

Глава 6. Первый запуск

На следующий день зелёная галочка уже не казалась такой убедительной.

Вчера ночью она была событием. Утром Рустик открыл папку build-1 и увидел лежащий в ней APK — несколько десятков мегабайт, которые ещё предстояло установить.

Рустик смотрел на название и вспоминал последнюю фразу ChatMind:

— При первой установке не меняйте сразу всё. Сначала зафиксируйте факт.

Рустик задержался на слове «факт». После пустых архивов он проверял, есть ли внутри код. После первых правок — сохранилась ли нужная версия в репозитории. Теперь CodeHub подтверждал, что пакет собран. Оставалось узнать, примет ли его телефон.

Обычный Android-смартфон с собственной памятью, системой установки и кнопкой питания. Устройство ничего не знало о том, сколько вечеров ушло на этот APK.

Рустик положил Android рядом с iPhone.

На одном последние месяцы жил почти весь процесс разработки. На другом предстояло впервые открыть результат.

Он снова открыл страницу последней сборки. Зелёная отметка оставалась на месте. За ночь ничего не исчезло, и это почему-то успокаивало.

Рустик открыл ту же страницу на Android и нажал на артефакт. Началась загрузка. На этот раз файл попадал туда, где его можно было запустить.

Полоса загрузки двигалась привычно медленно. Рустик несколько раз проверил, не погас ли экран. Когда файл появился в загрузках, он не стал сразу открывать его.

Сначала написал ChatMind:

— APK на Android. Что фиксируем?

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

Рустик посмотрел на список.

— Всё?

— Для первого прохода — да. Не проверяйте сейчас все функции.

Рустик усмехнулся. Если приложение откроется, ему захочется нажать всё. Если нет — немедленно начать чинить. Но сейчас хотя бы было понятно, с какой проверки не нужно перескакивать.

Он нажал на APK. Android отреагировал не приложением, а предупреждением. Система сообщала, что установка программ не из обычного магазина может быть опасной. Рустик прочитал текст дважды.

Несколько месяцев он пытался создать приложение. Теперь телефон впервые относился к его работе как к настоящей программе. Пусть пока подозрительной. Это было даже приятно.

Он разрешил установку из этого источника, вернулся назад и снова нажал на файл. Появилось системное окно с названием приложения и кнопкой установки.

Рустик нажал. Начала двигаться полоса.

ChatMind не могла помочь этой полосе двигаться быстрее. CodeHub отдал пакет, BuildCloud закончил свою работу ночью. Оставались Android и несколько секунд ожидания.

Рустик поймал себя на том, что держит телефон двумя руками. Так он обычно не делал. Для обычного приложения установка была скучным промежутком между скачиванием и запуском. Здесь каждая секунда будто проверяла всё сразу.

Он знал, сколько причин могло остановить установку: подпись, повреждённый пакет, конфликт версий, нехватка места. Любая из них вернула бы его к файлам и журналам. Рустик не сводил глаз с полосы.

Полоса дошла до конца. Экран на секунду замер. Потом Android показал сообщение:

Приложение установлено.

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

Теперь она относилась к тому, что ещё недавно существовало только в его разговорах с моделью.

Приложение установлено.

Он перечитал ещё раз. Потом сделал то, что ChatMind просила не усложнять. Зафиксировал факт.

— Установилось.

Ответ появился сразу:

— Хорошо. Следующий факт: запускается ли.

Рустик посмотрел на две системные кнопки. «Готово». «Открыть».

Можно было нажать «Готово», выйти на главный экран, найти иконку, проверить её отдельно. Но он уже несколько месяцев шёл к другой кнопке.

Нажал «Открыть».

Системное окно исчезло. На дисплее остался чёрный фон.

Рустик ждал. Секунда. Ещё одна. Палец замер над экраном: он боялся случайно прервать запуск, хотя понимал, что держать телефон совершенно неподвижно незачем.

Появился DeepTip.

Приложение осталось на экране.

Рустик коснулся дисплея, нажал на один элемент и вернулся назад. Ничего не исчезло. Попробовал другой. Часть интерфейса отзывалась, часть только занимала место и ещё ждала своей функции.

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

Android исполнял его код. Можно было нажать и получить ответ, не возвращаясь в чат за объяснением того, что должно произойти.

Рустик вышел на главный экран телефона. Среди чужих программ появилась новая иконка. Он нашёл её не сразу. Провёл вправо. Потом обратно. Увидел.

DeepTip.

Небольшой значок среди приложений, за которыми стояли банки, корпорации, сервисы доставки, карты, магазины и команды разработчиков. Рядом с ними лежало приложение, которое несколько месяцев назад начиналось с вопроса в чате.

Рустик нажал на иконку. DeepTip открылся снова.

Рустик закрыл приложение и открыл ещё раз. В третий раз задержал палец на секунду, словно телефон мог передумать. DeepTip снова показал экран.

— Три раза открылось, — написал он ChatMind.

— Хорошо. Первый запуск подтверждён.

Рустик перечитал сухую фразу. Ему хотелось более торжественных слов, но сейчас хватило этих. Подтверждение лежало у него в руке.

Он снова посмотрел на экран и вспомнил, как скачивал первые архивы DeepSight. Сообщение «Я подготовила приложение» тогда позволяло на несколько минут поверить, что основная работа закончена.

После этого слово «готово» приходилось уточнять почти каждый раз. Готов текст? Созданы файлы? Прошла сборка? Установился пакет?

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

Он разрешил себе несколько минут просто смотреть на DeepTip.

Он даже убрал iPhone в сторону. Последние месяцы тот заменял рабочий стол: чат, файлы, терминал, репозиторий, логи, скриншоты. Когда разговор начинал тормозить, приходилось открывать новый и переносить туда всё необходимое. Проект требовал, чтобы Рустик постоянно держал его части вместе.

Теперь впервые результат лежал на другом устройстве отдельно от процесса. Рустик мог выключить iPhone — и DeepTip не исчезал. Мог закрыть ChatMind — и приложение продолжало существовать. Мог забыть, в каком сообщении был последний кусок кода, — и Android всё равно открывал установленную версию.

Рустик погасил экран iPhone и ещё раз запустил DeepTip. Разговор был закрыт, а приложение работало.

Он положил телефоны рядом. Слева — ChatMind, справа — DeepTip.

Раньше почти каждую строку между ними пришлось бы переносить вручную. Теперь связанные изменения применял скрипт, CodeHub хранил их историю, BuildCloud собирал пакет. Несколько самых утомительных действий уже выполнялись без его пальцев на экране.

И всё равно между моделью и результатом оставался он.

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

На секунду Рустик снова почувствовал себя курьером между несколькими машинами. Только теперь он возил код, логи и сообщения об ошибках.

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

Как называлась эта работа, Рустик не знал. Большую часть мобильного кода он без модели не написал бы. При этом никто, кроме него, не решал, какой результат нужен и можно ли считать попытку удачной.

Он посмотрел на два телефона. С названием должности можно было подождать.

ChatMind тем временем ждала ответа.

— Есть ли системные ошибки после запуска?

Рустик вернулся к приложению, немного понажимал на элементы, переключил его в фон и вернул. Затем закрыл и открыл заново. Явной системной ошибки не было.

— Не падает.

— Тогда не меняем код до фиксации этого состояния.

— А что фиксировать? Оно же уже в CodeHub.

— Код зафиксирован. Теперь зафиксируйте результат проверки.

На основной работе он записывал результат проверки рядом с изменением. В личном проекте постоянно торопился: заработало — можно идти дальше. Через несколько исправлений уже было трудно вспомнить, какая версия запускалась и что именно в ней проверяли.

Он открыл заметку. Написал:

build-1

Устанавливается.

Запускается.

Повторный запуск работает.

Явного вылета нет.

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

Рустик дописал дату. Хотел добавить «готово», но удалил слово. Вместо него написал: «Первый запуск подтверждён».

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

Со значком на экране было особенно легко вообразить продолжение. Почти готово. Скоро можно показывать. Потом публиковать. А однажды — перестать брать вечерние заказы.

Последняя мысль появилась сама. Рустик отогнал её: один запускающийся APK не заменял ни одной курьерской смены. Сегодня снова нужно было ехать. Кредиты и счета оставались прежними.

Он убрал Android со стола, чтобы не задеть чашкой. Радость от этого никуда не исчезла. Просто рядом с ней снова поместился обычный день.

Днём Рустик несколько раз открывал DeepTip без необходимости. Иногда приложение запускалось быстро, иногда казалось, что заставка держится слишком долго. Он уже замечал то, на что утром не хватило внимания.

Отступ съедал место. Кнопка стояла неудобно. Надпись выглядела так, словно её оставили для разработчика и забыли заменить.

До установки всё это было строками, компонентами и стилями. Теперь лишний элемент занимал реальные миллиметры. Рустик закрывал DeepTip, открывал привычную программу и сразу видел разницу. Его приложение пока выглядело так, будто только училось быть приложением.

Рустик сделал скриншот. Отправил ChatMind.

— Вот так.

Модель предложила изменить размеры, отступы, подписи и порядок блоков. Один элемент убрать, другой перенести, третий сделать заметнее.

Рустик читал список и чувствовал знакомое ускорение. Вот оно. Сейчас снова начнётся.

Исправить экран, пересобрать, скачать, установить. Найти следующее неудобство. Начать заново.

Он почти написал: «Давай всё сразу». Остановился. Вспомнил вчерашнюю ночь: пять красных сборок, одну зелёную, папку build-1. И собственную фразу жене: «Сегодня — да».

— Нет, — написал он. — Сначала давай поймём, что вообще должно работать в этой версии.

Ответ задержался чуть дольше обычного.

ChatMind вернулась к минимуму: открыть приложение, перейти в основной раздел, проверить, что оно не падает. Если потребуются разрешения, запросить их в нужный момент. Функции, которым нужен второй телефон или сервер, проверять отдельно.

Рустик перечитал. Второй телефон. Вот следующая граница.

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

На одном устройстве этот путь не пройти.

Рустик посмотрел на лежащий рядом iPhone. На секунду идея казалась очевидной. Два телефона уже есть. Но проект был Android-сборкой. Даже если когда-нибудь появится версия для другой платформы, сегодня она не существовала. Значит, понадобится ещё один Android.

Потребуются Bluetooth, разрешения, поиск. То, о чём ChatMind писала абзацами, должно будет произойти между двумя предметами на столе.

Рустик почувствовал знакомую смесь интереса и раздражения. Он только дошёл до первого запуска, а уже нужно было искать другое устройство и готовить новую проверку.

На этот раз он хотя бы понимал, почему нельзя спросить у модели, работает ли весь DeepTip. Установка, запуск, обнаружение соседа и передача благодарности могли завершиться по-разному. Успех в одном месте ещё не давал ответа за остальные.

Он снова открыл заметку и под четырьмя строками написал: «Следующая проверка: два Android».

Потом удалил. Слишком рано. Сначала нужно было понять, что уже умеет первый.

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

В одном месте длинная подпись упиралась в край блока. Рустик потянулся к iPhone, затем остановился. Сначала перешёл в другой раздел и вернулся. Надпись снова обрезалась.

Он сделал скриншот и отправил его ChatMind вместе с описанием:

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

ChatMind ответила возможной причиной и предложила исправление.

Рустик уже был готов попросить скрипт с исправлением.

Потом спросил:

— Это мешает следующей проверке?

Модель ответила:

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

Рустик ещё раз проверил переход в основной раздел. Нужная кнопка оставалась доступной и нажималась. Известную проблему он пока оставил на месте и записал отдельно.

Это оказалось неожиданно трудно. Закрытая ошибка сразу давала ощущение прогресса. Сохранённая заметка — почти никакого.

Рустик сохранил заметку. build-1 остался build-1. Пересобирать приложение он пока не стал.

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

В дороге Рустик иногда вспоминал значок на Android. Маленький квадрат среди чужих приложений менял ощущение проекта сильнее, чем сотни строк кода. Теперь DeepTip можно было открыть там же, где навигатор или мессенджер.

Если дать телефон незнакомцу, тот увидит только экран. Ни месяцы переписки, ни красные сборки, ни скрипты к нему не прилагаются.

Рустик представил, как объясняет клиенту задержку подробным рассказом о шлагбауме и неправильной отметке на карте. Иногда человек понимал. Но остывший заказ от этого не становился тёплым.

Здесь будет похоже. Пользователь нажмёт кнопку и станет ждать того, ради чего её нажал. История разработки не сможет ответить вместо приложения.

Вечером Рустик снова взял Android.

Рустик нажал на иконку, затем отвлёкся на сообщение. Когда вернулся, на экране был DeepTip. Он даже не заметил, сколько длилась заставка.

Утром он следил за каждой секундой. Теперь просто ожидал, что приложение откроется.

Рустик написал ChatMind:

— Уже не страшно нажимать.

— Это хороший признак.

— Почему?

— Потому что успешный запуск перестаёт быть событием и становится ожидаемым поведением.

Рустик усмехнулся.

— А дальше что должно перестать быть событием?

— Установка новой сборки. Затем базовый сценарий. Затем связь между устройствами. В идеале пользователь вообще не должен замечать большую часть того, что сейчас требует от вас отдельной проверки.

Рустик снова посмотрел на DeepTip. Пока он знал здесь каждую неудачную попытку. За простым открытием экрана вспоминались неправильная версия зависимости, потерянный файл и настройка сборки, до которой удалось добраться только после предыдущих ошибок.

Если однажды всё заработает как следует, этого никто не заметит. Человек найдёт нужного получателя, подтвердит выбор и передаст благодарность. Рустик очень хотел дойти до такой обыденности.

На следующий день он открыл историю сборок: пять красных, одна зелёная. Затем посмотрел на Android и дописал в заметку ещё один успешный запуск.

Строк для первого устройства хватало. Нужен был другой опыт.

Он написал ChatMind:

— Хорошо. Первый запуск считаем закрытым. Что нужно минимально для проверки двух телефонов?

В ответе появились версии Android, разрешения Bluetooth, одинаковые сборки на обоих устройствах, сообщения об обнаружении и критерий успеха.

Рустик остановил её.

— Подожди. Сначала критерий.

ChatMind ответила:

— Устройство А должно увидеть устройство Б в приложении как доступного получателя. Этого достаточно для первой проверки обнаружения. Передачу пока не включаем.

Рустик перечитал. Телефон А видит телефон Б. На этот раз весь ближайший результат помещался в одну строку.

Он записал.

Раньше ему хотелось прежде всего услышать, что идею можно сделать. Теперь этого ответа было мало. Он всё чаще спрашивал, что именно нужно нажать и увидеть, чтобы считать шаг выполненным.

ChatMind могла ошибиться в коде или забыть условие из старой переписки. Рустик уже не рассчитывал устранить эту возможность одним удачным вопросом. Зато мог заметить, что ожидаемое действие не произошло, сохранить результат и вернуться с ним к модели.

На основной работе это было привычным. В DeepTip тот же навык давался труднее: слишком хотелось однажды перескочить сразу к готовому приложению.

Вечером Android лежал на столе рядом с ключами от машины. Рустик собирался на доставку.

Жена спросила:

— Опять сегодня?

Вопрос относился не к приложению.

К работе.

Он кивнул.

— Ненадолго.

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

Рустик убрал телефон с DeepTip в сторону, надел куртку и взял ключи.

Уже у двери Android завибрировал. Он разблокировал его и прочитал системное уведомление. Ничего важного. Смахнул сообщение, открыл список последних приложений и увидел DeepTip.

DeepTip ещё не помог оплатить ни одного счёта. Не был проверен на двух устройствах. Никто за пределами квартиры им не пользовался.

Рустик задержал взгляд на значке, затем погасил экран и положил Android в карман. Теперь ему предстояло сделать так, чтобы за этим значком появилось действие, нужное другому человеку.

Пока впереди был всего один следующий шаг. Второй Android.

Он вышел из квартиры. Через несколько минут приложение доставки снова показывало первый заказ. Рустик завёл машину. Телефон на креплении построил маршрут.

Обычная программа, созданная чужой большой командой, уверенно повела его по городу.

В кармане лежал Android с DeepTip — программой, которую он собирал вместе с моделью и несколькими автоматическими системами.

Пока она умела только доказать собственное существование.

Для одного дня этого было достаточно.

Первый запуск состоялся.

Теперь ему нужен был второй телефон.

Глава 7. Второй телефон

Второй Android нашёлся в ящике, где лежали провода, старые чехлы и вещи, которые было жалко выбросить, хотя никто уже не помнил, зачем они нужны.

Рустик искал его минут десять.

Жена подошла, выдвинула соседний ящик и достала телефон из-под инструкции к давно сломавшейся технике.

— Этот?

— Да. Вроде он.

— Ты же сам сюда положил.

Рустик промолчал. В последние месяцы он довольно хорошо помнил, где лежат файлы в чужой системе хранения, и всё хуже — где находятся вещи в собственной квартире.

Телефон был в потёртом чехле. У защитного стекла откололся угол, а вокруг камеры набилась пыль. Когда-то им пользовались каждый день. Потом купили другой, перенесли фотографии, переставили карту, и этот постепенно превратился в запасной. Запасной на случай, который всё никак не наступал.

Теперь наступил.

Рустик нажал кнопку питания.

Экран остался чёрным.

Он подержал кнопку дольше, словно телефон мог оценить настойчивость.

— Заряди сначала, — сказала жена.

Она ушла на кухню. Рустик остался искать провод.

Нужный разъём нашёлся у третьего кабеля. На экране появилась батарея с тонкой красной полоской.

До проверки связи между устройствами оставалось примерно сорок минут зарядки.

Рустик положил телефон на стол и открыл ChatMind на iPhone.

— Второй Android есть.

Модель попросила уточнить версию системы и подготовить оба устройства.

— Пока заряжается.

— Тогда начнём с того, какую сборку будем проверять.

Он посмотрел на первый Android. DeepTip по-прежнему открывался. Можно было перейти на нужный экран, нажать кнопку поиска, увидеть движение на дисплее. Этого хватало, чтобы вообразить следующий шаг совсем коротким: поставить ту же программу на соседний телефон и дождаться имени.

Рустик уже собирался отправить APK.

Потом вспомнил, сколько раз значение кнопки выяснялось только после её нажатия.

— В build-1 вообще есть настоящее обнаружение?

ChatMind не ответила сразу.

Попросила показать файл экрана и часть проекта, которая должна обращаться к Bluetooth.

Рустик открыл CodeHub.

На основной работе такой вопрос прозвучал бы странно. Если служба должна отправлять запрос, обычно можно найти место, где она это делает. Здесь он сам создавал проект и при этом не всегда знал, какие обещания уже превратились в действия.

Он помнил разговор об обнаружении.

Помнил название папки.

Помнил красивое объяснение, как два устройства должны находить друг друга.

Эти воспоминания складывались в ощущение, что соответствующая часть уже существует.

В файлах картина оказалась скромнее.

Был экран. Было состояние поиска. Были заготовки для получения событий. Некоторые части ещё возвращали заранее предусмотренный результат, чтобы приложение можно было собрать и открыть.

ChatMind написала:

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

Рустик откинулся на спинку стула.

Второй телефон ещё даже не включился, а эксперимент уже нашёл первую проблему.

— Почему тогда кнопка называется «Найти рядом»?

— Это элемент интерфейса для будущего сценария.

Он посмотрел на ответ и несколько секунд ничего не печатал.

Для будущего сценария.

Удобная формулировка. В ней помещалось всё, что выглядело законченным раньше времени.

Рустик хотел написать что-нибудь резкое. Спросить, сколько ещё в приложении таких элементов. Потребовать проверить весь проект сразу и убрать всё, что притворяется работой.

Но он уже представлял ответ: длинный список, новая архитектура, несколько больших файлов. К утру DeepTip мог стать аккуратнее в описании и снова перестать собираться.

Он стёр начатую фразу.

— Хорошо. Делаем только то, что нужно, чтобы один Android увидел другой. Без переводов и остальных функций.

ChatMind согласилась.

На втором телефоне загорелся логотип производителя.

Рустик поймал себя на том, что воспринимает это как поддержку.

* * *

Следующие вечера ушли на подготовку.

Со стороны она выглядела почти так же, как всё, что он делал раньше: Рустик сидел с телефоном, время от времени переходил в другую программу и иногда тихо ругался.

Внутри происходило новое.

Теперь у каждой правки появился адрес назначения — два конкретных устройства на столе. Можно было сколько угодно спорить о том, правильно ли называется функция, но вечером её предстояло вызвать и посмотреть, заметит ли соседний телефон хоть что-нибудь.

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

Один должен слушать.

Другой — сообщать о себе.

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

Рустик подумал о курьерах у стойки выдачи. Можно сколько угодно смотреть на людей в ожидании своего заказа. Если никто не произнесёт номер, ожидание само по себе ничего не решит.

Объяснение он понял.

Перенести его в работающую программу оказалось труднее.

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

На iPhone эти расстояния измерялись переключениями между окнами.

ChatMind собирала связанные изменения в скрипт. Рустик сохранял его в iSH, запускал, просматривал git diff и отправлял проверенную правку в CodeHub. Сборка начиналась уже там. Иногда он узнавал знакомые конструкции и мог проследить ход программы. Иногда приходилось открывать отдельный файл и задавать вопросы, пока непонятные строки не складывались в действие.

— Вот эта строка уже включает поиск?

— Она вызывает функцию запуска.

— А сама функция где?

Модель указывала следующий файл.

Рустик открывал его.

Он начал чаще ходить по этим переходам. Раньше название функции обычно завершало объяснение. Теперь за названием хотелось увидеть продолжение.

Обычное раздражение никуда не делось: код не помещался на экране, клавиатура закрывала место ошибки. Скрипт избавлял от переноса каждой правки вручную, но читать результат всё равно приходилось. В какой-то момент Рустик заметил, что десять минут смотрит не тот файл: после переключения в браузере осталась открыта старая вкладка.

Старый Android тем временем зарядился и требовал обновить несколько приложений.

Рустик отложил обновления.

На экране телефона осталась семейная фотография, которую он давно не видел. Дети там были меньше. Младшая ещё сидела на руках у жены, старшие щурились на солнце. Снимок оказался неожиданно близким: Рустик помнил день, в который его сделали, и никак не мог совместить это воспоминание с тем, сколько времени прошло.

Он задержался на фотографии, потом погасил экран.

Из соседней комнаты позвали помочь.

Рустик встал сразу. На этот раз сборка всё равно ещё шла.

* * *

Новый APK он назвал build-2.

Первую сборку оставил отдельно. Она умела запускаться, и этот небольшой запас уверенности хотелось сохранить.

Установка на старый телефон заняла больше времени. Система показала предупреждение, затем попросила разрешить установку. Рустик несколько раз возвращался между экраном загрузок и настройками, пока наконец не появилась знакомая кнопка.

Когда DeepTip открылся, рядом лежали уже три телефона.

iPhone с ChatMind.

Первый Android.

Второй Android.

Место, на котором обычно стояла хлебница, превратилось в испытательный стол. Провода пересекались возле сахарницы. Один телефон пришлось подпереть чехлом, чтобы видеть экран.

— Ты бы хоть подписал, — сказала жена, ставя тарелки.

— Я различаю.

Через минуту Рустик отправил модели сведения не с того устройства.

Он заметил это только после ответа.

Посмотрел на жену. Она ничего не сказала.

Рустик оторвал два маленьких кусочка бумаги и написал «А» и «Б». Положил рядом с телефонами. В тестовых настройках дал устройствам такие же короткие обозначения.

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

Оба телефона оставались на зарядке.

На обоих была одна и та же сборка.

Bluetooth включён.

Рустик написал ChatMind:

— Готово. Что нажимаем?

На Б нужно было включить доступность. На А — запустить поиск.

Он выполнил первое действие.

На экране Б появилась надпись, что устройство доступно.

Потом второе.

На А начал двигаться индикатор.

Рустик подождал.

Телефоны лежали в двадцати сантиметрах друг от друга. Между ними были только край бумажки и тень от чашки. Даже расстояние казалось нарочно подобранным так, чтобы у программы не осталось оправданий.

Прошло несколько секунд.

Потом ещё.

На экране А никого не было.

Рустик придвинул Б ближе.

Теперь чехлы соприкасались.

Ничего.

Он остановил поиск и включил снова.

Индикатор послушно начал новый круг.

— Не видит, — написал Рустик.

ChatMind попросила проверить разрешения.

Он открыл настройки.

На одном телефоне нужный пункт назывался иначе, чем на другом. Версии Android отличались. Рустик сделал скриншоты, отправил оба и дождался пояснения. Вернулся в приложение, подтвердил запрос, повторил попытку.

Экран оставался пустым.

Жена убрала со стола одну тарелку, освободив место под провод.

— Они хотя бы оба включены?

— Включены.

— А что должно появиться?

— Вот здесь второй телефон.

Она посмотрела на А, потом на Б.

— Он же вот.

Рустик рассмеялся.

Сначала тихо, потом уже по-настоящему. Вопрос был настолько точным, что спорить с ним было невозможно.

Он же вот.

Весь смысл нескольких вечеров работы сейчас упирался в необходимость доказать двум предметам очевидное для любого человека за столом.

— Ему надо самому понять, — сказал Рустик.

— Ну, пусть понимает. Только хлебницу потом верни.

Она ушла укладывать младшую.

Рустик снова посмотрел на индикатор.

Тот работал безупречно.

* * *

Через полчаса на столе изменилось многое.

Телефоны поменялись местами. Bluetooth несколько раз выключался и включался. DeepTip закрывали, открывали, убирали из последних приложений и запускали снова. Рустик успел посомневаться в старом устройстве, в новом устройстве, в разрешениях и в том, правильно ли он вообще понял последовательность действий.

Результат сохранялся один.

Никого рядом.

Он написал ChatMind длинное сообщение. В нём были сразу все попытки, несколько предположений и раздражение, которое он уже не особенно старался скрывать.

Модель ответила ещё длиннее.

Возможных причин набралось достаточно, чтобы провести за столом остаток недели.

Рустик начал читать, дошёл до предложения изменить настройки и остановился.

Он больше не помнил, какое разрешение включил до перезапуска, а какое после. Не мог уверенно сказать, оставался ли второй телефон доступным, когда первый начал очередной поиск. Один раз он проверял новую попытку, не заметив, что на другом устройстве открыл настройки и так там и остался.

Скриншотов стало больше.

Ясности — меньше.

На работе за такой рассказ он сам попросил бы коллегу начать сначала.

Рустик поставил телефоны на прежние места.

— Подожди. Давай по одному. Сейчас я уже не понимаю, что именно мы проверили.

ChatMind предложила вернуть устройства в исходное состояние и повторить последовательность.

Он открыл заметку рядом с записью о первом запуске.

build-2. А ищет. Б доступен. Оба экрана включены. Результат: пусто.

Строка была неприятной. В ней исчезали усилия, оставляя голый итог.

Рустик всё-таки сохранил её.

Потом задал вопрос:

— Откуда мы знаем, что Б действительно что-то передаёт?

ChatMind сослалась на состояние доступности.

— Это надпись на экране, — написал он. — Она откуда берётся?

Снова понадобился файл.

Рустик открыл обработчик кнопки. Часть кода он уже узнавал. После нажатия менялась переменная, вслед за ней — текст.

Дальше его понимание заканчивалось.

— Покажи, где тут ответ от Android.

ChatMind попросила ещё один фрагмент.

Он скопировал.

Потом второй.

Модель начала объяснять последовательность вызовов, но в этот раз Рустик читал медленно. Сверял названия с файлами. Возвращался назад, если не находил строку, на которую она ссылалась.

Это заняло больше времени, чем принять готовое исправление.

Зато вскоре обнаружилось, что в присланных фрагментах есть две разные истории.

В одной экран принимал нажатие и сообщал об успехе.

В другой находилась функция, которая должна была запустить передачу сигнала через Android.

Соединяющий их вызов отсутствовал.

ChatMind написала об этом коротко.

Рустик перечитал ответ.

Потом вернулся к предыдущему сообщению, где модель предлагала ещё раз проверить системные разрешения.

— То есть мы сейчас полчаса искали настройки, а кнопка до этой функции вообще не доходила?

— В текущем коде вызова из этого обработчика нет. Да, сначала нужно исправить эту связь.

Он положил iPhone экраном вниз.

Самым раздражающим оказалось то, что всё уже было рядом.

Функция существовала.

Кнопка существовала.

Разрешения обсуждали.

Сборка прошла.

ChatMind могла подробно объяснить каждую часть, и в её объяснениях целое возникало само собой. На столе целого не получалось.

Два телефона честно ждали того, чего программа ни разу не попросила сделать.

Рустик встал, налил воды и выпил, стоя у раковины.

В квартире стало тихо.

Из комнаты донёсся голос жены:

— Ты скоро?

Он посмотрел на часы.

Хотел ответить привычное «сейчас».

— Нашёл ошибку. Ещё сборку запущу и приду.

Это хотя бы можно было выполнить.

* * *

Утром Рустик открыл переписку в машине, прежде чем ехать на основную работу.

Ночной ответ ChatMind уже не казался таким обидным. В нём была конкретная недостающая связь, которую можно было исправить. Обида возникала из другого: он опять поверил, что модель держит в голове весь проект так же уверенно, как рассказывает о его отдельных частях.

Она могла собрать убедительное объяснение быстрее, чем он успевал заметить пропуск.

Это не мешало пользоваться её помощью. Но меняло вопросы, которые следовало задавать.

Рустик написал:

— Когда предлагаешь причину, говори, на чём она основана. Если в файле не видно — так и говори. Мы вчера несколько раз меняли настройки по предположению.

ChatMind согласилась и предложила добавить короткие сообщения о фактическом запуске поиска и передачи сигнала.

Рустик уже хотел попросить убрать технические надписи. На маленьком экране их и без того хватало.

Потом вспомнил вчерашнее «устройство доступно».

— Для проверки оставим. Только чтобы сообщение появлялось после ответа системы.

В разговоре возникло ещё одно маленькое правило. Рустик записал его возле описания опыта, потому что не был уверен, что в следующем длинном чате оно сохранится само.

ChatMind добавила сообщения о запуске к исправлению пропущенного вызова и подготовила короткий скрипт. Рустик применил его, просмотрел разницу и отправил новую версию в CodeHub.

Успешное нажатие на кнопку ещё ничего не говорило о выполнении команды.

На работе похожие вещи выглядели буднично. Служба могла принять задачу и не выполнить её. Запрос мог уйти и не получить ответа. Процесс мог продолжать работать, давно перестав приносить результат. Рустик умел это искать в знакомых системах.

В собственном приложении ему всё время хотелось сократить путь.

Слишком много личного стояло за каждым удачным экраном.

Он погасил телефон и завёл машину.

Новая сборка ждала вечера.

* * *

build-3 Рустик скачал на оба устройства.

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

Вечерняя проверка началась спокойнее.

А лежал слева. Б — справа.

Бумажки остались те же.

На iPhone была открыта заметка с последовательностью действий. ChatMind находилась в соседнем окне, но пока Рустик туда не переходил.

Он включил доступность на Б.

Сначала появилась промежуточная надпись. Через мгновение её сменила следующая: система подтвердила запуск передачи сигнала.

Теперь за текстом на экране стояло хотя бы одно действие за пределами самого экрана.

Рустик запустил поиск на А.

Индикатор сделал оборот.

Ещё один.

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

На третьем обороте под индикатором появилась карточка.

Небольшой прямоугольник.

Обозначение Б.

Рустик замер.

За окном проехала машина. В соседней комнате дети о чём-то спорили. Холодильник щёлкнул и загудел громче.

Карточка оставалась на месте.

Он посмотрел на второй телефон.

Потом снова на первый.

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

Рустик взял iPhone.

Набрал:

— Увидел.

Не отправил.

Посмотрел на карточку ещё раз.

— Подожди, — произнёс он вслух.

Слово было обращено к себе.

Он выключил доступность на Б.

Карточка на А осталась.

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

Ещё несколько секунд.

Карточка не исчезла.

Он стёр неотправленное сообщение.

Написал другое:

— Появился Б. Но выключаю доступность, а он остаётся в списке.

ChatMind объяснила, что найденное устройство могло сохраниться в памяти экрана. Для его удаления нужна отдельная логика: учитывать, когда сигнал был замечен последний раз, и убирать устаревшую запись.

— То есть он всё-таки увидел?

— Давайте подтвердим, что карточка пришла именно от второго телефона. Очистите результаты, оставьте доступность на Б выключенной и запустите поиск на А заново.

Рустик выполнил.

Список остался пустым.

Он подождал дольше, чем в первой попытке.

Пусто.

Включил доступность на Б.

Через несколько секунд карточка появилась.

Рустик провёл проверку ещё раз. Остановил поиск, очистил найденное, выключил Б, начал сначала.

Пустой список.

Включил Б.

Карточка.

Теперь он отправил то короткое сообщение, которое набрал минутами раньше:

— Увидел.

* * *

Жена пришла на кухню за водой.

— Посмотри, — сказал Рустик.

Она остановилась у стола.

— Вот этот ищет. А этот сообщает о себе.

Он показал оба телефона. Потом выключил доступность, очистил список и повторил опыт.

Жена смотрела на экран А.

Появилась карточка.

— Это он?

— Он.

— И что теперь?

Рустик открыл рот и замолчал.

Вопрос был естественным. Человек увидел другого человека. В приложении, которое придумано для благодарности, дальше должна происходить благодарность.

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

Он уже рассказывал его много раз.

— Теперь можно проверить обратно, — сказал Рустик. — Чтобы второй увидел первый.

Жена улыбнулась.

— Хорошо. Давай.

Она села на свободный стул и взяла Б.

Рустик сразу потянулся показать, куда нажать, но жена отодвинула телефон.

— Подожди, я прочитаю.

Он убрал руку.

Это оказалось неожиданно трудно. Нужная кнопка находилась совсем рядом с её пальцем. Он знал, как быстро пройти экран, и видел, как она тратит время на подписи, мимо которых он давно двигался автоматически.

— Мне сейчас быть доступной или искать?

— Искать. А я включу доступность.

— Тогда почему сверху написано, что я уже доступна?

Рустик посмотрел.

Они не выключили прежний режим.

Для него два состояния существовали отдельно. Для человека, впервые взявшего телефон, экран уже содержал лишний вопрос.

— Это можно оставить. Просто нажми поиск.

Она нажала.

Рустик включил доступность на А.

Они подождали.

В этот раз карточка появилась быстрее.

— Есть, — сказала жена.

Рустик посмотрел на её экран.

А.

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

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

Жена коснулась карточки.

Рустик напрягся.

Открылся следующий экран. Часть элементов ещё была заготовкой.

— А сюда уже можно?

— Пока рано. Мы сегодня только поиск проверяем.

Она вернулась назад.

— Тогда убери лишнее, пока не работает. А то хочется нажать.

Рустик взял iPhone и записал её слова.

— Ты это модели отправишь?

— Потом. Сейчас просто запомнить.

Она поставила телефон на стол и пошла к детям.

Рустик остался сидеть.

Он хотел позвать её ещё раз, объяснить, сколько всего сошлось в этой карточке. Что сигнал прошёл из одной системы в другую. Что код, который он переносил с маленького экрана, заставил два разных телефона заметить друг друга. Что ещё недавно ему присылали пустой архив, а теперь жена держала в руках работающий кусок его идеи.

Но из комнаты снова позвали.

Рустик убрал телефоны подальше от края и пошёл помочь.

* * *

Позже они проверили исчезновение.

Старую запись Рустик теперь замечал сразу. Стоило выключить доступность, как буква Б на соседнем экране начинала раздражать. Телефона больше не должно быть среди тех, кому можно что-то отправить, а программа продолжала его показывать.

Он представил курьера, который уже вышел из подъезда.

Клиент задержался, открыл DeepTip и увидел знакомую карточку. Нажал, рассчитывая, что человек ещё рядом.

Вчера Рустик обрадовался бы любому имени в списке.

Теперь значение имело, почему имя там находится и когда должно исчезнуть.

ChatMind предложила хранить время последнего обнаружения и удалять запись после некоторого ожидания. Сигнал не приходит непрерывной линией. Если убирать человека при любой короткой паузе, список начнёт мигать. Если держать слишком долго, экран будет показывать прошлое.

Рустик спросил, какое время правильно.

Модель назвала ориентир для начала проверки.

— Это мы уже знаем или пока выбираем?

— Пока выбираем. Подходящее значение нужно проверять на устройствах.

Такой ответ ему понравился.

Он не делал задачу проще, зато оставлял место для того, что телефоны покажут вечером.

Рустик применил следующий скрипт, проверил изменения и отправил их в CodeHub. Он уже не пытался вместить весь путь в оставшиеся перед сном минуты. Запустил проверку кода и сборку, убрал со стола провода и лёг.

На следующий вечер установил build-4.

Сначала повторил вчерашнее обнаружение.

Б появился.

Рустик выключил на нём доступность.

Карточка осталась.

Он ждал, глядя на часы.

Через короткую паузу список очистился.

Рустик выдохнул.

Пустой экран впервые оказался хорошим результатом.

Он включил доступность обратно.

Карточка вернулась.

Повторил ещё раз и записал, сколько приблизительно занимали появление и исчезновение. Цифры немного различались. Раньше это насторожило бы его сильнее. Теперь он уже видел, что у этого небольшого мира есть собственное время, которое не обязано совпадать с моментом нажатия.

Затем он попросил старшего ребёнка отнести Б в соседнюю комнату.

— Только экран пока не выключай.

— А что делать?

— Просто подержи.

Ребёнок посмотрел на телефон, потом на отца.

— И всё?

— Пока всё.

— Я думал, там игра.

Рустик улыбнулся.

— До игры мы ещё не дошли.

Из соседней комнаты донеслось:

— Здесь стоять?

— Да.

Б оставался в списке.

Между телефонами теперь была стена, дверной проём и несколько метров квартиры. Рустик понимал, что это ничего не говорит о людном ресторане или подъезде. Но у двух устройств на столе впервые появилось продолжение в пространстве.

Он встал и пошёл за ребёнком, держа А перед собой.

В дверях они едва не столкнулись.

— Ты же сказал стоять.

— Всё, спасибо. Давай сюда.

Ребёнок вернул телефон и побежал по своим делам, для которых обнаружение устройства за стеной не имело никакой ценности.

Рустик принёс Б обратно на кухню.

На столе оставалась вмятина в бумажке с буквой. На чехле — след от пальца. Он поправил провод и поймал себя на том, что улыбается.

У DeepTip появилась первая проверка с помощником, который очень хотел поскорее освободиться.

* * *

Потом Рустик сделал то, чего пока делать не собирался.

Нажал боковую кнопку на Б.

Экран погас.

Он продолжал смотреть на А.

Сначала ничего не изменилось. Карточка стояла на прежнем месте, индикатор поиска двигался. Рустик уже потянулся к заметке, чтобы записать ещё один успешный случай.

Но через некоторое время Б исчез.

Телефон лежал рядом.

Bluetooth оставался включённым.

DeepTip никто не закрывал.

Рустик разбудил экран.

Карточка вернулась после возобновления работы приложения.

Он повторил.

На этот раз следил сразу за обоими устройствами и часами. Сначала экран Б темнел. Затем проходила пауза. Потом на А исчезала запись.

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

Приложение не сохраняло доступность так, как он ожидал, когда второй телефон переставали держать открытым.

Рустик отправил ChatMind последовательность.

Ответ был осторожнее прежних.

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

Рустик прочитал и опустил телефон.

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

Получатель благодарности вряд ли будет стоять с открытым DeepTip и следить, чтобы ничего не уснуло.

До этой минуты два включённых экрана казались естественным условием опыта.

Теперь стало ясно, насколько тщательно он помогал собственному приложению работать.

Выбрал место.

Подключил зарядки.

Держал нужные разделы открытыми.

Сам нажимал кнопки в правильном порядке.

Никто не звонил. Никто не уходил. Ни одному из телефонов не нужно было заниматься ничем ещё.

На столе у DeepTip были прекрасные условия.

У людей таких условий обычно не бывает.

ChatMind уже предложила следующий разбор.

Рустик посмотрел на время. Потом на курьерское приложение, уведомление от которого появилось на iPhone.

Сегодня ему нужно было выехать.

Он написал:

— Пока не меняем. Запиши проблему отдельно. То, что сейчас работает с открытыми экранами, сначала сохраним.

Модель помогла коротко описать состояние.

Рустик прочитал описание и внёс его рядом с версией проекта.

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

Он перечитал последний пункт.

Оставил.

За эти несколько вечеров идея приблизилась к реальности, но деньги всё ещё находились далеко от кухни с двумя телефонами.

Это не отменяло сделанного.

Просто помогало правильно закрыть вечер.

* * *

Рустик убрал бумаги с буквами в чехлы.

Так они хотя бы не потеряются до следующей проверки.

Провода свернул. Хлебницу вернул на место. Старый Android оставил заряжаться там, где на него не могли поставить мокрую чашку.

Жена стояла у двери в комнату.

— Ну что?

— Видят друг друга.

Она кивнула.

— Хорошо.

Рустик ждал ещё какого-то вопроса. Может быть, о том, что дальше. О том, скоро ли всё будет готово. О деньгах, ради которых он когда-то открыл первый разговор с моделью.

Но жена спросила:

— Завтра ты сможешь вечером побыть дома?

Он посмотрел на ключи в руке.

Завтра можно было снова сесть за обнаружение. Разобрать блокировку экрана. Запустить новую сборку. Попробовать ещё несколько настроек, пока два телефона стоят рядом и он помнит все подробности.

Подробности уже лежали в заметке.

— Смогу, — сказал Рустик.

— Тогда я записалась по своим делам. Хорошо?

— Да.

Он обулся, взял сумку и вышел.

В машине несколько секунд сидел без света. Потом поставил iPhone в держатель и открыл доставку.

Город на карте был полон цветных участков. Где-то предлагали больше заказов, где-то добавляли небольшую доплату. Приложение быстро определило его положение и предложило начать смену.

Рустик нажал.

Первый заказ пришёл почти сразу.

У ресторана пришлось ждать. Он припарковался, сообщил, что прибыл, и остался возле стойки. Рядом стоял другой курьер. На экране его телефона что-то двигалось; затем человек нажал боковую кнопку и убрал устройство в карман, чтобы взять готовый пакет.

Рустик проследил за этим движением.

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

Он достал свой телефон и открыл заметку DeepTip.

Под записью о следующей проверке написал:

Телефон должен оказаться в кармане.

Посмотрел на фразу и сохранил.

Из-за стойки назвали номер его заказа.

Рустик убрал iPhone, взял пакеты и пошёл к выходу.

Дома, на двух разных устройствах, одна и та же программа уже умела находить соседа.

Следующий опыт должен был начаться с погасшего экрана.

Глава 8. Телефон в кармане

— Ты хлеб купил?

Рустик поднял пакет.

— Купил.

— И молоко?

Он показал вторую сторону пакета.

Жена стояла в прихожей уже в куртке. Одной рукой придерживала волосы, другой искала что-то на полке. Рустик снял с крючка её ключи и протянул.

— Спасибо. Я постараюсь не очень поздно.

— Не торопись.

Она посмотрела на него, словно проверяя, правильно ли расслышала. Обычно эту фразу произносил кто-нибудь другой.

Из комнаты позвала младшая. Старшая сразу сообщила, что ничего не делала. Сын сказал, что он вообще сидит далеко.

— Разберёмся, — сказал Рустик.

Жена улыбнулась, быстро поцеловала его и вышла. Дверь закрылась.

Несколько секунд он стоял с продуктами в руках и слушал квартиру. Вода шумела в ванной. В комнате что-то упало, потом кто-то засмеялся. На кухне негромко гудел холодильник.

В это время Рустик обычно уже смотрел, куда выгоднее выехать. Где меньше пробок, откуда проще забрать заказ, стоит ли ехать в район, который приложение закрасило особенно привлекательным цветом.

Сегодня перед ним лежал другой маршрут: убрать продукты, накормить детей, найти потерявшуюся вещь, потом, скорее всего, ещё одну.

Он прошёл на кухню и поставил пакет на стол.

Младшая пришла следом. В руках у неё была коробка с лото.

— Будешь?

— После ужина.

Она поставила коробку рядом с хлебом и не ушла.

— Здесь оставлю.

— Оставляй.

Рустик достал молоко. Коробка стояла так близко, что убрать её можно было только отдельным, хорошо заметным движением.

Он оставил её на месте.

* * *

Пока закипала вода для макарон, сын вошёл на кухню и увидел у отца три телефона.

— Тебе уже мало одного?

— Одного точно мало.

— А трёх хватит?

Рустик задумался.

— Надеюсь.

Сын взял старый Android, повертел, посмотрел на отколотый угол защитного стекла. Из-под прозрачной части чехла выглядывала бумажка с буквой Б.

— Это который надо было держать и не выключать?

— Этот. Теперь можно выключать.

— Совсем?

— Экран.

— А ты проверял?

— Немного. Ещё посмотрим.

В обед Рустик успел вернуться к вчерашнему опыту. Он написал ChatMind, как курьер убрал телефон в карман, чтобы взять заказ, и спросил, можно ли сделать так же с DeepTip.

Модель предложила начать с короткого режима видимости. Человек сам включает его на время встречи, а телефон продолжает сообщать о себе, пока экран погашен. Постоянное ожидание всех вокруг можно было пока отложить.

Рустик сразу представил этот жест. Достать телефон на несколько секунд, дать понять, что готов принять благодарность, убрать обратно. Руки снова свободны.

— На сколько включать? — спросила ChatMind.

— Пока на две минуты. Чтобы успеть попробовать.

К вечеру у него был новый вариант. Модель помогла изменить поведение приложения, а Рустик проверил его на своих аппаратах. Один экран погас, второй продолжал видеть соседа. Когда время закончилось, запись исчезла.

Он хотел посмотреть ещё раз, но дома его ждала жена с ключами и младшая с коробкой.

— Дай сюда, — сказал он сыну. — После ужина покажу.

Сын положил Android на стол.

— У тебя всё после ужина.

— Потому что ужин раньше.

Рустик поставил тарелки. Старшая пришла с ложками, младшая принесла себе самую большую салфетку и объяснила, что это скатерть.

Они сели.

Разговор сразу пошёл в три стороны. Сын рассказывал о чём-то, чего отец сначала не понял, потому что история началась с середины. Старшая ждала паузы, чтобы поправить его, хотя при описанном событии не присутствовала. Младшая требовала признать, что большая салфетка действительно лучше обычной.

Рустик слушал и время от времени задавал вопрос, после которого выяснялось, что он всё-таки перепутал участников.

— Нет, пап. Это другой.

— Подожди. Тогда кто первый сказал?

Сын начал сначала.

Телефон рядом с хлебницей коротко завибрировал. Рустик машинально повернул голову, но сообщение закрылось раньше, чем он прочитал его целиком.

— Так кто первый сказал? — повторил он.

Сын продолжил.

* * *

У лото оказались правила, которых Рустик не помнил.

Младшая могла поменять карточку, если на ней долго ничего не закрывалось. Старшая считала это несправедливым, но оставляла за собой право один раз заглянуть в мешок. Сын требовал играть нормально и при этом сразу взял две карточки.

— Тогда и я две, — сказал Рустик.

— Ты взрослый.

— Это мешает?

— Тебе должно быть сложнее.

Он получил одну карточку.

Бочонки пахли деревом и коробкой. Несколько катились по столу, и младшая каждый раз ловила их обеими ладонями. Когда нужное число оказывалось у неё, она объявляла это раньше, чем ведущий успевал договорить.

Рустик проиграл первую партию. Во второй решил играть внимательнее и проиграл ещё быстрее.

— У тебя ничего не закрывается, — заметила старшая.

— Вижу.

— Можешь поменять карточку.

— Мне можно?

Она подумала.

— Ты же первый раз сегодня играешь.

Рустик посмотрел на дочь. Потом обменял карточку на ту, которую она ему протянула.

Телефон опять подал голос. На этот раз сын успел увидеть, как отец к нему потянулся.

— Это твоё приложение?

— Нет. Обычное сообщение.

— А твоё умеет присылать?

— Пока нет.

— А что умеет?

Рустик открыл рот и остановился. Он мог рассказать о поиске, видимости, времени ожидания. Сын смотрел на него поверх карточки, ожидая короткого ответа.

— Находить второй телефон рядом.

— И всё?

— Пока всё.

Младшая закрыла ладонью половину своей карточки.

— У меня много.

Рустик засмеялся и убрал iPhone в прихожую, на полку возле ключей. Когда вернулся, ему сообщили, что он пропустил число.

— Моё было?

— Теперь уже нет, — сказал сын.

* * *

После игры Рустик убирал со стола, а дети вспоминали обещанное приложение.

— Ты сказал, покажешь, — напомнила старшая.

— Сейчас.

Он принёс оба Android. На первом открыл поиск, на втором включил короткую видимость.

— Этот ищет. Этот можно найти.

Сын взял второй аппарат.

— А если я уйду?

— Далеко — перестанет находиться.

— Насколько далеко?

— Вот это ещё будем узнавать. Пока по квартире.

Сын нажал боковую кнопку. Экран погас. Рустик удержался от желания немедленно забрать телефон и убедиться, что всё включено правильно.

На его экране оставалась карточка Б.

Сын ушёл в комнату. Вернулся. Ушёл в прихожую.

— Не подглядывай.

— Я и не смотрю.

Старшая встала рядом с отцом. Младшая забралась на стул и тоже уставилась в дисплей, хотя пока не очень понимала, чего они ждут.

Б оставался рядом.

Через несколько секунд сын вышел из прихожей с пустыми руками.

— Ну, найди.

Рустик посмотрел на экран, затем на сына.

— Оно не показывает, куда ты его положил.

— Но видит?

— Видит.

— Тогда ищи.

— Это не совсем такая игра.

Младшая уже побежала проверять диван.

Старшая заглянула под стол. Рустик подошёл к сыну, который слишком старательно стоял неподвижно, и ощупал карман его кофты.

Пусто.

— Не там, — довольно сказал сын.

Телефон нашёлся в кармане куртки Рустика. Сын успел положить его туда и вернуться на кухню.

Старшая засмеялась первой.

— Пап, у тебя куртка рядом.

Рустик посмотрел на карточку Б. На крючке действительно висела его куртка, и всё, что приложение знало об этой небольшой компании, сейчас находилось в её правом кармане.

Он достал Android.

— Куртке ничего отправлять не будем.

— Почему? — спросила младшая.

— Она ответить не сможет.

— Ты можешь за неё.

— Тогда это уже мне.

Пока они спорили, отведённое время закончилось. Карточка исчезла. Рустик включил видимость снова и вернул аппарат сыну.

— Теперь просто положи себе в карман. Не прячь от всех.

Сын убрал телефон и сел на диван. Взял пульт. Старшая ушла за своей вещью, младшая осталась рядом с отцом и спросила, скоро ли на экране появится что-нибудь красивое.

Рустик посмотрел на сына. Тот уже забыл об опыте и выбирал, что включить. Чёрный телефон лежал у него в кармане, а Б оставался в списке.

— Скоро, — ответил Рустик младшей.

На этот раз ему не понадобилось просить помощника держать экран открытым и стоять на месте.

* * *

Позже, когда младшая уже переоделась ко сну, сын задержался на кухне.

— А кто тебе это делает?

Рустик убирал чашки в сушилку.

— Мы с моделью. Помнишь, я показывал чат?

— Которая на вопросы отвечает?

— Да.

— Она и приложения умеет?

— Помогает делать. Я рассказываю, что хочу получить. Она пишет код, объясняет то, чего я не знаю. Потом смотрим, что получилось.

Сын взял старый Android, который снова лежал у хлебницы.

— А сам бы смог?

— Без неё — нет. Я раньше такого не делал.

Рустик сказал это легко. На работе он знал множество вещей, которых сын никогда не видел: как найти причину сбоя, восстановить службу, понять чужой скрипт. Но раньше между этими знаниями и собственным мобильным приложением оставалось расстояние, на которое он даже не решался всерьёз посмотреть.

Теперь на другом конце этого расстояния лежал телефон в потёртом чехле.

Сын провёл пальцем по треснувшему уголку стекла.

— Получается, ты учишься?

— Получается.

— А зачем сразу такое сложное?

Рустик задумался. Можно было ответить про деньги, про работу, про то, как хотелось однажды самому выбирать вечера. Но сын пока спрашивал только о приложении.

— Потому что мне захотелось именно такое.

Сын кивнул, будто этого было достаточно.

— Покажешь, когда оно уже что-нибудь отправит?

— Покажу.

Он ушёл в комнату. Рустик вытер руки и посмотрел на два Android у хлебницы.

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

* * *

Жена вернулась, когда дети уже укладывались. В прихожей зашуршала куртка, затем она тихо вошла на кухню.

— Как вы тут?

— Я проиграл всё.

Она посмотрела на коробку с лото.

— Там невозможно выиграть. Они правила меняют.

— Я поздно понял.

Жена улыбнулась и села. Рустик поставил перед ней чашку. Несколько минут они говорили о её вечере, потом о завтрашних делах. Он вспомнил про iPhone в прихожей только тогда, когда понадобилось посмотреть время.

На экране было несколько уведомлений. Одно от доставки предлагало выйти в район, где сейчас особенно нужны курьеры.

Рустик смахнул его.

— А это ты сегодня проверял? — жена кивнула на старый Android.

— С детьми. Они решили, что это поиск спрятанного телефона.

— По крайней мере, понятно, зачем нажимать.

— А у меня непонятно?

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

— На что?

— На «спасибо». Ты с этого начинал.

Рустик подвинул к себе аппарат.

— Да.

— Тогда сделай, чтобы я могла его кому-нибудь отправить. Хоть тебе.

Он открыл DeepTip. На экране были знакомые элементы, над которыми успел провести столько времени, что уже замечал их расположение раньше смысла.

Список устройств работал. Чёрный экран больше не обрывал короткий опыт. Но слово, ради которого всё затевалось, пока существовало главным образом в разговорах.

Жена допила чай и встала.

— Я в душ.

— Сейчас только запишу.

— Запиши.

Рустик взял iPhone и написал ChatMind:

— Хочу уже попробовать само спасибо. Без суммы, без кошелька. Один человек выбирает другого, оба понимают, что выбрали правильно, и на втором телефоне появляется знак. Поможешь?

Ответ пришёл быстро.

— Да. Что получатель должен увидеть первым?

Рустик посмотрел на дверь, за которой шумела вода.

— Что это обращаются к нему. А потом само слово.

ChatMind предложила использовать знакомый им вариант с совпадающим знаком на двух экранах. Получатель подтверждает, что готов принять обращение. Отправитель видит, кому отправляет. Для первой попытки можно оставить обычное «Спасибо» и одну запись о получении.

Рустик перечитал ответ. Представил телефоны в двух руках. Один коротко откликается, человек смотрит на него, поднимает глаза.

— Давай так.

Он отправил сообщение, положил iPhone на зарядку и пошёл убирать коробку с лото. Под ней обнаружился бочонок, который они искали всю последнюю партию.

Рустик перевернул его.

Число было с его карточки.

* * *

Следующие несколько дней работа стала для него заметно яснее.

ChatMind помогала добавить короткую передачу, Рустик пробовал её на двух аппаратах. Иногда спрашивал, почему получатель видит одно, а отправитель другое. Модель объясняла, они меняли нужное место и возвращались к тому, что должен увидеть человек.

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

Он ещё не умел самостоятельно сделать всё, что возникало на дисплее. Зато мог разглядеть в результате собственный замысел и объяснить, где тот потерялся. ChatMind помогала придать ему форму, с которой можно было что-то делать.

В один из вечеров Рустик остановился у двери комнаты.

— Можешь на минуту?

Жена подняла глаза.

Он показал два телефона.

— Теперь не просто смотреть друг на друга.

— Телефонам или нам?

— Для начала телефонам.

Она отложила своё дело и прошла с ним на кухню.

Рустик передал ей старый Android. Она включила видимость. Он нашёл её аппарат и выбрал карточку.

На обоих экранах появился синий круг с коротким кодом.

— У тебя такой же?

Она повернула телефон к нему.

Рустик кивнул. Жена подтвердила, что обращаются к ней. Он нажал кнопку.

Старый аппарат коротко дрогнул в её ладони.

На экране появилось:

Спасибо.

Жена посмотрела на слово. Потом на Рустика.

— Это мне?

— Тебе.

— За что?

Он не готовился к этому вопросу. Хотел только показать, что знак дошёл, что она увидит его на своём экране. Теперь нужно было ответить человеку, которому он сам только что отправил благодарность.

— За телефон, — сказал он. — Который ты нашла в ящике. Я бы ещё долго искал.

Жена рассмеялась.

— Я могу так много заработать.

— Денег там пока нет.

— Я заметила.

Она посмотрела на экран ещё раз, затем нажала назад и нашла короткую запись о получении.

— А обратно можно?

Рустик поменял роли телефонов. Теперь видимость включил он.

Жена сама выбрала нужную карточку. Дождалась совпадающего знака, посмотрела на его экран и нажала.

Телефон Рустика отозвался в руке.

Спасибо.

— За макароны, — сказала жена. — Дети велели передать.

Он улыбнулся, продолжая смотреть на слово.

До этого Рустик видел эту надпись много раз. В ответах ChatMind, в описании будущего экрана, на собственных пробных запусках. Но сейчас кнопку нажала жена, и фраза относилась к ужину, который он действительно приготовил.

Жена вернула старый Android на стол.

— Вот теперь я поняла, что ты мне показываешь.

Из комнаты позвала младшая. Жена пошла к ней, а Рустик остался с двумя телефонами.

На одном хранилось отправленное спасибо. На другом — полученное.

Он некоторое время не трогал экраны.

Потом снял с чехла бумажку с буквой А. Клей оставил небольшой прямоугольный след. Рустик потёр его большим пальцем и положил аппарат рядом с ключами.

* * *

Вечером следующего дня у ресторана другой курьер придержал ему дверь.

Рустик нёс два пакета и стаканы. Дверь с тяжёлым доводчиком уже начала возвращаться, но парень успел подставить плечо.

— Проходи.

— Спасибо.

На улице Рустик поставил заказ в сумку и вернулся внутрь: второй заказ ещё готовили. Парень остался возле стойки, смотрел в телефон. Под подошвами темнели мокрые следы. Из кухни пахло жареным луком.

Рустик прислонился к стене рядом.

— Тоже ждёшь?

— Сказали, сейчас.

Они посмотрели друг на друга и усмехнулись.

Из-за стойки снова прозвучало «сейчас», но уже для кого-то другого.

Рустик коснулся кармана, где лежал старый Android. Он взял оба аппарата, чтобы показать приложение кому-нибудь, если представится случай. Сейчас, когда случай представился, доставать их стало неловко.

— Слушай, можно одну штуку покажу?

Парень отвёл взгляд от телефона.

— Какую?

— Приложение делаю. Хочу, чтобы можно было поблагодарить человека рядом. Без номера телефона и карты.

Курьер посмотрел на его сумку.

— Ты?

— Я. С моделью искусственного интеллекта.

— В смысле, с тем чатом?

— Ну да.

Рустик достал оба Android. Со стороны это, наверное, выглядело странно: человек в куртке, с курьерской сумкой, предлагает возле выдачи подержать старый телефон.

Парень взглянул на второй аппарат.

— На мой ничего ставить не надо?

— Для этой проверки — нет. Это оба мои. Просто возьми один.

Тот убрал собственный телефон и взял предложенный.

Рустик показал кнопку видимости. Парень нажал, прочитал появившуюся строку и машинально погасил экран.

Несколько дней назад Рустик остановил бы его на этом движении.

Теперь открыл поиск.

Нужная карточка появилась. Рустик выбрал её. Старый Android коротко откликнулся, курьер снова поднял экран.

— Что тут?

— Посмотри на знак. У меня такой же.

Парень сравнил экраны и подтвердил.

Рустик нажал «Поблагодарить».

— Это за дверь, — сказал он.

Курьер прочитал появившееся слово. На лице сначала было выражение человека, который ждёт ещё чего-то. Потом он улыбнулся.

— Пожалуйста.

Обычный ответ прозвучал неожиданно хорошо.

Рустик тоже улыбнулся. В ресторане продолжали выкрикивать номера, за спиной кто-то разговаривал с поддержкой, дверь снова открылась и впустила холодный воздух. Человек напротив держал его телефон и только что получил знак, который ещё недавно существовал в нескольких сообщениях ChatMind.

— Тут пока просто спасибо, — сказал Рустик. — Без денег.

— Понял. С деньгами было бы ещё приятнее.

— Мне тоже.

Парень нажал назад. Ещё раз открыл запись. Затем посмотрел на свой телефон, который лежал у стойки.

— А если на моём?

— Нужно поставить приложение. И у меня, и у тебя.

— То есть если у человека его нет, он не появится?

— Нет.

Курьер кивнул. Никакого разочарования в этом движении не было; он просто выяснял, как устроена незнакомая вещь.

— Ну, покажешь, когда доделаешь. Мы тут всё равно постоянно стоим.

Из кухни назвали его номер.

Он вернул Android, забрал пакет и уже у двери обернулся:

— Слушай, а как оно называется?

— DeepTip.

— Диптип, — повторил парень. — Ладно. Удачи.

Дверь закрылась.

Рустик убрал старый аппарат в карман. На другом ещё оставалась запись об отправленном знаке.

Через несколько минут выдали и второй заказ Рустика. Он отнёс его в машину, поставил сумку на сиденье и написал ChatMind:

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

Потом добавил:

— И можно ли поставить на его телефон.

Прежде чем построить маршрут, он успел прочитать ответ. Модель помогла ему наметить короткое приглашение для первого пробного пользователя. Рустик сохранил его, но отправлять пока было некому: они с курьером не обменялись контактами.

Он посмотрел на светящиеся окна ресторана.

Теперь ему хотелось вернуться сюда ещё раз.

Вечер оставался рабочим. Заказы ждали в сумке, на обратной дороге нужно было купить хлеб. Рустик поставил iPhone в держатель и включил навигатор.

Два Android лежали в карманах. Экраны у обоих были погашены.

На одном сохранилось спасибо за открытую дверь.

На другом — за макароны.

Глава 9. Без подсказки

— Диптип!

Рустик обернулся.

У стойки стоял тот самый курьер. В одной руке телефон, в другой перчатка. Он поднял перчатку, будто они были знакомы гораздо дольше одного разговора.

— Ну что, доделал?

Рустик улыбнулся. До этого название приложения произносили в основном дома. Здесь, среди запаха жареного масла и голосов, оно прозвучало непривычно громко.

— Смотря что считать готовым.

— На мой поставить можно?

Вот эту часть ответа Рустик знал.

— Можно попробовать.

Он несколько раз заезжал в этот ресторан после их первой встречи. Забирал заказы, оглядывался и уезжал. Один раз увидел похожую куртку со спины, но человек повернулся, и Рустик сделал вид, что смотрел на табло.

Теперь нужный человек стоял перед ним и сам просил приложение.

Из кухни сообщили, что заказ будет через пять минут.

Курьер посмотрел на Рустика.

— Успеем. Тут пять минут длинные.

Рустик достал iPhone, потом один Android. Второй остался в кармане.

— У тебя тоже Android?

— Ага.

— В магазине его пока нет. Это пробный вариант. Я тебе пришлю.

— Давай.

Они открыли переписку и обменялись именами.

Курьера звали Денис.

Рустик вдруг понял, что несколько дней мысленно называл его человеком, который придержал дверь. Денис наверняка называл его примерно так же — только вместо двери были три телефона.

Пришлось назвать себя вслух.

* * *

Установка заняла меньше времени, чем ожидание заказа.

Пока Денис занимался своим телефоном, Рустик рассказывал, что уже получается, а что они ещё проверяют. После третьей фразы он услышал себя со стороны и замолчал.

Денис даже не заметил паузы. Он читал экран.

На его телефоне DeepTip выглядел немного иначе: буквы крупнее, свободного места меньше. Рустик знал каждую кнопку, но сейчас ему хотелось посмотреть на них поближе, будто кто-то успел переставить вещи в знакомой комнате.

— Так. Я хочу отправить, — сказал Денис.

— Тогда здесь нужно…

— Подожди. Я сам.

Рустик закрыл рот.

Денис нажал на поиск. На экране появилась единственная карточка — Android Рустика, на котором тот заранее включил видимость.

Палец Дениса задержался над ней.

— Это ты?

Рустик кивнул.

— Сейчас будет знак, надо…

Денис поднял глаза.

— Ты можешь минуту помолчать?

— Могу.

Он сунул свободную руку в карман.

На экранах появился одинаковый круг и код. Денис посмотрел на телефон Рустика, затем на свой. Дождался подтверждения и нажал кнопку.

Android в руке Рустика коротко дрогнул.

Спасибо.

— Пришло?

Рустик повернул экран.

— Пришло.

— Ну вот.

Денис вернулся назад, ещё раз открыл историю и посмотрел на запись. Рустик следил за его пальцем. Каждый раз, когда тот выбирал что-то сам, хотелось объяснить, почему именно там находится кнопка и что за ней должно быть.

Он удержался.

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

Денис снова открыл поиск.

— А у кого-нибудь ещё здесь есть?

— Приложение?

Он кивнул на двух курьеров у двери.

— Ну да. Может, ты уже кому-нибудь поставил.

— Пока нет. Только тебе.

Денис посмотрел на свой экран. Потом на людей у двери.

— То есть пока только ты.

— Пока да.

Рустик сразу захотел добавить, что потом всё будет иначе. Представил несколько карточек, знакомые профессии, фотографии, людей, которых можно выбрать одним движением. Слова уже собирались в привычную длинную фразу.

Но Денис убрал DeepTip и открыл доставку.

— Мне ещё второй заказ добавили. Теперь точно постоим.

Он отошёл к стойке узнавать, что с новым заказом.

Рустик остался со своим Android. На нём лежало только что полученное спасибо, а человек, который его отправил, уже занимался другим делом.

* * *

У девушки на выдаче дрожала наклейка на пальце.

Она пыталась приклеить её к пакету, одновременно отвечала на вопрос из кухни и слушала мужчину, который показывал ей время на экране. Наклейка цеплялась за перчатку, пакет проваливался внутрь самого себя, мужчина повторял вопрос.

— Я вижу, что вы ждёте, — сказала она.

— А я вижу, что время идёт.

Рустик отвёл взгляд. Фраза была знакомая. Ему самому не раз хотелось произнести её ровно таким тоном.

Девушка поставила пакет на стойку и назвала номер Дениса.

Тот сверил экран, забрал заказ и пошёл к двери.

— Подождите! — позвала она. — У вас ещё напитки.

Денис остановился.

Девушка достала из-под стойки второй пакет. Заглянула внутрь, что-то проверила и подвинула к нему.

— Вот теперь всё.

— Точно. Спасибо, — сказал Денис.

Он вернулся к стене, поставил оба пакета в сумку и застегнул молнию. Второй его заказ ещё готовили.

— Хорошо, окликнула, — сказал он Рустику. — Я бы уехал без этого. Потом выясняй, где потерял.

Рустик посмотрел на девушку. Она уже приняла следующий вопрос и тянулась за новой наклейкой.

Денис поймал его взгляд.

— Ей бы отправить.

Он достал телефон и открыл DeepTip.

Рустик увидел знакомый экран и почувствовал, как что-то быстро, почти болезненно поднимается в груди. Денису никто не напоминал о приложении. Он сам вспомнил, зачем оно могло понадобиться.

В списке снова был только один телефон.

Денис посмотрел на стойку.

— У неё, конечно, нет.

— Нет.

— И поставить сейчас некогда.

Рустик коснулся второго Android в кармане. Можно было подойти, объяснить, дать его подержать. На это нужно совсем немного времени.

Девушка повернулась к кухне и громко повторила номер заказа. Мужчина напротив неё начал говорить прежде, чем она закончила.

Рустик убрал руку из кармана.

— Сейчас некогда, — согласился он.

Денис погасил экран.

Потом, когда девушка на секунду осталась без вопроса перед лицом, поднял руку.

— Спасибо за пакет. Вы меня выручили.

Она посмотрела на него, кивнула и улыбнулась краем рта.

— Не за что.

Денис снова прислонился к стене.

Рустик стоял рядом. Ему хотелось продолжить разговор про приложение, но подходящих слов сразу не нашлось.

* * *

— Ты поэтому его придумал? — спросил Денис.

— Из-за чаевых вообще. Чтобы человеку рядом можно было отправить. Без номера и всего остального.

— Мне вчера женщина написала, что спасибо переведёт. Потом спросила, куда. Я уже следующий заказ вёз. Ответил, когда остановился, — она прочитала и всё.

Он пожал плечами.

— Может, отвлеклась. Может, передумала. Я же не буду напоминать.

Рустик кивнул.

Он слишком хорошо понимал последнюю фразу. В ней было то неловкое место, которое хотелось убрать с самого начала: благодарность уже прозвучала, а за продолжением приходилось идти самому.

Денис провёл пальцем по мокрому следу на своей сумке.

— А здесь как будет? Я нажал — и человеку деньги пришли?

— Так хочу сделать.

— По двадцать рублей, например.

— Почему по двадцать?

— Ну а сколько за пакет? Не тысячу же. Просто спасибо. Сотню я бы уже подумал, а двадцать — почему нет.

Он сказал это так буднично, что Рустик на мгновение перестал слышать кухню.

О суммах они с ChatMind говорили уже не раз. Маленькие переводы, удобный выбор, короткое действие. Там всё выглядело правильно.

Теперь двадцать рублей возникли у стойки ресторана. Денис примерял их к своему карману, к чужой помощи и к нескольким секундам перед уходом.

— Пока можно только слово, — сказал Рустик.

— Я понял.

— Деньги сложнее.

— Наверняка.

Денис не торопил его. От этого стало легче.

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

Денис слушал, поглядывая на кухню.

— А положить туда как?

— Это ещё надо решить.

— И забрать?

— Тоже.

Денис кивнул.

— Ладно. Когда можно будет, напиши. Я попробую.

Рустик открыл их новую переписку и убедился, что она на месте.

— Теперь хоть есть куда написать.

Денис усмехнулся.

— А то ходил бы здесь с тремя телефонами.

Назвали номер Рустика.

Он убрал Android, подошёл к стойке и показал заказ. Девушка подняла пакет. Рустик проверил состав, спросил про напитки и получил второй.

Когда он застёгивал сумку, Денис уже снова говорил с кухней. Их разговор закончился без особого момента, почти посреди фразы.

На улице моросило. Рустик донёс сумку до машины, поставил на сиденье и вытер руки о джинсы.

Ему хотелось сразу рассказать ChatMind всё.

Он успел написать только:

— Поставили на чужой телефон. Он сам разобрался и отправил.

Потом включил маршрут.

Ответ прочитает на следующей остановке.

* * *

У первого клиента не открывался домофон.

Рустик нажал ещё раз, подождал и позвонил по номеру из заказа. Мужчина ответил, что сейчас спустится.

Рустик стоял под козырьком, прикрывая пакет от мелких капель. На экране телефона было достаточно места для нового сообщения, и он наконец дописал:

— Он захотел поблагодарить девушку на выдаче. Сам вспомнил про приложение. Но у неё его нет и ставить некогда. Потом сказал, что отправил бы двадцать рублей. Я рассказал про кошелёк.

ChatMind ответила, когда в подъезде щёлкнул замок.

Рустик убрал телефон и отдал заказ. Мужчина извинился за домофон, пожелал хорошего вечера и придержал дверь, пока тот поправлял лямку сумки.

— Взаимно, — сказал Рустик.

Он вернулся в машину и прочитал ответ.

Модель предложила записать, что именно Денис сделал сам, где понадобилось объяснение и чего он хотел в тот момент у стойки. Потом отдельно разобрать денежную часть.

Рустик написал:

— Может, уже начнём её делать?

— Начнём с пробного перевода между твоими телефонами. С условной суммой, которую нельзя принять за настоящие деньги. Для пополнения и вывода понадобится отдельное решение.

Ему понравилось слово «начнём».

В последние дни перед ним всё чаще появлялись задачи, за которые он раньше просто не взялся бы. Теперь он мог задать первый вопрос, получить понятный ответ и сделать следующий шаг. Когда разговор доходил до чего-то незнакомого, уже не хотелось сразу отступить.

Рустик уже хотел написать «давай сто рублей на пробу», когда заметил выше своё сообщение о Денисе.

Он перечитал его.

Получалось, что чужой человек установил приложение, разобрался и захотел отправить благодарность.

Всё это действительно произошло.

А ещё произошло то, что девушка продолжила выдавать заказы, Денис сказал спасибо вслух, и оба обошлись без DeepTip.

Рустик несколько секунд смотрел на переписку.

— Денежную часть потом отдельно, — написал он. — Сначала помоги мне ничего не потерять из сегодняшнего.

ChatMind задала несколько коротких вопросов. Он отвечал, не пытаясь придать встрече более законченный вид.

Денис понял, как отправить знак.

Нужный ему человек в списке не появился.

Двадцать рублей остались словами.

У Рустика появился первый человек, которому можно написать про следующую попытку.

Он сохранил этот короткий итог и поехал дальше.

* * *

Дома жена разбирала чистое бельё.

Рустик снял куртку, выложил телефоны на тумбочку и подошёл помочь. Ему достались носки, которые после стирки снова утратили всякую связь друг с другом.

— Нашёл своего человека? — спросила жена.

— Нашёл. Даже не я его. Он меня окликнул.

— По имени?

— По названию приложения.

Она улыбнулась.

— Узнаваемость растёт.

Рустик засмеялся и показал два почти одинаковых носка.

— Эти вместе?

— Нет. Они разного размера.

Он положил их обратно.

— Его Денис зовут. Поставили на его телефон. Сам разобрался.

— Ты ничего не объяснял?

— Пытался. Он попросил помолчать.

Жена подняла на него глаза.

— Хороший человек.

— Я заметил.

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

— Ты уже считаешь, сколько получится?

Рустик посмотрел на неё.

— Немного.

— Из двадцати рублей?

— Если людей будет много…

Он остановился и улыбнулся, потому что услышал, насколько далеко успел уйти от Дениса и его телефона.

— Ладно. Пока один.

Жена протянула ему найденный носок.

— Вот. К тому, который у тебя в руке.

Рустик сложил пару.

— Он ведь сам спросил, — сказал он после паузы. — Не я ему предлагал вот это всё про двадцать рублей.

— Значит, ему интересно.

— Да.

— Это хорошо.

Она сказала просто, продолжая разбирать бельё. Рустик почувствовал, что ему именно этого и хотелось: рассказать про сегодняшнее, не доказывая сразу, во что оно однажды превратится.

На тумбочке подал голос iPhone.

— Посмотри, — сказала жена. — Вдруг твоя узнаваемость.

Сообщение было от Дениса.

— Я дома включил, никого рядом. Так и должно быть?

Рустик набрал ответ. Объяснил, что соседей по дому приложение само не найдёт: у них тоже должен быть DeepTip, и они должны разрешить себя видеть.

Денис прочитал почти сразу.

— Понял. Я думал, может, кто ещё проверяет.

Рустик присел на край дивана.

Значит, после ресторана Денис всё-таки открыл приложение ещё раз. Уже дома, без него.

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

Вместо этого он написал:

— Пока ты первый вне нашей кухни. Спасибо, что попробовал.

В ответ пришёл поднятый большой палец.

Затем ещё одно сообщение:

— Про двадцатку не забудь.

— Не забуду.

Рустик вернулся к белью. Нашёл ещё одну пару, потом последнюю, которая отличалась только едва заметной полоской на резинке.

Когда всё было убрано, он сел за стол и открыл разговор с ChatMind.

— Давай попробуем передачу суммы. Пока условной, как ты предложила. Хочу понять весь путь.

Модель спросила, что они будут считать удачной проверкой.

Рустик положил перед собой два своих Android. Вспомнил Дениса у стойки, его вопрос про пополнение и короткое «и забрать?». Никакой новой надписи на экране для этого было недостаточно.

— На первом станет на двадцать меньше. На втором — на двадцать больше. Оба увидят, что произошло. И будет ясно написано, что это пробные единицы, а не настоящие деньги.

Он перечитал сообщение и добавил:

— С настоящими будем разбираться отдельно.

ChatMind предложила начать со ста пробных единиц и провести их между двумя аппаратами.

— На каком телефоне будет начальная сумма?

Рустик подвинул к себе тот, с которого обычно отправлял.

— Сначала у меня.

Спасибо за чтение

Вернуться к книге