А сколько у вас в компании во внутренних системах используется наименований одного и того же поля в API? А сколько способов назвать поле, которое перечисляет список id?
Я часто сталкиваюсь с тем, что при проектировании и разработке HTTP REST API команды (чаще неосознанно) собирают целый семантический и лексический зоопарк наименований. Потом бывает сложно разобраться, что нужно записать в определенное поле, или какое название поля выбрать для перечисления списка ID (например) из уже существующих.
При проектировании и разработке HTTP REST API, консистентность в именовании параметров и ресурсов является недооценненным (по моему мнению) аспектом, который влияет на понятность и удобство использования API.
Консистентность (или согласованность) означает использование одинаковых, похожих и понятных обозначений для свойств, методов и других элементов системы. Это помогает уменьшить когнитивную нагрузку:
при проектировании: нет необходимости каждый раз изобретать названия;
при чтении: стандартные свойства несут одинаковое значение в разных методах;
при реализации: меньше возможность опечатки, т. к. IDE (среда разработки) поможет выбирать из уже существующих переменных.
Поэтому я однажды собрала для себя и коллег гайд–чек-лист под названием “Приглаживаем названия в API” и теперь публикую его для широкой аудитории. Уверена, что он кому-то да пригодится.
Быстрый чек-лист для проверки консистентности:
А теперь поподробнее.
1. Свойства написаны в едином стиле
Существуют разные практики грамматического оформления свойств:
camelCase
, например"createdAt": 1320296464
;snake_case
, например"created_at": "Thu Nov 03 05:19;38 +0000 2011"
;PascalCase
, например"DateTime": "2011-10-29T09:35:00Z"
;kebab-case
, например"date-time": "2011-10-29T09:35:00Z"
;UPPER_CASE_SNAKE_CASE
, например"UPDATE_TIME": "2011-10-29T09:35:00Z"
;и другие вариации.
? Часто на разных слоях системы придерживаются разных стилей, но в пределах одного слоя стоит использовать один (с некоторыми оговорками) стиль.
С разработчиками из моей команды у нас такая договоренность:
В БД мы используем snake_case.
В API мы используем camelCase.
2. Используются стандартные названия и стандартные правила наименования
В большинстве систем используются одни и те же стандартные свойства, например:
-
Пагинация
limit, offset
(Facebook)page, rpp (records per page)
(Twitter)pageNumber, pageSize
start, count
(LinkedIn)
-
Текстовый поиск
q, subString, search, text
-
Диапазоны
startDate, endDate
startAt, stopAt
depart_start, depart_range
(количество дней +- от даты)(aviasales)price_min, price_max
-
Поля в ответе
fields
-
Сортировка
sort, sortBy, sortProperty
- признак сортировкиorder, sortDirection
- направление сортировкиsort=-age
- два в одномМногоуровневая сортировка:
sort[name]=ASC, sort[email]=DESC
И много других
? Составьте свой словарь стандартных названий и переиспользуйте те же свойства
? Составьте правило формирования названий. Например, если вы возвращаете дату и время, используйте постфикс smthAt. Если указываете проценты, используйте постфикс smthPct.
*Постфикс и префикс могут быть любые, просто используйте релевантные и одинаковые для решения похожих задач.
Если не придерживаться справочника, в одном ответе JSON будет свойство pctPrice
, в другом pricePct
, рядом еще createdDate
и createdAt
и будет сложно и проектировать, и разрабатывать, и авто-тесты писать.
3. Названия в единственном и множественном числах согласованы
Сразу пример для иллюстрации:
Идентификатор пользователя передается как
userId
, а список идентификаторов какuserList
Как сказал один из системных аналитиков команды: "Выглядит очень загадочно".
? Я предпочитаю использовать конструкции, в которых можно найти общий знаменатель. Например такие пары:
Идентификатор пользователя -
userId
, а список идентификаторов -userIds
Аналогично, например,
errorCode
, а список -errorCodes
Менее предпочтительные варианты, но тоже имеют место быть:
Идентификатор пользователя -
user
, а список идентификаторов -userList
Однако, смущает, что по названию не понятно, идентификатор это или целый объект с данными (см. п.5 чек-листа)Идентификатор пользователя -
user_id
, а список идентификаторов -user_id_list
.
Конструкция сложная и длинная, но ее можно использовать, если самый первый вариант не подходит для всех комбинаций, которые нужны.
4. Названия согласованы между несколькими слоями приложения
Чаще всего атрибуты в таблице сущности и в ее отражении в API схожи.
Например, если в таблице есть поле discount_pct
(скидка в процентах), логично соответствующее свойство в API назвать discountPct
. Это упростит жизнь и аналитику, и тестировщику, и разработчику, и команде поддержки.
5. По названию поля понятно, что в нем содержится
Если без технической документации вам не понятно, что нужно вписать в поле, или что конкретно вы получите в ответе - стоит его поменять. Плохие примеры:
[{
"request": "N12345",
"author": "Anna"
}]
По полю
author
не понятно, что в ответе будет имя. Я бы назвала свойствоauthorName
, а еще лучше сделала бы вложенный объектauthor
со свойствамиid, name
GET /recommendations?code=1234
В этом случае поле
code
слишком абстрактное. Это пример из практики. В результате оказалось, что там нужно указать тип рекомендации.
6. По названию поля понятно, как оно используется
Этот пункт тесно связан с предыдущим, но немного о другом. Приведу пример из реального драфта технической спецификации, которую я изучала:
{
"taskId": "1",
"atLeastOne": true,
"channelIds": ["1", "2", "3", "4"]
}
Логика: Если
atLeastOne = false
, проверка считается финальной и результат нужно сохранить в БД.
Уже видите количество часов, которое понадобится для того, чтобы разгадать эту особенность без технической спецификации?
Контекст для тех, кто хочет разобраться
Контекст был такой: нужно проводить предварительную проверку ("просто посмотреть") и финальную (зафиксировать результат). Так совпало по бизнес-требованиям, что для предпроверки нужно было проверять в режиме "хотя бы одно верно", а для финальной проверки "все верно". Логика при проектировании так и сложилась:
если режим не "хотя бы одно верно" (
atLeastOne = false
) => значит проверка финальная => значит сохраняем в БД
В чем проблема: Поле atLeastOne
по смыслу и названию означает "хотя бы один или все", а применяется как "предпроверка или финальная проверка" или "сохрани или не сохрани".
Что стоило бы сделать:
пусть поле
atLeastOne
управляет логикой проверки и говорит: "хотя бы один или все"пусть доп. поле
isFinal
управляет режимом проверки и говорит: "предпроверка или финальная проверка"или доп. поле
saveResult
управляет результатом проверки и говорит: "сохрани или не сохрани"
? Делайте так, чтобы ваши поля говорили сами за себя.
7. Отсутствует полиморфизм в структуре
Что такое полиморфизм? Вернемся к одному из предыдущих примеров. Например, содержимое ответа на запрос GET /requests
ответ выглядит так:
{
"request": "1",
"author": "Anna"
}
А ответ на запрос GET /requests/1
так:
{
"request": "1",
"author": {
"id": "123",
"name": "Anna"
}
}
? Это полиморфизм структуры ресурса (в данном случае вложенного):
в одном случае поле
author
— это строка с именем;в другом случае поле
author
— это объект с вложенными данными.Такой ситуации стоит избегать. Если один и тот же объект используется во многих эндпоинтах, я рекомендую его сразу заключать в объект, как в
GET /titles/1
Иногда так делают, чтобы просто не возвращать все данные, укоротить ответ. Но в таком случае стоит отдельным параметром (да или ладно, просто в коде) управлять списком полей в ответе (если не во всех ответах требуются все из них).
Подробнее про объекты
P. S. Я бы хотела, чтобы эта статья дополнялась и была живой, поэтому если у вас есть свои стандарты и предложения по наименованиям и правилам, оставляйте их в комментариях, будем вместе пополнять рекомендации.
Samr1
Спасибо за труд! Полезный гайд, стандартизация сильно экономит время. А как вы решаете эту задачу между командами?
Мы придерживаемся стиля snake_case, т.к. при чтении меньше когнитивная нагрузка. Но разделение стилей БД с API - хорошая идея.
WayMax
Почему?
AnnaMozer Автор
я придерживаюсь такого разделения на основании документации по технологиям
Например если заглянуть в официальную доку postgresql, там все аттрибуты в БД указаны через snake_case. И раз авторы технологии так указали, я решила следовать этому стилю при работе с базой
во всех официальных туториалах javascript переменные в основном называются через camelCase, поэтому при работе с JSON (javascript нотация ведь), придерживаюсь такого стиля
ну а где JSON в API, там и остальные параметры, поэтому их в ту же корзинку
не претендую, конечно, на истину, но у меня были такие размышления