Компьютерам безразличен дизайн API. Удобные форматы сериализации, осмысленные названия параметров и RESTful-интерфейс ничего не значат для робота. Эти аспекты API предназначены не для машины, а для вас — разработчика. API должны создаваться в первую очередь для людей, а во вторую — для компьютеров.
Не останавливайтесь на одном дизайне, пока его не увидело как можно больше глаз и умов. Если перед вами стоит задача «Разработать способ поиска телефонных номеров, которые клиенты могут приобрести», что у вас получится? Не существует единственного дизайна, который удовлетворял бы этому требованию, и в результате вы можете создать множество разумных API.
По этой причине ресурс AvailablePhoneNumbers было сложно спроектировать. Одно из главных правил, которым мы следуем при разработке API, — **сделать простой случай максимально лёгким**. При поиске номеров в США большинство пользователей хотят одно из двух: номер с определённым кодом региона или номер, содержащий определённую последовательность цифр. Это первые два фильтра, которые мы документируем, и те, на которые, как мы ожидаем, обратят внимание большинство пользователей.
Зачем вообще включать параметр кода региона? То же самое можно сделать с помощью параметра ‘Contains’ и подстановочных знаков, например, +1555******. В первой версии дизайна был только параметр Contains. Он был простым и мощным. Но он также вызывал раздражение, когда требовался только поиск по коду региона, так как приходилось помнить точный синтаксис фильтра. Если для выполнения простейшей задачи нужно так много думать, значит, мы провалились как дизайнеры API.
Рассмотрим ресурс IncomingPhoneNumbers. POST-запрос к этому ресурсу позволяет купить номер, а GET-запрос — увидеть купленные номера. При проектировании этой функциональности первым инстинктом многих разработчиков было бы создание отдельного ресурса для покупки номеров. POST /ProvisionPhoneNumber для покупки и GET /IncomingPhoneNumbers для получения списка купленных номеров. Однако теперь нам нужен совершенно новый ресурс для предоставления телефонных номеров, поддерживающий только один метод. Если умножить этот подход на количество функций, которые вы хотите реализовать, получится взрывной рост сложности.
«`
GET /Calls
POST /PlaceCall
GET /SMS/Messages
POST /SendSMS
GET /IncomingPhoneNumbers
POST /ProvisionPhoneNumber
«`
против
«`
GET|POST /Calls
GET|POST /SMS/Messages
GET|POST /IncomingPhoneNumbers
«`
Выбрав последовательную конвенцию, мы смогли упростить поверхность API. Когда мы добавляем такие вещи, как субаккаунты, это становится естественным расширением API. POST /Accounts для создания субаккаунта, GET /Accounts для просмотра ваших аккаунтов. Кроме того, каждое уникальное название параметра увеличивает вес вашего API. Будьте последовательны и старайтесь сократить количество ресурсов, которые клиенту вашего API придётся изучать. Поставьте себя на место пользователей.
Существует тонкая грань при добавлении новых функций. Если у вас есть особенно спорный дизайн функции, не бойтесь его исключить. После того как вы выпустите её в публичный API, удалить функции будет крайне сложно без a) повышения версии API или b) нарушения работы приложений клиентов. Это означает, что каждое дополнение должно быть рассмотрено с пониманием того, что удаление или изменение API позже может оказаться сложнее для клиентов, чем его отсутствие изначально. В Twilio мы часто добавляем дополнительные параметры, ресурсы или свойства по мере того, как видим, как клиенты используют API, вместо того чтобы пытаться предсказать все сценарии использования с самого начала.
Даже после всего этого ошибки всё равно будут.
В долгосрочной перспективе это лучше для вашей пользовательской базы и для вас. Иногда ваши предположения оказываются неверными, иногда название параметра недостаточно понятно, иногда формат ответа далёк от идеала.
Однако после выпуска новой версии API очень важно поддерживать доступ к старой версии в течение некоторого времени. Очень сложно заставить клиентов, которые уже интегрировались с определённой версией API, обновиться. Вы можете предложить стимулы: новые функции, лучшую производительность и т. д. Но даже в этом случае многие клиенты, у которых уже есть работающая интеграция, неохотно идут на затраты, связанные с обновлением.
Один из способов снизить эти затраты — сделать изменения версий максимально прозрачными. С помощью умных клиентских библиотек можно абстрагировать определённые аспекты версионирования API. Например, клиентская библиотека может преобразовать такие запросы:
«`
responseText = client.request(«/2010-04-01/Accounts»)
d = parseXML(responseText)
a = d.xpath(«//Accounts»)
«`
в такие:
«`
accounts = client.getAccounts()
«`
Теперь предположим, что вы добавили поддержку JSON в дополнение к XML. Вы можете умолять всех своих клиентов переключиться, но почти никто не сделает этого, если уже реализовал первый вариант. Во втором случае все могут получить обновление бесплатно. Более того, если ваша клиентская библиотека достаточно умна, чтобы отправлять заголовок Accept: application/json; text/xml;, сервер может на лету решать, какое представление наиболее эффективно для данного запроса, и динамически отправлять соответствующий Content-Type. Пользователю библиотеки никогда не нужно будет знать, какой тип ответа он получает, и ему не придётся обновлять библиотеку для обработки того или иного формата.
Дизайн API — это в такой же степени искусство, как и наука. Не существует единственного «правильного способа» его создания. Разные API ощущаются и ведут себя по-разному. Мы развивали API Twilio от нескольких функций до более чем десятка и продолжаем добавлять новые возможности постоянно. В конечном счёте, мы создаём наши API для вас: *человека, которому нужно выполнить работу.*