API як продукт: досвід YouControl

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

Я Дмитро Фандоренко, CTO компанії YouControl. Працюю із .NET з часів 2-ї версії, коли весь стек працював суто під Windows. На щастя, технологія розвивалася в потрібному напрямі, і нині ми з командою використовуємо .NET Core 3.1. Працюємо тільки під *nix-системами та починаємо перехід на п’ятий .NET. Наші бекенд-програми охоплюють як різноманітні сервіси-демони, так і вебсайти і вебапі, написані на C#. Про один з таких проєктів я далі й розповім.

API: окремий продукт в портфелі компанії

API-as-a-Product — це відносно нова концепція в софтверному середовищі. Для компаній, що сформувалися з веб- чи мобільних продуктів, побудова навколо API іншої бізнес-моделі може бути реальним викликом.

YouControl не був винятком, адже наш основний продукт — це вебплатформа для перевірки контрагентів: бізнес-партнерів, клієнтів, постачальників, потенційних працівників за допомогою відкритих даних та їхньої аналітики. Іншими словами, все те, що на Заході називають Customer Due Diligence та AML. До створення АРІ ми дійшли лише через три роки після запуску вебпродукту.

Якщо навколо вебплатформи нам вдалось консолідувати кілька тисяч користувачів, то API як продукт виявився значно складніший для продажу та просування. Велику роль в цьому відіграють саме внутрішні фактори та брак системного підходу до API як окремого продукту.

Останні пів року ми регулярно проводимо корпоративні опитування, щоб заміряти, як «почувається» API та процеси, пов’язані з ним усередині компанії. Ми запитуємо колег, чи вважають вони API нашим окремим продуктом. При першому опитуванні так вважало лише 37% працівників, сьогодні це вже 61%. Наша мета — 95%. Навіщо це?

API та вебпродукт мають усе ж відмінні цінності для клієнта. У API: користувач, потреба і «біль», процес розробки та внесення змін, продаж та просування, метрики, підтримка. В такому випадку API треба менеджити як окремий продукт, формувати для нього продуктову стратегію.

Потреба та користувач

На B2B-ринку API — це природне продовження продуктової лінійки. Адже у кожної компанії різні внутрішні процеси, задовольнити потреби всіх неможливо. API дає можливість клієнту отримати цілком кастомізоване рішення, яке ідеально і безшовно вписуватиметься у всю інфраструктуру наявних інструментів.

Якщо вебпродуктом здебільшого користується людина, що виконує роль комплаєнс-офіцера, то користувачем API є розробник. Головним замовником/бенефіціаром може бути як ІТ-департамент, так і інший відділ, який має на меті автоматизувати перевірку.

«Біль», який знімає вебпродукт, — це можливість швидко і в зручному інтерфейсі побачити всю консолідовану інформацію з відкритих джерел про компанію чи фізичну особу. API ж допомагає усунути з процесу перевірки людський фактор. Завдяки цьому клієнти можуть заощадити ресурси, уникнути людської помилки, ухвалювати швидше рішення на основі власних правил і алгоритмів. А найголовніше — швидше масштабуватись без потреби залучати нових людей та організовувати їм навчання.

Інакшість користувача і проблеми, які вирішує продукт, впливає і на UX. В API є поняття DX (developer experience), тому роль класичного UX-дизайнера повинна виконувати саме технічна команда, яка розуміє користувача якнайкраще.

Метрики

Головною метрикою в API для нас є MAU (Monthly Active Users). Частка «Active» є надважливою, адже основна цінність API — в ефектах від його використання, а не у факті володіння ним. Тому звична для SaaS-метрика платних підписників (коли враховані клієнти, які заплатили й не користуються) є навіть антиметрикою у випадку з API.

Окрім клієнтів, є ще кількість транзакцій на клієнта і їх зростання. Це вказує на те, наскільки клієнт використовує можливості API. Метрики прив’язані до кількості транзакцій, тому результують у нашій мотиваційні моделі для відділу продажів.

Монетизація

Є різні варіанти монетизації API: upsale для вебпродукту, підписка, транзакційна модель, поєднання різних моделей. Ми для себе обрали мікс і продовжуємо експериментувати в цьому напрямі.

Те, чого ми навчились на власних помилках: модель має бути максимально зрозумілою і прозорою для того, хто не знає, що таке API, запити, ендпоінти тощо. З великою ймовірністю в B2B-продуктах рішення про закупівлю вашого API буде приймати не ІТ-фахівець чи аналітики. Саме тому цій людині має бути добре зрозуміло, який закласти бюджет, за що він/вона платить і яку з цього отримає вигоду.

Процес продажу та просування

На ринку поширена думка, що найкраще для продажу API мати Sales Engineer. Але що робити, якщо таких працівників у вас немає?

У нашій сфері потребу варто згенерувати у відділі комплаєнсу, фінансового моніторингу, топменеджменту тощо, лише тоді такий запит з’явиться в ІТ-департаменті. Це різні аудиторії, які часто мають зовсім різні критерії вибору і яким потрібна різна комунікація.

Одним з рішень для нас стало партнерство з ІТ-командами, які розробляють власні продукти чи створюють рішення для замовника. Ми запустили партнерську програму, пропонуємо наше API безкоштовно стартапам або знижену ціну компаніям-інтеграторам та розробникам софту.

Від теорії до практики: виклики та факапи під час розробки REST API

До появи YouScore (API від YouControl) у нас був невеликий набір API. Він працював лише синхронно, був частиною написаного на PHP сайту і не мав чіткої структури та логіки. Проте потреби низки клієнтів API забезпечував, і всі були задоволені, за винятком розробників, яким треба було його підтримувати.

Ідею створення нового продукту з вільним вибором технології і архітектури «з нуля» колеги зустріли з радістю і позитивом. Для бекенду був обраний .NET Core з його WebApi, архітектура — REST. Ми справедливо вирішили, що REST АРІ ідеально підходить через простоту розробки, наявність тонни документації щодо самого підходу, а також можливості концентруватися на даних, які ми віддаємо клієнту.

Основною ідеєю було створення саме REST-full API з «full», наскільки це було можливо. Ми свідомо відмовилися від HATEOAS, тому що це давало багато оверхедів і користь для кінцевого клієнта була вкрай сумнівною. Однак всі інші принципи REST ми все-таки спробували зберегти.

Який код віддати клієнту

З першою проблемою ми зіткнулися відразу. Попри наявність безлічі статей, офіційного стандарту для REST API не існує. Адже це архітектурний підхід, а не конкретний протокол. Але це не повинно було стати проблемою, адже у світі створено десятки тисяч API. Здавалося б, хтось вже написав неофіційні стандарти, де все ясно і однозначно. Проте, як виявилося — ні.

Перші питання з’явилися одразу при формуванні структури відповіді API: як віддавати порожній результат? Чи повинна відповідь бути 404 чи 204? Або ж просто 200 з порожньою відповіддю, з null-відповіддю, з порожнім масивом? З одного боку, питання дуже просте, а з іншого — API робиться для інтеграції третіми особами, тому все повинно бути максимально логічно.

Зрештою ми дійшли до такого рішення:

  • якщо запит йде на конкретний ресурс, тобто це запит за ідентифікатором виду /resource/666 — віддаємо 404;
  • якщо запит «пошуковий», тобто запит виду «поверніть нам сутності, які підходять під певні параметри», то віддаємо 200 з порожнім масивом.

Стосовно 404 були сумніви, бо не завжди клієнт може знати, чи є дані з контрагента, якого він запитує. Вважати це клієнтською помилкою (4**) може бути не завжди логічно. Понад те, як клієнтові це відрізнити від запиту на неіснуючий метод API? Але в підсумку перемогла логіка того, що запит все-таки йде на конкретний ресурс по ID, тому 404 — цілком коректна відповідь. Також усі помилки ми доповнюємо інформацією з їхнім типом та описом, тому клієнт не сплутає помилкове посилання на АРІ з відсутньою інформацією за запитом.

Вищеописана проблема здається дрібницею, проте тут ховається те, що йде червоною ниткою через всю нашу розробку API — не можна змінювати формат АРІ після релізу. Саме тому всі нюанси повинні бути опрацьовані заздалегідь, до останніх дрібниць. І ми, звичайно ж, цього не зробили.

Схема взаємодії клієнтів з АРІ та асинхронність

Друга проблема полягала в наших даних, а точніше в їхньому оновленні. Переважно всі ендпоінти надають інформацію щодо контрагента з певного реєстру. Тобто клієнт може або відкрити умовний ЄДР та отримати актуальну інформацію, або ж зробити запит на наше АРІ й отримати ту ж інформацію, тільки більш структуровану й нормалізовану.

Тобто ми віддаємо клієнту не просто дані з бази, а актуальні дані. У старому АРІ це вирішувалося тим, що безпосередньо при запиті ми переходили на реєстр і оновлювали інформацію. В результаті запит міг виконуватися від кількох секунд до таймауту, у випадку наявності проблем у нас або на стороні реєстру.

У новому АРІ ми вирішили використовувати так званий асинхронний підхід. Якщо дані не оновлювались, ми віддавали відповідь 202 і заповнювали відповідні хедери, щоб клієнт знав, коли й куди зробити наступний запит. Ми віддавали або 202, або 200 з актуальними даними, відповідно сам ендпоінт вже ставав не просто «дані за реєстром ЄДР», а «актуальні дані за реєстром ЄДР», бо саме за цим клієнт і приходив.

Проте одразу постало питання — що робити, якщо клієнту потрібна хоч якась інформація зараз, особливо якщо з реєстром проблеми і ми довго не можемо оновити дані? Ми вирішили у відповідь додати посилання на поточні дані. Тобто віддаємо 202, але в тілі відповіді статус — «Update in progress» і посилання на ті дані, які є у нас зараз. Це був той же ендпоінт, але з додатковою приміткою, що не треба йти на реєстр за оновленнями, а показати тільки те, що зараз є в базі. У підсумку маємо один ресурс, жодних дієслів у запитах на кшталт getActualData і гнучкість для випадків, коли оновлення затягується.

«Класне ж рішення» — подумали ми. Але клієнти бачили це по-іншому. Виявилося, що такий підхід заплутує користувачів. Адже коли клієнти дізналися, що в запиті є чарівний параметр, при якому дані віддаються відразу — почали використовувати його постійно. Навіть здійснюючи запити за щойно створеними контрагентами, за якими у нас ще не було даних. Відповідно запит з прапорцем показу старих даних повертав 404, і клієнти не могли зрозуміти, в чому ж проблема.

Попри те, що ми досі вважаємо цю логіку максимально простою, плюс щодо цього є документація, відкидати інформацію, що вона плутає клієнтів, не можна. Якби АРІ розроблялося знову, то, найімовірніше, ми б розділили ендпоінти для актуальних і старих даних.

Документація

Стара-добра істина говорить про те, що неважливо, які ви стандарти обрали, наскільки вони відрізняються від загальноприйнятих — головне їх послідовно дотримуватися. Не менш важливо, що ці стандарти повинні бути не тільки в голові у розробника, а й задокументовані.

Коли проєктом займалася одна людина, то не було проблем під час додавання нових ендпоінтів, адже вони всі були написані в одному стилі. Але коли до проєкту долучилось більше людей, то ми подумали, що код — це найкраща документація. Не будуть же розробники в проєкті, де щось зроблено в одному стилі, робити це ж в іншому?

Думаю, нашу помилку ви вже зрозуміли. Яким би не був класним розробник, він не зможе відразу прочитати весь код проєкту і знати, що одне поле треба віддавати так, а друге по-іншому. Отож ми зіткнулися з двома основними проблемами: найменування однакових полів в різних методах могло відрізнятися; логіка ендпоінтів могла бути відмінною одна від одної. Проте головний фейл у тому, що здебільшого після релізу ці речі вже не можна виправити.

Якщо по-різному реалізована пагінація — це проблема для інтеграторів, але не смертельна, то з логікою ендпоінтів усе набагато гірше. У нас є три алгоритми отримання даних:

  • перший — найпростішій, коли віддаємо дані з бази (один запит, одна відповідь — 200);
  • другий — для даних, які оновлюються раз на день (потрібно робити кілька запитів до отримання 200);
  • третій — коли дані оновлюються онлайн.

Для третього кейсу ми використовуємо логіку двох запитів. Спершу робиться запит на отримання даних і передається клієнтові унікальний номер запиту, а далі за цим номером уже запитуються дані, і ми віддаємо або 202 (якщо дані не встигли оновитися), або 200 (якщо все ок). Проте це не лише зламало мозок інтеграторам, а ще й нашу логіку білінгу, яка була суворо зав’язана на транзакціях. Через це клієнти не розуміли, коли транзакція платна, а коли ні.

Наші висновки? Документація важлива як зовнішня, так і внутрішня. У ній повинен бути загальний опис логіки та підходу в розробці, а ще детальні елементи, наприклад, словники назв полів.

Технічна частина

Як не дивно, але цей пункт був найбільш гладким, бо, по суті, в АРІ немає занадто складних і заплутаних речей. Переважно все навантаження лягає на базу даних, а навантаження на саме АРІ легко вирішується горизонтальним масштабуванням і балансуванням через NGINX або HAProxy.

Однак зовсім без проблем не обійшлося, тому з цим нам на допомогу прийшов REDIS. На проєкті ми його використовуємо для зберігання статистики запитів, щоб визначити, чи може клієнт зробити запит, чи треба списувати кошти тощо. REDIS дозволяє робити максимально швидкі запити і вирішує всі проблеми з продуктивністю для цього функціонала.

Проблеми починаються з падінням кластера, коли дані стають неузгодженими і ми втрачаємо статистику щодо запитів. Клієнтам приємно, але нам не особливо. Вирішення — перенесення найбільш критичного функціонала в інші БД, які краще забезпечують узгодженість даних.

Особливості Agile в API-розробці: що б ми зробили по-іншому

Ще один елемент, який в API дуже відрізняється від вебпродукту, — change management. Адже окрім рішення, яку зміну найкраще зробити, і швидкої імплементації (ми працюємо в Scrum), треба ці зміни впроваджувати так, щоб не зламати щось клієнтам.

Жартома кажемо, що у команди, яка працює над API, і лікаря є спільне головне правило — не нашкодити. У нас бувають ситуації, коли стоїмо перед вибором: ризикуємо зламати щось 3–5 клієнтам чи виправити попередні помилки для зручності всіх майбутніх клієнтів.

Наш теперішній підхід до цього: не відкидати потребу змін, але максимально мінімізувати ризики. Це можна досягти у тісній співпраці з клієнтами, якщо ви йдете одне одному назустріч. І ключем тут є правильна комунікація.

Наше завдання — максимально зменшити, а краще повністю прибрати ті помилки, які робимо ми самі. Тому, якщо подумати, що б ми зараз зробили по-іншому, то це вже згадана раніше документація, а також стандартизація підходу без жодних компромісів:

  • єдиний підхід до неймінгу (як ми формуємо назву ендпоінтів, однакові назви в аналогічних полях з різних методів, наявність словника скорочень стандартних назв);
  • використання словників для відповідних даних з поверненням не тексту, а id і додавання ендпоінту з цим словником (ми й так використовуємо цей підхід, але не скрізь. Через це доводиться додавати всі приклади в документацію, збільшувати вагу JSON, що повертається і взагалі плутати клієнтів, які не зрозуміють, у цьому полі суворо задані значення чи будь-які);
  • опис і проходження стандартної процедури перевірки нового ендпоінту, коли всі перевірки заздалегідь обговорені й ми тестуємо не тільки роботу, а й логіку та структуру відповіді.
👍ПодобаєтьсяСподобалось9
До обраногоВ обраному5
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Дуже гарна стаття, дякую. Дивно, чому в документації на api.youscore.com.ua/swagger/index.html не вказали на самому видному місці про

так званий асинхронний підхід

, напевно щоб загуглили і прочитали цю чудову статтю

Приятно читать статью о том, что кто-то думает над дизайном реста апи.
Потому что 99% «рест апи» считается любая бессистемная каша из кое-как накиданных хттп-ендпойнтов.
Соответственно, огромное количество разработчиков не представляет себе ни проблем дизайна, ни возможностей хорошего апи, отсюда страсти к разным говнорешениям типа графкл.

З приводу стандартів та респонс кодів, є багато конвенцій типу
opensource.zalando.com/restful-api-guidelines
github.com/microsoft/api-guidelines
cloud.google.com/apis/design

З приводу документації, є OpenAPI, яке потім конвертують у human readable html, і навіть на сайт одразу можна залити, та дати можливість юзерам зі сваггер юі руцями пощупати ))

З приводу backward compatibility та після релізу неможна міняти — версіонність наше все, прям спочатку. А наскільки воно юзабельно буде — то інтерв’ю з інтеграторами, дефайн тіпікал юс кейсов, бета версія і т д...

Через це клієнти не розуміли, коли транзакція платна, а коли ні.

А це цікава проблема, як її вирішили?

офіційного стандарту для REST API не існує

OData + OpenAPI specs codegen ?

Скорочу до змісту: якщо ми створили «розумне гнучке» API, скоротивши кількість «зайвих» слів, але клієнти не розуміють що треба вичитати TLDR RTFM щоб знайти ті зайві слова та все зрозуміти — то може не таке воно вже й розумне?

Підхід «то не продукт неправильний, то люди неправильні» нагадує мені одного ведмедя, якому неправильні бджоли давали неправильний мед при спробі збройного пограбування з проникненням, за попередньою змовою, групою осіб.

Если разрабытвается публичное апи, клиентами которых будут сотни разных интеграторов, то всегда найдутся люди, которым будет непонятно или что-то или вообще все, даже если 95% остальных интеграторов пищат от восторга. Поэтому в таких условиях, да, правильно — это так, как делаешь ты, а если кому-то что-то не нравится, то это «неправильный» клиент, и менять подходы в апи ради 1 клиента из 10 000 никто не будет.
Я занимался чем-то похожим и прекрасно понимаю, о чем говорит автор статьи.

Вот именно, что есть разница, когда 1 из 10 000 это клиент, которому откажут. И когда 1 из 10 000 — ты сам, отказавший 10 000 клиентам, из которых едва ли пятеро сказали тебе что не так, но ты их слушать не стал. Тебе ж виднее, «как лучше»

ты сам, отказавший 10 000 клиентам, из которых едва ли пятеро сказали тебе что не так, но ты их слушать не стал.

Сорян, но я не понял твою мысль.

когда 1 из 10 000

Обычно это клиент, который недоволен вообще всем, криворукий, или слишком много о себе думает.
Обычно это абсурдные предъявы типа «наш сисадмин не понимает вашего жсона и ендпойнтов, сделайте нам хмл и один урл для всех запросов» или «у нас нет айти отдела, только контрактор на 1с, напишите нам либу для 1с» или «наш единственный программист ушел и мы не хотим нанимать нового, как вы посмели поменять апи?!» или «ваш жсон ответа слишком длинный а наш программист вызывает ваше апи из скрипта в базе данных, а там ограничение строки 8000 символов, укоротите ваш ответ до 8000 символов максимум» или другая подоьная ахинея.

Я тебе больше скажу, если ты будешь писать хотя-бы приблизительно адекватеное апи, 99% интеграторов, повидавших некое дерьмо, будут тебя боготворить. Потому что есть с чем сравнивать. 95% апи коммерческих компаний, предлагаемое для интеграции — невообразимый треш. А 1% недовольных неадекватов будет всегда, даже если ты будешь валить идеальное апи как боженька.

Підписатись на коментарі