Глаголы и имена
Функция — это действие, значит имя начинается с глагола. Глагол берётся из закрытого словаря, и каждый глагол несёт контракт, который нельзя нарушать.
Чтение
find*Может вернуть null. Никогда не бросает — отсутствие это нормальный исходget*Гарантирует значение. Нет значения — бросает. Никаких null в типеlist*Всегда коллекция. Пустая коллекция — не ошибка и не nullcount*Число. Никогда не nullОдна эта пара меняет чтение кода:
const track = await findTrack(trackId)
if (track === null) return makeFail('music.track.missing', 'Трек не найден')
const owner = getOwner(track)Видно, не открывая функций: у первой отсутствие результата — штатная ситуация, у второй — баг. getOwner в этом месте означает «владелец обязан существовать, иначе данные сломаны».
Границы и хранилище
read* / write*Пересечение границы I/O: сеть, диск, объектное хранилищеload* / save*Агрегат целиком через хранилище (БД)Разделение read и load не педантизм: по имени видно, переживёт ли вызов офлайн и стоит ли его кэшировать.
Вычисление
make*Чистый конструктор значения. Без I/O, без времени, без случайностиderive*Чистое вычисление из уже имеющихся данныхresolve*Выбор одного варианта из нескольких по правиламparse* format*Разбор и представление: вход → выход, без состоянияencode* decode*Смена представления без потери смыслаnormalize*Приведение к канонической формеsign* verify* hash*Криптографические преобразованияИзменение
create* / delete*Мутация домена: меняет состояние и порождает событиеarchive* / restore*Обратимое изъятие из оборотаensure*Идемпотентно приводит состояние к нужному — повторный вызов ничего не меняетassert*Бросает при нарушении инварианта, иначе void. Ничего не возвращаетapply*Накатывает готовое изменение на цельset*Присваивает значение, дальше по системе ничего не происходитСвязь и жизненный цикл
emit*В шину. Адресат неизвестен и не важенsend*Конкретному адресату — адресат часть имени: sendVerifyEmailstart* / stop*Жизненный цикл процесса или подсистемыuse* with* render*Только в UI-коде: хуки, обёртки, отрисовкаЗапрещённые глаголы
Каждый запрещён потому, что не сообщает действия — он сообщает, что автор не решил, что делает функция.
| Запрещено | Вместо |
|---|---|
handle* | Назови действие: submitLogin, retryPayout, dropStaleSession |
process* | derive* / apply* / точный глагол преобразования |
manage* | Функция делает больше одной вещи — раздели |
do* perform* execute* run* | Назови действие |
check* | is* если возвращает ответ, assert* если бросает |
init* | start* для процесса, make* для значения |
update* | save* / apply* / set* — что именно происходит |
fetch* retrieve* | read* (I/O) / load* (агрегат) / get* |
calculate* compute* | derive* |
generate* build* prepare* | make* |
setup* | ensure* / start* |
validate* | assert* (бросает) / parse* (возвращает разбор) |
transform* convert* | derive* / format* / encode* |
determine* | resolve* |
trigger* | emit* / send* |
Порядок слов: домен → уточнение → единица
urlOfCover
refreshTimeout
amounttrackCoverUrl
sessionRefreshTtlSec
payoutAmountCentsОбщее слева, частное справа. Такие имена сортируются в осмысленные группы, а автодополнение по первому слову выдаёт всё про домен сразу.
Число без единицы измерения — ошибка
Это единственное правило GRAIN, которое ловит целый класс продакшн-багов: секунды, отданные туда, где ждали миллисекунды.
| Суффикс | Смысл |
|---|---|
*At | Момент времени, всегда UTC |
*Ms *Sec *Min *Hours *Days | Длительность |
*Bytes *Kb *Mb *Gb | Размер |
*Cents | Деньги, всегда в минорных единицах, всегда целое |
*Ratio | Доля 0..1 |
*Pct | Проценты 0..100 |
*Count *Index | Счёт и позиция |
*Px *Deg *Hz *Bpm *Db | Доменные величины |
Безразмерные величины, которым суффикс не нужен: x y z width height depth port page limit offset version priority. Список закрыт — если твоя величина не в нём, у неё есть единица.
Булевы — семь префиксов, других нет
is · has · can · should · was · will · must
const isPublished = track.publishedAt !== null
const hasActiveSubscription = subscription.state === 'active'
const canPublish = isOwner || isAdminenabled, published, flag, status булевым именем не являются.
Слова, которые ничего не сообщают
Запрещены как последнее слово имени и как имя целиком:
data info obj object temp tmp stuff misc thing things
res ret arr str num flag val result
helper handler manager wrapper util utilsuserData → user. trackInfo → track или trackSummary. parseResult → parsedTrack. Если после удаления пустого слова имя стало неотличимо от другого в той же области видимости — значит эти две сущности и правда одно и то же, либо одной из них нужно настоящее имя.
Сокращения
Разрешены ровно эти: id url uri db io rpc ttl utc api sql css html json jwt s3 ip dns cdn ui ms.
Всё остальное пишется целиком: config не cfg, request не req, message не msg, error не err, index не idx, previous не prev.
Длина имени пропорциональна области видимости. В колбэке на две строки t допустимо и честно. В экспортируемом API однобуквенных имён нет.
Самопохвала
enhanced advanced comprehensive ultimate robust powerful seamless smart intelligent optimized improved modern simple easy quick — в именах запрещены. Это оценка, а не описание; она устаревает в момент коммита и почти всегда означает «я не нашёл, чем эта версия отличается от прошлой».