Вчера я закрыл ноутбук с чек-листом на утро.
Второй пункт в нём: собрать проект, и чтобы ни одной ошибки, ни одного предупреждения.
Чек-лист назывался «на утро». Открыл я его в половине второго ночи.
Cmd+B.
Не собралось.
До третьего пункта дело не дошло.
Вчера я предположил, что мне не понравится.
Не угадал. Ровно этого я и ждал.
🎨 Сначала пятна
Разочарования во всём этом ноль.
Я так и планировал.
Объясню аналогией. И сразу скажу: я не художник.
Но если бы мне поставили задачу написать картину, я бы, наверное, начал так. Сначала общая разметка — где горизонт, где фигура. Потом широкие мазки, разными цветами и без всякой аккуратности: не предметы, а пятна и силуэты, которые потом станут фоном. Композиция, чтобы стало видно, где что будет стоять.
И только поверх — детали. Объём. Текстура.
Пятно на этом этапе не ошибка. Пятно — это метод.
Мне нужно было увидеть картину, чтобы её потом нарисовать.
Возможно, настоящий художник прочитает это и скажет, что так не пишут. Что это неэффективно и вообще не с того конца.
Ну и пусть.
Сделав ноль попыток, ты точно не узнаешь, как надо было пробовать.
И вот это уже не про живопись.
Те, кто запускал первые ракеты, отлично понимали, что они ляпнутся. Первые три Falcon 1 не долетели. Четвёртый вышел на орбиту.
Никто не рассчитывал, что первая попытка будет удачной. Рассчитывали, что она будет.
(Я, понятное дело, не сравниваю приложение для сниппетов с ракетой. Я про то, что первая попытка нужна не для результата.)
Так что «работает через задницу» стояло в плане с самого начала. Интересовало меня другое, и это четыре вопроса.
Насколько поломанным оно окажется.
Выйдет ли вообще его запустить.
Когда запустится — чего будет стоить это поправить.
И вывезет ли это ИИ.
Первый ответ я получил, не успев дойти до третьего пункта: не собирается вовсе. Хорошо. Дальше по списку.
🧭 Никто не знает, как правильно
Я когда-то не дочитал книжку Брэнсона — «К чёрту всё! Берись и делай!». Но главное из неё помню до сих пор.
Никто не знает, как правильно.
Не «мало кто». Никто. Того, чего ещё нет, никто ещё не делал, и спросить не у кого.
И когда ты это понимаешь, тебя становится сложнее остановить. Ты больше не ждёшь, пока у тебя появятся компетенции. Ты просто пробуешь. Потом ещё раз. Потом снова.
Хотя работаю я ровно наоборот.
Я работаю с теми, кто умнее и начитаннее меня. Кто делает то, что умеет, и не лезет в то, чего не умеет. Это правильно, это эффективно, и так я работаю много лет.
А этот проект устроен наизнанку. Я делаю то, чего не умею. Пока не умею. И сознательно не даю своему незнанию права голоса.
Потому что от ординарных действий не бывает экстраординарных результатов.
А я хочу большего.
Я смотрю на свою жизнь и понимаю, что я на середине пути. Если мне хватит плодов хорошего работника — окей, можно просто работать, это честная и достойная жизнь.
Но если я хочу чего-то большего, оно делается только шагами в неизвестность.
Вот один из них.
Он сейчас не собирается.
🔨 Три причины
И ни одна из них не про Swift.
Первая — та, над которой я вчера пожал плечами и пошёл спать.
Коммит назывался «Start iOs development», и я решил, что это оговорка. Пальцы помнят десять лет фронтенда, Apple — значит iOS, бывает.
Это была не оговорка.
Проект и правда был настроен как iOS'ный. Целевая платформа — iphoneos. Семейство устройств — телефон и планшет. Идентификатор приложения — koskei.com.jpaste, то есть обратный домен, написанный наоборот.
А код внутри — AppKit. Тот самый NSPasteboard, ради двух строчек которого всё и затевалось.
AppKit на iOS не существует.
Мы всю ночь строили приложение для Mac внутри проекта для айфона. И не заметили этого оба: я — потому что не знаю, где у Xcode ручка, он — потому что его об этом не спросили.
Вторая причина: файлы тестов лежат в мишени приложения, и import Testing там просто не находится.
Третья: вьюхи и вьюмодели лежат в мишени тестов, поэтому главное окно их не видит.
А у этих двух причина одна, и она объясняет весь вчерашний бардак разом. В современном проекте Xcode в какую мишень попадёт файл, решает папка, в которой он лежит. Не список в настройках — папка. Файлы с путём внутри имени легли туда, куда легли, и папка распорядилась ими сама.
Вчера я смотрел на слипшиеся имена и видел неопрятность.
Это была не неопрятность. Это была схема сборки.
Плюс пара файлов, существовавших в двух экземплярах сразу.
Итог: тридцать семь файлов, две с половиной тысячи строк, и ни одной новой возможности.
Похоже на уборку. На выравнивание стопки бумаги перед тем, как начать писать.
Только это не уборка. Это разница между приложением и папкой: вчера у меня было двадцать два файла на Swift и ни одного способа их запустить.
Но вот что я заметил, пока выравнивал.
🪞 Неправильная цель
Сегодня утром я попросил две вещи, и попросил именно в таком порядке.
Первое: пусть оно работает. Что бы про него ни было правдой, это должно быть приложение, в которое можно положить сниппет.
Второе я спросил, пока мы ещё не разогнались, и это было не прозрение. Это был маленький вопрос: а почему мы не используем родное стекло системы? С того места, где я стою, приём выглядит относительно дешёвым.
Ответ вернулся переписанной спецификацией.
В самом начале проекта есть документ, который я писал сам, и в нём стоит слово паритет: взять набор возможностей прототипа за стартовый объём, вместо того чтобы выводить его заново с нуля.
Эта фраза — про то, что приложение делает.
Где-то между тем документом и планом, по которому я сейчас строю, паритет тихо превратился в то, как приложение выглядит. Этого никто не решал. Так прочиталось — и я читал так же, ни разу на этом не остановившись.
Потому что прототип жил в браузере.
Плоский тёмный фон, прямоугольники, собственноручно нарисованные тени. Я два месяца считал это дизайном. Это не дизайн — это то, на что хватило вкладки.
Браузер не умеет размывать то, что лежит под окном. Не знает, тёмная у вас система или светлая, пока не спросит. Не имеет доступа к материалам, из которых macOS собирает собственные окна. Всё, что я там сделал, было обходом этих ограничений.
А в нативном приложении этих ограничений нет.
И получалось, что я переношу в него не решение, а компромисс. Причём компромисс с условием, которого больше не существует.
Прототип отвечал на вопрос, что должно происходить. Он никогда не отвечал на вопрос, как это должно выглядеть.
Поэтому правило переписано: прототип остаётся источником истины для раскладки и поведения — где что лежит, что происходит по клику, куда едет блок. И перестаёт быть источником истины для внешнего вида.
Полностью. Совсем.
🧊 Стекло, которое можно выключить
Вместо плоского фона — родные материалы системы.
Матовое окно, сквозь которое видно то, что за ним. Поверхности, которые берут цвет у обоев. Управление прозрачностью, чтобы каждый настроил под себя.
И сразу два решения, которые пришлось принять сегодня утром.
Первое: минимальная версия системы остаётся прежней. Не всем достался Mac, на котором есть новые материалы, и поднимать планку ради красоты — это выкинуть часть людей ради того, чего они даже не увидят. На старых системах приложение подставляет привычный материал и работает точно так же. Одна сборка, один магазин, одна покупка.
Второе мне нравится больше.
На новых системах, где настоящее стекло есть, в настройках всё равно лежит переключатель, который его выключает.
Потому что стекло — это вкус. Кому-то оно мешает читать. Кому-то просто не нравится.
Не уверен, что этим переключателем вообще когда-нибудь воспользуются. Но материальный вариант уже написан — он обязан существовать ради систем постарше, он уже в том же бинаре, он уже протестирован. Отдать его людям стоит ноль.
А лежит он в настройках достаточно далеко, чтобы никому не мешать. Дать переключатель дешевле, чем быть правым.
🔌 Engineer honesty moment
Теперь две вещи, которые выяснились за сегодня. Это и есть ответ на первый из моих вопросов — насколько поломанным оно окажется.
Первая.
Я сидел и смотрел на холст с одним блоком. Не искал ошибку. Просто смотрел.
И поймал себя на вопросе, который звучит нелепо применительно к приложению, написанному вчера: а как это, собственно, должно работать?
В приложении не было поля ввода.
Вчера я обещал себе сырое, глючное, неработоспособное — но существующее. Вот насколько сырое, и это не по краю.
Поля, в которое печатают текст нового сниппета, в приложении не существовало. Редактирование двойным кликом было сломано. Блок бросить на холст можно, а положить в него текст — нет.
Приложение для сниппетов, в которое нельзя положить сниппет.
Это стояло в плане на потом, потому что в плане стояло красиво выглядеть — а как оно там появляется, разберёмся по дороге. Пришлось поднять этот пункт в самое начало и сделать раньше всего остального. До единой строчки про внешний вид.
Вторая хуже. И нашёл её не я.
В приложении был сейф. Отдельный уровень приватности для вкладок, поле для пароля, замочек, всё как задумано.
Я посмотрел на него ровно один раз. Подумал: а зачем он мне сейчас, если в блок всё равно нечего положить. Потестирую потом.
«Потом» не наступило. Сейф подключили к приложению сегодня вечером — и оказалось, что подключать было нечего.
Криптография была написана. Отдельным файлом, с тестами, и тесты проходили. Сервис сейфа умел зашифровать текст блока и расшифровать обратно — обе функции на месте, обе рабочие.
Их просто не звал тот, кто сохраняет блок.
А в функции, которая выводит ключ из пароля, в комментарии написано: PBKDF2, сто тысяч итераций. В теле — HKDF. У HKDF итераций нет вообще, и число, которое туда передавали, было некуда девать.
Замок висел на двери, которая не закрывалась. И на замке стояла гравировка с классом стойкости.
Сегодня это соединили: ключ выводится из пароля двумястами тысячами итераций, каждая вкладка получает свою соль, при первой настройке сейфа существующие блоки дошифровываются. И то, что уже было расшифровано и лежит в памяти открытой вкладки, теперь честно ищется поиском — а при закрытии исчезает.
И вот это — единственное за сегодня, чего в плане не было. Поломанное я ждал.
Чего я не ждал — что поломанное будет выглядеть достаточно законченным, чтобы я сам отложил проверку на потом.
Сутки в репозитории лежал сейф, который ничего не запирал. Единственное, что помешало мне что-нибудь в него положить, — что положить было нечем.
💬 Вопрос
Когда вы в последний раз копировали собственное старое решение, не спросив, почему оно было таким?
У меня ответ — сегодня. И самое обидное, что ограничение, под которое я его когда-то придумал, давно снято, а решение я тащил дальше как ценность.
Расскажите, как у вас 👇
Дальше всё зелёное: сборка, тесты, четырнадцать задач.
Вот это меня и беспокоит 😉