Константин, ведущий блог на Medium, перевел статью Анупа Ковкура с рассказом о том, как спроектировать API и сделать это идеально.
Другим разработчиками действительно придётся использовать API, который ты напишешь. Поэтому здесь нельзя лажать. Если не хочешь, чтобы полчища разъярённых программистов с факелами и вилами осадили твой дом посреди ночи, то проектируй API правильно.
Здесь приведены несколько советов по разработке, которые я перенял от коллег за прошедшие годы. Они применимы ко всем видам API: будь то библиотеки с открытым кодом, внутренние SDK, модули или даже единственный класс.
Не оставляй сомнений
Это, пожалуй, самый важный совет. Если у тебя есть метод под названием getUser
и он делает что-то неявное, да ещё и умалчивает о таком поведении — это может привести к множеству проблем.
Не изменяй значения общих переменных, не оговаривая это. Если я вызываю getUser
, то ожидаю, что он просто вернёт пользователя. А не увеличит, в процессе, user_id
на 1. Прими во внимание неизменяемые структуры данных.
Имя метода/класса/модуля должно нести как можно больше смысла о том, что он делает. В разумных пределах, конечно. Не жди, что пользователи будут нырять в код в поисках скрытого поведения, о котором не говорит имя. Таким образом, ты избавишь себя от головной боли в дальнейшем.
Как спроектировать API: уменьшай площадь охвата
Никто не любит раздутые программы. Чем меньше API ты открываешь для выполнения задачи, тем будет лучше для всех.
Кто-то действительно просит этот новый API, который ты хочешь написать? Может стоит его отложить до тех пор, пока это действительно не станет проблемой, которую кто-то хочет решить.
В некоторых средах разработки, таких как Android, существуют жёсткие ограничения на общее количество методов, которые могут быть у приложения. Так что имей это ввиду, если нацелен на такие платформы.
Преждевременное выполнение в ответе за умопомрачительную растрату часов программирования. Применяй YAGNI.
Уменьшай шаблонизацию
Как можно больше деталей реализации должны обрабатываться внутри, чтобы уменьшить нагрузку на клиентов. Чем меньше потребитель должен делать, тем меньше возможное количество ошибок, с которыми ты потом будешь сталкиваться.
В этом также состоит вопрос эстетики. Использование шаблонного кода может разрушить вполне хороший API, и потребительский код будет выглядеть безобразно. Мы все любим чистый код, не так ли? Облегчи потребителям задачу сделать код лаконичным и чистым при использовании твоего API.
Как спроектировать API: уменьшай зависимости
Стремись к тому, чтобы твой код был автономным насколько это возможно. Чем больше у него зависимостей, тем больше проблем это может вызвать в потребительском коде.
Если ты действительно хочешь тот красивый кусочек функциональности из другого модуля, попробуй извлечь его и включить только то, что тебе нужно.
Уравновесить возможность повторного использования кода и тесную связь всегда непросто. Тебе придётся принять такое решение. Если эта функциональность невелика, возможно стоит переписать её самому.
Возвращай осмысленные сообщения об ошибках
Я мог бы целый день разглагольствовать о том, что null — это бесполезная конструкция. Она буквально означает ничего.
— Эй, модуль, дай мне пользователя!
— Неа. Вот тебе вместо этого ничего.
Это даёт мне нулевую информацию о том, что пошло не так, и что я могу сделать, чтобы улучшить ситуацию. Если вместо этого, у нас есть задокументированный способ выражения ожидаемых ошибок внутри нашей предметной области, таких как Error.USER_NOT_CREATED
или Error.USER_DELETED
, — это даёт мне гораздо более действенные данные и помогает отлаживать проблему.
Сообщения об ошибках должны следовать тем же правилам. «Вам нужно авторизироваться, прежде чем выполнить это действие» гораздо лучше, чем «LOL! Что-то пошло не так».
Используй исключения для по-настоящему исключительных случаев
Если в твоём языке отсутствуют исключения, радуйся! В любом случае, тип Either
и его когорты, встречающиеся в функциональных языках, гораздо лучше в предоставлении осмысленных состояний ошибки.
Исключениями, как правило, склонны злоупотреблять в мире Java. Исключения созданы для обработки действительно исключительных случаев. Ты действительно не ожидал, что getUser
может не найти пользователя? Не выкидывай UserNotFoundException
. Вместо этого, возвращай подходящее состояние ошибки.
Впрочем, при реальной неудаче, лучше потерпеть её быстро.
Как говорит Джейк Вартон: «Единственное, что может быть хуже падающей программы — программа, которая не падает и продолжает работать в неопределённом состоянии».
Как спроектировать API: документируй всё
Документация — это скучно. И, как многие скучные вещи, очень важна. Хорошая документация спасёт твою психику. Ты сможешь избежать постоянных вопросов от потребителей твоего API, что само по себе расценивается на вес золота.
Хорошая документация должна состоять из:
- Описание модуля на верхнем уровне, и как он работает.
- Публичные методы и протоколы описанные в Javadocs, Heredocs, Rdocs или т.п.
- Пример кода, показывающий как его использовать.
Не все абстракции требуют одного и того же уровня документации. Небольшому классу, например, не нужен пример кода.
Документация также должна эволюционировать. Если ты получаешь кучу вопросов об одном и том же, то можешь добавить это в документацию для будущих потребителей.
Слишком много документации также является пустой тратой времени. Так как это создаёт ещё один актив, который нужно держать в актуальном состоянии. И у него не будет никакой ценности, если его никто не использует.
Однако, целенаправленная и соответствующая документация — это всегда полезно.
Пиши тесты
Тесты — это доказательство правильности, документация и пример кода в одном флаконе. Они несут огромную ценность при рефакторинге и позволяют тебе двигаться быстро и уверенно, когда ты что-нибудь меняешь.
Потребители, которые хотят глубже вникнуть в твою реализацию, всегда могут прочитать тесты, чтобы больше узнать о цели и внутреннем поведении кода. Документация не может охватить всё, и вот тут помогают тесты.
«Зачем тогда вообще писать документацию, если у меня есть тесты?» — можешь спросить ты. Позволю себе такую отдалённую аналогию: если документация, это руководство пользователя к твоему API, тогда тесты — это инструкция под опкод x86.
Делай API тестируемым
Тестирование своего собственного кода — это одно. Написать API, который с лёгкостью позволит тестировать свой код тем, кто его использует— совсем другое. API, которые трудно имитировать / использовать как заглушку в тест-кейсах, будут отталкивать разработчиков, уделяющих внимание тестам.
Ты можешь использовать параметры конфигурации для версий отладки и релиза, где это возможно. Зачастую, некоторые вещи должны вести себя по-разному в условиях Непрерывной интеграции / развёртывания, нежели чем в продакшене. Обрати на это внимание.
Дай пользователям возможность выбирать
Каждый потребитель захочет использовать твой API по-своему. Одни захотят чтобы он работал синхронно. Другие предпочтут асинхронные обратные вызовы (callback), future, promise или Rx observable.
Позволь потребителям выбирать то, что они хотят, на сколько это возможно. Чем проще твой API интегрируется в их существующую программную и системную среду, тем более вероятно, что люди будут использовать его.
Не давай пользователям слишком большой выбор
Не давай потребителям слишком широкий простор для выбора, чтобы в итоге их не охватил аналитический паралич. Всегда стремись обеспечить осмысленные значения по умолчанию. Большую часть времени, твой API будет использован определённым образом. Так пусть дефолтные настройки обеспечивают такое поведение.
API должен поощрять каноническое поведение. Не позволяй потребителям изменить какие-то случайные параметры в твоём модуле, если это не часть функционала. Если ты откроешь какое-то непреднамеренное поведение — можешь быть уверен, что когда-нибудь оно будет использовано, порождая непредсказуемые последствия.
Будь упрямым. Не теряй фокус, давая слишком много вариантов. Способность выбрать между верным количеством опций и верной степенью гибкости требует практики и опыта. Если сомневаешься: заблуждение на той стороне, где меньше вариантов.
Как спроектировать API: заключение
Проектирование API — это искусство. Будем надеяться, что советы, изложенные здесь, помогут тебе в написании лучшего кода. Вероятно, я упустил много других вещей, но эти служили мне хорошо. Живи и учись.