Работая над проектом obsidian-tasks вместе с Clare Macrae, я познакомился с двумя мощными концепциями, которые помогли мне справиться с одной из самых сложных проблем в программировании — именованием переменных.

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

Я так заморачивался на правильных именах, что мог ходить по кругу, постоянно переименовывая вещи — тратя энергию без заметного результата. А конца этому не было видно.

7 стадий именования

Первая концепция, которая помогла мне вырваться из этого порочного круга — твит Arlo Belshee: The 7 Stages of Naming.

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

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

Метод Apple Sauce

Вторая концепция — Apple Sauce Method. Я заметил, что при именовании часто пытаюсь предсказать, как что-то должно называться, вместо того чтобы называть по тому, что оно реально делает. Но предсказать «правильное» имя заранее почти невозможно — особенно до реализации, когда поведение еще не сложилось.

С реальными объектами это тривиально (Location с latitude и longitude — очевидно). Но как только появляются абстракции, начинается каша.

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

Решение: не называть (пока). Вместо этого я даю временное, бросающееся в глаза имя — например, AppleSauce или fooBar. Такие имена служат яркими напоминаниями, что перед мержем нужно переименовать. И несколько таких имен можно использовать одновременно.

Примеры из моего кода:

interface AppleSauce {
  bar: (id: string) => Promise<Device>;
  foo: (id: string) => Promise<null>;
}

// Еще один пример

type Foo = (action: Action) => Bar;
type AppleSauce = { [key in ActionType]: Foo };

Интересно, что такие имена не ухудшают читаемость — пока общая структура понятна, смысл все равно считывается. Главное: AppleSauce удерживает фокус на форме кода и не позволяет застрять в мелких деталях именования слишком рано.

Когда появляется правильное имя?

Смешно, но правильное имя обычно проявляется само — примерно при 75% реализации. В какой-то момент приходит осознание:

«Стоп, это же не AppleSauce, это DeviceClient

И переименование происходит само собой.

В каком-то смысле этот подход напоминает стартап-мышление применительно к именованию. AppleSauce — это просто MVP: оно существует, и этого достаточно. Финальный продукт (нормальное имя) появляется итеративно, когда накапливается информация.

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