Более пяти лет назад мы запустили платформу Slack, предоставив разработчикам легкий способ создавать приложения в Slack и публиковать их в нашей App Directory. Сегодня миллионы пользователей переносят свою работу в Slack, и их приложения, создаваемые более чем 885 000 активными разработчиками, действующими на этой платформе – залог дальнейшего улучшения совместной работы в Slack.
Все эти годы мы постоянно держим в уме: «проектировать так, чтобы разработчикам было максимально удобно». Да, под капотом мы можем менять реализацию некоторых наших фич, но удалить или изменить контракт, описывающий поведение существующего API, очень сложно. Вот почему так важно аккуратно продумывать проектирование API с самого начала.
Если API спроектированы хорошо, разработчики их полюбят и могут стать самыми креативными инноваторами, пользующимися вашими API. Они будут сильно в них вкладываться, а иногда даже могут стать евангелистами ваших API. Мы также ценим время разработчиков и те ресурсы, которыми они могут рискнуть, имея дело с нашей платформой. Если API плохо спроектировать, то его примут холодно, и он даже может вызывать фрустрацию. Плохие API превращаются для компании в обременение.
Я не утверждаю, что Slack всегда проектировал API хорошо. У нас были ошибки, и платформа определенно могла бы быть удобнее для разработчиков. Но мы признаем эти ошибки и определяем, как их исправить – иногда даже дополнительно упирая на то, чтобы придерживаться какого-то выбора, сделанного в прошлом, тогда как сейчас мы бы с этим выбором не согласились — и в целом можем улучшить для разработчика опыт работы с платформой.
Разработка собственного стилевого руководства по проектированию API не может полностью застраховать нас от корявых решений или такого выбора, который мы сегодня делали с энтузиазмом, а завтра в нем будем раскаиваться. В этом посте мы опишем принципы проектирования наших API, а также расскажем, как у нас пишут спецификации для новых API, рецензируют их и тестируют. В конце концов вы можете выстроить для себя некоторую картину и воспользоваться этими идеями в процессе работы над собственными API.
Наши принципы проектирования
С годами мы выработали некоторые принципы, которыми руководствуемся при проектировании наших API.
1. Делай одно дело и делай его хорошо
Когда приступаешь к проектированию API, существует соблазн попробовать одним махом решить сразу множество проблем. Когда пытаешься делать несколько дел сразу, ситуация становится сложной и неразборчивой. Если же выбрать конкретный кейс, то можно сосредоточиться на проектировании единственной фичи, что позволяет сохранять простоту API. Простые API легче не только понимать, но и масштабировать, они более производительны и безопасны. Более того, к такому API легко добавлять новые фичи, а вот удалять – нелегко.
В Slack большинство из наших API сделаны в соответствии с этой философией. Однако бывает и так, что приходится дробить имеющиеся сложные API на более простые и адресные. Один из самых популярных методов наших API, rtm.start
, со временем стал обходиться весьма затратно. Этот метод должен возвращать URL для подключения к API наших веб-сокетов, а также к самым разнообразным данным о команде, ее каналах и членах. По мере того, как размер команды увеличивается, полезная нагрузка становится слишком велика и неудобна в обращении, разработчикам тяжело с ней управляться.
Хотя и есть считанные разработчики, использующие данные, возвращаемые от этого метода, большинство разработчиков хочет всего лишь подключаться через него к WebSocket. Поэтому в Slack был введен новый метод API, rtm.connect
, делающий ровно одну вещь: возвращающий URL сеанса WebSocket API, а больше не содержащий никакой полезной нагрузки. Этот новый метод помог разработчикам приложений и самому Slack преодолеть некоторые проблемы масштабирования, возникавшие с rtm.start
.
Вот один из выводов, следующих из этой специфической истории: в случае сомнений навязывай, чтобы в любой коллекции было конечное количество объектов, либо разбивай их на страницы. Это не преждевременная оптимизация, когда определяешь разумную, рациональную верхнюю границу. Если вы сами пытаетесь нащупать эти границы, допуская органический рост, то будет гораздо сложнее удержать их в сколь-либо разумных пределах.
2. Дайте возможность быстро и легко приступить к работе
Разработчикам важно иметь возможность понять ваш API и быстро приступить к работе. Вы же хотите, чтобы разработчики, пока не знакомые с вашим API, могли достаточно быстро изобразить с ним пример «Hello world». Одна из метрик, позволяющих оценить этот показатель – «время, уходящее на написание первого «Hello World»». В Slack нас устраивает, если разработчику, который только знакомится с платформой, требуется 15 минут, чтобы узнать о платформе, создать на ней приложение и отправить первый вызов API. Ваше целевое «время, уходящее на написание первого «Hello World»» будет варьироваться в зависимости от платформы, на которой вы работаете, а также от уровня подготовки разработчиков, которые являются вашей аудиторией.
Руководства, туториалы, образцы кода и интерактивная документация «молодого бойца» может значительно помочь программистам быстро приступить к работе. В документацию Slack кроме таких руководств входит интерактивный инструмент для тестирования API, где разработчик может прямо в браузере может прощупать любую конечную точку API. Мы даже стали вплетать туда фрагменты кода на разных языках, которые могут быть легко подставлены в основной код при помощи нашей растущей коллекции SDK.
![Образец кода в документации Slack API Образец кода в документации Slack API](https://habrastorage.org/getpro/habr/upload_files/b1e/c18/033/b1ec180334ce5be4940a72325f88bf1d.png)
Bозможность быстро приступить к делу – это отлично, но что будут делать ваши разработчики, справившись с «Hello World»? Если вы хотите измерить, удобно ли работать вашему разработчику, то обратите внимание и на другие цели, которых ему требуется достигать. Мы в Slack считаем, что разработчику важно показать свое приложение как минимум одному пользователю, либо довести интерактивность до такого уровня, чтобы приложение могло сделать что-нибудь сверх отправки сообщения по команде.
Проектируя вашу исходную документацию API, всегда задавайте себе вопрос: что наиболее важно на создаваемой нами платформе?
3. Стремитесь к интуитивно понятной непротиворечивости
Нужно, чтобы ваши API интуитивно воспринимались как непротиворечивые. Это должно быть отражено в именах ваших конечных точек, параметров ввода и откликах, получаемых в качестве вывода. Разработчик должен иметь возможность сам догадаться, какие части вашего API за что отвечают, даже не читая документацию. API должен работать примерно так же, как другие API, уже известные разработчику. Есть 3 уровня непротиворечивости:
Соответствие стандартам, принятым в индустрии: API должен, насколько это возможно, воплощать наилучшие практики, принятые в индустрии.
Соответствие вашему продукту: имена полей следует подбирать так, чтобы они соответствовали названиям концепций из вашего продукта, избегать аббревиатур, сокращений и жаргона. Здесь пишите подробно.
Соответствие другим методам вашего API: имена, которые вы используете в различных методах API, должны согласовываться друг с другом.
Один из лучших способов добиться такой непротиворечивости – закрепить и расписать ваши мнения о проектировании API, особенно, если сделать что-то явно можно несколькими способами. Выберите сторону и придерживайтесь ее. В Slack мы написали подробное руководство по проектированию API, определяющие непротиворечивые практики и паттерны, которых мы в дальнейшем придерживаемся.
![Предварительная версия не претендующих на объективность правил проектирования API в Slack Предварительная версия не претендующих на объективность правил проектирования API в Slack](https://habrastorage.org/getpro/habr/upload_files/685/325/caa/685325caaa6f5751cd68de1cda8d0836.png)
4. Возвращайте информативные ошибки
Еще один принцип проектирования API – возвращаемые ошибки должны быть информативными, так как это упрощает разработчику устранение неисправностей. Слишком часто бывает так, что при проектировании API разработчик не слишком внимательно смотрит за тем, какие ошибки возвращают его API. Неверно или нечетко описанные ошибки раздражают, и это может отбить у людей желание переходить на ваши API. Разработчик может застрять и просто сдаться. Хорошо сформулированные ошибки легко понимать, они недвусмысленные и сразу подсказывают, что делать. Они помогают разработчику понять саму проблему и подсказывают, как к ней подступиться. Детали реализации не должны утекать в ваш API, в особенности это касается ошибок.
![Хорошие ошибки и не очень хорошие Хорошие ошибки и не очень хорошие](https://habrastorage.org/getpro/habr/upload_files/5e8/4bd/ba8/5e84bdba8b07ff761af0b7efbba58750.png)
Наряду с кодами ошибок полезно также добавлять развернутое описание ошибки, либо в документации, либо где-нибудь в отклике API. Эта информация может включать человеко-читаемые характеристики ошибки, рекомендации, как исправить проблему, либо ссылку на дополнительную информацию.
![Сообщения об ошибках в развернутой форме в Slack API Сообщения об ошибках в развернутой форме в Slack API](https://habrastorage.org/getpro/habr/upload_files/337/767/838/3377678383ad225120cafbb19281c81c.png)
Если вы также создаете SDK и библиотеки для разработчиков, важно, чтобы сообщения и предупреждения об ошибках не «глотались». Убедитесь, что у разработчика есть доступ ко всему, что может ему пригодиться в горестном отладочном разделе, в частности, есть HTTP-заголовки и заготовки для тела запроса. Здорово, если SDK может интерпретировать ошибки и извлекать из них еще больше пользы, но разработчик также должен быть в состоянии просто прочитать отрывок документации API об ошибке – и все равно отловить ее на уровне SDK. Иногда это не получается и по нашей вине, когда мы лишаем разработчиков полезных сообщений об ошибках, случающихся на нашей платформе.
5. Проектируйте с расчетом на масштабирование и производительность
Некачественное проектирование ограничивает производительность API. Поэтому, работая над созданием API, убедитесь, что делаете следующие несколько вещей:
Разбиваете большие коллекции на страницы: Довольно часто API приходится обрабатывать крупные множества данных. В ответ на вызов API могут быть возвращены тысячи элементов. Так можно перегрузить бекенд веб-приложения и даже замедлить работу клиентов, не приспособленных для работы с большими множествами данных. Именно поэтому важно разбивать на страницы любую большую выборку с результатами.
Не вкладываете большие коллекции в другие большие коллекции: в таком случае разбиение на страницы чрезмерно усложняется.
Ограничиваете скорость передачи данных на вашем API: конечно же, вы не хотите, чтобы один неаккуратный разработчик или один вышедший из-под контроля цикл положил весь ваш код. Чтобы защитить инфраструктуру, в то же время повысив надежность и доступность приложения, эта скорость не должна превышать разумных пределов.
Один из API Slack, channels.list
(уже выведен из обращения), обычно возвращал список каналов и всех членов каждого канала – в сущности, при этом одна коллекция вкладывалась в другую. Когда Slack только появился, предполагалось, что в каждой команде будет не более нескольких сотен пользователей. Но, по мере того, как проект рос и продолжал развиваться, это допущение перестало выполняться. У нас появились команды с десятками тысяч участников. К этому моменту структуру, требовавшую возвращать все каналы и всех пользователей для каждого канала, стало слишком сложно поддерживать. Мы разбили имевшийся API на два более простых: conversations.list
и conversations.members
. Так удалось не только повысить производительность нашего API, но и добиться, чтобы пользователям стало удобнее с ним работать.
Мы вновь затрагиваем тему разбивки на страницы не только потому, что такая разбивка важна, но и потому, что ошибка происходит все по тому же принципу. То, что «вполне хорошо» для вас, может быть не «вполне хорошо» для остального мира. Отчасти работать с API настолько интересно, поскольку никогда не знаешь, как его станет использовать другой разработчик. Но, все равно это ваша обязанность – предвосхищать множество тех возможностей, которые от вас пока ускользают, особенно если вы сами являетесь потребителем того же API.
6. Избегайте коренных изменений
Меньше всего вам хотелось бы, чтобы при релизе обновления вашего продукта сломался код у ваших клиентов – и все потому, что изменился API. Итак, что же такое «коренное изменение»? Это любое изменение, из-за которого приложение может перестать работать так, как работало до него.
В Slack мы придерживаемся философии, что все, работавшее вчера, должно работать и завтра. Конечно, мы признаем, что изменения неизбежны. В редких и исключительных случаях мы решаемся на коренные изменения. Насколько громко об этом объявлять и как далеко заходить, чтобы работа с API все равно казалась пользователям гладкой – зависит от множества факторов, как то количество пользователей и рабочих пространств, которые будут затронуты при изменениях, а также насколько сложно разработчикам будет освоиться с данными нововведениями. Не хочется вынуждать одних и тех же разработчиков вникать в новую порцию коренных изменений каждые несколько месяцев.
Бывает, что вещи ломаются, когда этого совсем не ждешь. Пусть у вас будет (пожарный) план, как договориться о реагировании на коренное изменение, наступившее внезапно, особенно такое, которое невозможно откатить.
Процесс проектирования
У нас есть не только четко определенное руководство по проектированию API, мы к тому же придерживаемся строгого процесса проектирования API, который позволяет любой команде, работающей со Slack, создавать публичные API. Вот его пошаговое описание:
1. Пишем спецификацию API
Как только команда конкретизирует ту проблему, которую собирается решить и определит кейсы для использования API, приступаем к написанию спецификации. Она поясняет различные аспекты API, которыми разработчики собираются пользоваться. В спецификации можно указать, в частности, имена методов, назначение методов, пример запроса, пример отклика, возможные ошибки. Спецификация позволяет досконально продумать весь дизайн API. Она также играет роль централизованного черновика, по которому у все складывается общее предоставление о том, каковы наши цели, и как у нас предоставляются ресурсы.
Если вы работаете в команде, которая склонна писать схему JSON и/или спецификацию в общедоступном формате, например, OpenAPI или AsyncAPI, это даже поможет вам прототипировать ваш API до реализации. Опубликовав вашу схему для разработчиков, вы поможете им подобрать собственный инструментарий, но, признаюсь, постоянное поддержание спецификации в актуальном виде выматывает. Это – то направление, в котором мы собираемся совершенствоваться.
![Пример спецификации API Пример спецификации API](https://habrastorage.org/getpro/habr/upload_files/5ad/777/7c0/5ad7777c04c5dd7ab8b1add6fa1ede94.png)
2. Внутреннее рецензирование
Проблемы с проектированием выявляются гораздо эффективнее, если искать их прежде, чем писать какой-либо код. В Slack, написав спецификацию, разработчики делятся ею во внутреннем канале, который называется #api-decisions. Это позволяет разработчикам взглянуть на свое предложение с разных сторон, и помогает им в этом кросс-функциональная группа, в которую входят деврелы, программисты, продакт-менеджеры, специалисты из службы поддержки, партнерские инженеры и специалисты-безопасники. Если для внесения изменения требуется подробнее поговорить с разработчиками, мы делаем такое ревью в специально выделенные рабочие часы. Во время таких совещаний обсуждаются вопросы вроде: «согласуются ли вносимые изменения с имеющимся у нас API?» или «согласуются ли они с нашим руководством по написанию API»? Также группа подробно разбирает проблемы именования, удобства использования, безопасности и производительности.
3. Ранняя обратная связь от партнеров
Мы предоставляем черновик спецификации API не только стейкхолдерам из нашей компании, но и партнерам, которые с нашей точки зрения являются идеальными разработчиками для нашего API. Их обратная связь критически важна для оценки того, решает ли API задачу именно так, как должен решать; также она помогает нам понять те аспекты нашего API, которые нуждаются в улучшении. Обращаясь за такой обратной связью, прежде, чем писать какой-либо код, мы можем разбить проектирование API на быстрые итерации, и сам API получается гораздо более качественным.
4. Бета-тестирование
Прежде, чем выставить новые API в широкий доступ, мы предоставляем ранний доступ избранным партнерам. Эти партнеры интегрируют наши API в свои продукты и подробно нам рассказывают, как эти API работают. Так мы получаем дополнительную возможность еще немного поправить API и отловить какие-нибудь проблемы до официального релиза.
Сохраняйте гибкость
Если у вас никак не получается выработать собственные правила проектирования API и выстроить процесс рецензирования API в соответствии с ними – что ж, это означает, что в какой-то степени ваша команда эти правила уже усвоила.
Пройдет не так много времени, и вам встретятся случаи, не вписывающиеся в спрогнозированные вами сценарии. Вы пойдете на компромиссы, которые сами бы никогда не посоветовали делать, иногда ради блага собственной инженерной практики, иногда – стремясь, во что бы то ни стало сохранить непротиворечивость. Но иногда вам попадается на пути API совершенно нового вида, и при работе с ним нужно следовать духу ваших правил. Букву этих правил соблюсти всегда успеете.
Заключительные мысли
Сложно проектировать интуитивно понятные и легкие в использовании API. В этом посте мы описали принципы и процессы проектирования API, которых мы придерживаемся в Slack. Вот некоторые ключевые выводы, которые пригодятся вам при проектировании API: потратьте время и немного продумайте заранее, как API будет устроен, целенаправленно выбирайте определенные варианты, собирайте обратную связь от множества заинтересованных лиц.