ИТ развој

Beyond the README: Engineering API Contracts for True Developer Collaboration

Надвор од README: Инженеринг на API договори за вистинска соработка меѓу развивачите

API не е збирка од крајни точки. Тоа е договор меѓу луѓе, услуги и распореди за изданија кои ретко се движат со иста брзина. README може да го претстави тој договор, но не може самостојно да го носи. Вистинската соработка започнува кога договорот е доволно прецизен за да ги насочува имплементацијата, тестирањето, распоредувањето и промените.

Ова е особено важно во заднинските системи, каде што мала нејаснотија може да се прошири далеку. Дали изоставено поле се разликува од null? Дали неуспешно ажурирање го остава ресурсот непроменет? Дали клиентите можат безбедно да повторат барање по истек на временското ограничување? Ова се прашања за договорот, а не за дотерување на документацијата.

Дефинирајте однесување, не само структури

Многу API описи запираат на примери за барања и одговори. Тие примери се корисни, но на програмерите им требаат правилата околу нив: граници на валидација, семантика на грешки, подредување, однесување при пагинација, исходи од авторизација и очекувања за конкурентност.

Разгледајте PHP крајна точка што создава фактура. Шемата на барањето може да наведува дека customerId и amount се задолжителни. Употребливиот договор мора исто така да наведе дали клиентот мора да припаѓа на организацијата на повикувачот, кои правила за валута важат, дали дупликат-поднесувања создаваат дупликат-фактури и кои неуспеси се грешки на клиентот наспроти грешки на серверот.

final class CreateInvoiceRequest
{
    public function __construct(
        public readonly string $customerId,
        public readonly int $amountInCents,
        public readonly string $currency,
        public readonly string $idempotencyKey,
    ) {}
}

Присуството на клуч за идемпотентност сигнализира важна обврска во однесувањето: клиентот може да повтори обид без случајно да наплати или да создаде втора фактура. Тоа ветување мора да биде поддржано со дизајн на перзистентност и трансакции, а не само да се прифати како заглавие и да се игнорира.

Направете ги одговорите при неуспех предвидливи

Клиентите ја градат сопствената логика околу грешките. Ако една крајна точка враќа детали за валидација како низа, друга враќа низа знаци, а трета враќа HTML од обработувач на исклучоци, интеграцијата станува одбранбено нагаѓање.

Стабилна обвивка за грешки им овозможува на тимовите за клиенти да разликуваат невалиден внес, недостапни ресурси, забранети дејства и привремени неуспеси. Точните имиња на полињата се помалку важни од доследноста.

{
  "error": {
    "code": "validation_failed",
    "message": "Барањето содржи невалидни полиња.",
    "details": {
      "amountInCents": ["Мора да биде поголемо од нула."]
    }
  }
}

Не ветувајте поголема прецизност отколку што системот може да одржува. На пример, враќањето детални пораки за ограничувања на базата на податоци може да открие детали за имплементацијата и неочекувано да ги направи миграциите промени што го кршат договорот. Преведете ги инфраструктурните неуспеси во намерни одговори на ниво на апликација, додека дијагностичкиот контекст го задржувате во логовите на серверската страна.

Верзионирајте ги промените според влијанието, не според формалноста

Верзионирањето често се третира како избор меѓу URL префикс и медиумски тип. Поважната одлука е да се знае што се смета за промена што го крши договорот. Отстранувањето поле е јасно нарушувачко, но менувањето на стандардното подредување, стеснувањето на прифатениот внес, изменувањето на ограничувањата за пагинација или промената на код за грешка исто така може да ги наруши потрошувачите.

Додадените промени обично се побезбедни, но не и автоматски безопасни. Клиент што отфрла непознати JSON својства, го прикажува секое поле динамички или претпоставува фиксен сет од enum вредности сепак може да не успее кога одговорот се проширува. Добрите договори јасно ги наведуваат овие очекувања: клиентите треба да толерираат непознати полиња во одговорот, а серверите треба да отфрлаат или игнорираат непознати полиња во барањето според документирана политика.

Пред да промените воспоставена крајна точка, поставете директно прашање: може ли постоечки, правилно имплементиран клиент да продолжи да функционира без измена? Ако одговорот е не, обезбедете патека за миграција наместо да се надевате дека координацијата на изданијата ќе го реши тоа.

Застарувањето бара оперативен план

Застарена крајна точка не е завршена задача. Потребни ѝ се најавена замена, јасен рок кога е соодветно, следење за да се идентификува преостанатата употреба и сопственик одговорен за конечното отстранување. Без овие елементи, застарувањето станува траен неред, а договорот станува потежок за разбирање секој квартал.

Нека тестовите го спроведуваат договорот

Документацијата им кажува на луѓето што треба да се случи. Тестовите на договорот потврдуваат дека тоа сè уште се случува по рефакторирање, надградба на рамка или промена во базата на податоци. Ова е особено вредно во PHP апликации, каде што контролерите, валидацијата на барања, сериализаторите и middleware за исклучоци можат да се развиваат независно.

Фокусиран тест на договорот треба да тврди набљудливо однесување, наместо внатрешна структура на класи. За крајна точка за листа, тоа може да вклучува статусен код, задолжителни полиња во одговорот, врски за пагинација, однесување при авторизација и дефиниран одговор за празен резултат.

$response = $this->getJson('/api/orders?limit=20');

$response
    ->assertOk()
    ->assertJsonStructure([
        'data' => [['id', 'status', 'createdAt']],
        'meta' => ['nextCursor'],
    ]);

Користете ги овие тестови покрај единечните тестови, а не наместо нив. Единечните тестови брзо ги заштитуваат правилата на доменот. Интеграциските тестови и тестовите на договорот ги заштитуваат споевите каде што се среќаваат конфигурацијата на рамката, сериализацијата, автентикацијата и однесувањето на складирањето.

Дизајнирајте договори имајќи ги предвид базата на податоци и Docker околината

Договорот за API не може да се дизајнира изолирано од неговата работна околина. Нов филтер може да изгледа како безопасен параметар за пребарување сè додека не предизвика нескенирано пребарување во базата на податоци. Масовна крајна точка може да изгледа практична сè додека не ги надмине ограничувањата за барања или не држи трансакција отворена доволно долго за да создаде спор за заклучување.

Направете ги оперативните карактеристики дел од дискусијата за дизајнот. Дефинирајте разумни големини на страници, претпочитајте курсорска пагинација за големи и променливи збирки на податоци, намерно поставувајте временски ограничувања и идентификувајте дали операцијата е синхрона или асинхрона. Ако клиентот прими 202 Accepted, наведете како открива завршување и како изгледа неуспешна задача во заднина.

Локалните околини засновани на Docker помагаат кога репродуцираат значајни услови на договорот: истиот тип на база на податоци, семантика на редици, променливи на околината и однесување на обратен прокси што влијаат на извршувањето на API. Тие се помалку корисни кога само докажуваат дека контејнерот се стартува. Развојната усогласеност треба да се фокусира на однесување што може да го промени договорот.

Пишувајте за следниот соработник

Најефективната API документација одговара на прашања пред некој да мора да отвори нишка за разговор: што прави крајната точка, кој смее да ја повика, какви внесови прифаќа, што враќа, како не успева и што се случува при повторни обиди или конкурентни ажурирања.

  • Вклучете едно реалистично успешно барање и одговор.
  • Документирајте значајни неуспеси при валидација и авторизација.
  • Наведете стандардни вредности за подредување, филтрирање и пагинација.
  • Наведете очекувања за идемпотентност и повторни обиди за операции за запишување.
  • Забележете насоки за миграција покрај однесување што го крши договорот или е застарено.

Силен API договор не го елиминира разговорот. Тој го прави разговорот повреден, преместувајќи го од „што значи оваа крајна точка?“ во „дали ова е правилното однесување за производот?“ Тоа е исчекорот над README: третирање на интерфејсите како трајни инженерски одлуки, тестирани во код и одржувани со истата грижа како и системите зад нив.

Портрет на автор на блогот

Mihajlo

Јас сум Михајло - развивач поттикнат од љубопитност, дисциплина и постојаната желба да создадам нешто значајно. Споделувам увиди, упатства и бесплатни услуги за да им помогнам на другите да ја поедностават својата работа и да растат во постојано развивачкиот свет на софтверот и вештачката интелигенција.