私たちは皆、ピカピカの新しいツールが大好きですが、常に更新するという面倒な作業は嫌います。これは、オペレーティング システム、アプリ、API、Linux パッケージなど、あらゆるものに当てはまります。更新によってコードが動作しなくなるのは苦痛ですが、更新が私たちによって開始されていない場合は、その苦痛は 2 倍になります。
Web API 開発では、新しい更新が行われるたびにユーザーのコードが破損する危険に常にさらされています。あなたの製品が API である場合、これらのアップデートは毎回恐ろしいものになります。 Monite の主な製品は、API とホワイトラベル SDK です。当社は API ファーストの企業であるため、API を安定して使いやすく保つことに細心の注意を払っています。したがって、互換性を損なう変更の問題は、優先順位リストの最上位に近いものとなります。
一般的な解決策は、クライアントに非推奨の警告を発行し、重大な変更はほとんどリリースしないことです。突然、リリースに数か月かかる場合があり、一部の機能は次のリリースまで非表示にしておくか、マージしないようにしなければなりません。これにより開発が遅くなり、ユーザーは数か月ごとに統合を更新する必要があります。
リリースを迅速化すると、ユーザーは統合を頻繁に更新しなければならなくなります。リリース間の期間が長くなると、企業としての動きが遅くなります。ユーザーにとって不便になればなるほど、あなたにとっても便利になりますし、その逆も同様です。これは確かに最適なシナリオではありません。私たちは、既存のクライアントに何も影響を与えることなく、自分たちのペースで進めたいと考えていました。これは、通常の非推奨アプローチでは不可能でした。これが、代替ソリューションである API バージョン管理.
を選択した理由です。これは非常に単純なアイデアです。重大な変更はいつでもリリースしますが、新しい API バージョンの下では非表示にします。これにより、両方の長所が得られます。ユーザーは統合が日常的に中断されることがなくなり、好きな速度で移動できるようになります。ユーザーは、プレッシャーを感じることなく、いつでも好きなときに移行できます。
アイデアのシンプルさを考慮すると、どの企業にも最適だと思います。これは、典型的なエンジニアリング ブログで期待される内容です。残念ながら、それはそれほど単純ではありません。
API のバージョン管理は非常に困難です。その幻想的な単純さは、実装を開始するとすぐに消えてしまいます。残念なことに、このトピックに関するリソースは驚くほど少ないため、インターネット上では実際に警告されることはありません。圧倒的多数の記事は API バージョンをどこに配置するかについて議論していますが、「どうやって 実装 するか?」という質問に答えようとしている記事はほとんどありません。最も一般的なものは次のとおりです:
個別のデプロイメントは非常に高価でサポートが困難になる可能性があり、単一ルートのコピーは大規模な変更にあまり対応できません。また、アプリケーション全体をコピーすると余分なコードが大量に作成されるため、ほんの数バージョンでコードに溺れてしまいます。
安いものを選ぼうとしても、すぐにバージョン管理の負担が追いついてしまいます。最初は単純に感じるでしょう。ここに別のスキーマを追加し、そこにビジネス ロジックの別のブランチを追加し、最後にいくつかのルートを複製します。しかし、十分なバージョンがあると、ビジネス ロジックはすぐに管理できなくなり、多くの開発者がアプリケーションのバージョンと API のバージョンを間違えて、データベース内のデータのバージョン管理を開始し、アプリケーションを保守できなくなります。
同時に 2 つまたは 3 つ以上の API バージョンを使用しないことを望むかもしれません。古いバージョンは数か月ごとに削除できるようになります。これは、少数の内部消費者のみをサポートする場合に当てはまります。しかし、組織外のクライアントは、数か月ごとにアップグレードを強制される経験を楽しむことはできません。
API のバージョン管理はすぐにインフラストラクチャの最も高価な部分の 1 つになる可能性があるため、事前に入念な調査を行うことが重要です。内部コンシューマのみをサポートする場合は、GraphQL などを使用すると簡単に済むかもしれませんが、すぐにバージョン管理と同じくらい高価になる可能性があります。
スタートアップの場合は、API のバージョン管理を、適切に行うためのリソースがある開発の後期段階まで延期することが賢明です。それまでは、非推奨化と追加的な変更戦略で十分かもしれません。 API の見栄えが必ずしも優れているとは限りませんが、明示的なバージョン管理を避けることで、少なくとも大幅なコストを節約できます。
数回の試行と多くのエラーの後、私たちは岐路に立たされました。上で述べた以前のバージョン管理アプローチは維持するにはコストが高すぎました。苦闘の結果、私は完璧なバージョニング フレームワークに必要な次の要件リストを考案しました。
残念ながら、既存のアプローチに代わる選択肢はほとんどありませんでした。このとき、クレイジーなアイデアが頭に浮かびました。洗練されたもの、仕事に最適なもの、つまり Stripe の API バージョニングのようなものを構築してみたらどうなるでしょうか?
無数の実験の結果、現在は Cadwyn を使用しています。これは、Stripe のアプローチを実装するだけでなく、その上に大幅に構築されたオープンソースの API バージョン管理フレームワークです。 Fastapi と Pydantic の実装について説明しますが、中心となる原則は言語とフレームワークに依存しません。
他のすべてのバージョン管理アプローチの問題は、重複が多すぎることです。コントラクトのごく一部が壊れているだけなのに、なぜルート、コントローラー、さらにはアプリケーション全体を複製するのでしょうか?
Cadwyn を使用すると、API メンテナが新しいバージョンを作成する必要があるたびに、最新のスキーマ、モデル、ビジネス ロジックに重大な変更を適用できます。次に、バージョン変更 (新しいバージョンと以前のバージョンのすべての違いをカプセル化するクラス) を作成します。
たとえば、以前はクライアントがアドレスを持つユーザーを作成できましたが、今後は単一のアドレスではなく複数のアドレスを指定できるようにしたいとします。バージョンの変更は次のようになります:
class ChangeUserAddressToAList(VersionChange): description = ( "Renamed `User.address` to `User.addresses` and " "changed its type to an array of strings" ) instructions_to_migrate_to_previous_version = ( schema(User).field("addresses").didnt_exist, schema(User).field("address").existed_as(type=str), ) @convert_request_to_next_version_for(UserCreateRequest) def change_address_to_multiple_items(request): request.body["addresses"] = [request.body.pop("address")] @convert_response_to_previous_version_for(UserResource) def change_addresses_to_single_item(response): response.body["address"] = response.body.pop("addresses")[0]
instructions_to_maigrate_to_previous_version は、スキーマの古い API バージョンのコードを生成するために Cadwyn によって使用されます。2 つのコンバータ関数は、必要な数のバージョンを維持できるようにするための秘訣です。プロセスは次のようになります:
API メンテナがバージョン変更を作成した後、それを VersionBundle に追加して、この VersionChange が一部のバージョンに含まれることを Cadwyn に伝える必要があります:
VersionBundle( Version( date(2023, 4, 27), ChangeUserAddressToAList ), Version( date(2023, 4, 12), CollapseUserAvatarInfoIntoAnID, MakeUserSurnameRequired, ), Version(date(2023, 3, 15)), )
以上です。重大な変更を追加しましたが、ビジネス ロジックは 1 つのバージョン (最新) のみを処理します。数十の API バージョンを追加した後でも、ビジネス ロジックにはバージョン管理ロジック、定期的な名前変更、if およびデータ コンバーターが必要ありません。
バージョンの変更は API のパブリック インターフェイスに依存しており、既存の API バージョンに重大な変更を追加することはほとんどありません。これは、一度リリースしたバージョンは壊れないことを意味します。
バージョンの変更はバージョン内の重大な変更を記述し、古いバージョンには重大な変更がないため、バージョンの変更は完全に不変であると確信できます。変更する理由はありません。不変エンティティは常に進化するため、ビジネス ロジックの一部である場合よりも保守がはるかに簡単です。バージョンの変更も次々に適用され、バージョン間にトランスフォーマーのチェーンが形成され、リクエストを新しいバージョンに移行したり、レスポンスを古いバージョンに移行したりできます。
API コントラクトは、単なるスキーマやフィールドよりもはるかに複雑です。これらは、すべてのエンドポイント、ステータス コード、エラー、エラー メッセージ 、さらにはビジネス ロジックの動作で構成されます。 Cadwyn は、上で説明したのと同じ DSL を使用してエンドポイントとステータス コードを処理しますが、エラーとビジネス ロジックの動作は別の話です。DSL を使用して記述することは不可能であり、ビジネス ロジックに埋め込む必要があります。
このようなバージョンの変更はビジネス ロジックに影響を与えるため、他のバージョンの変更よりも維持コストがはるかに高くなります。私たちはこの特性を「副作用」と呼び、メンテナンスの負担を考慮して、いかなる犠牲を払ってでも副作用を回避しようとします。ビジネス ロジックを変更するすべてのバージョン変更は、副作用があるものとしてマークする必要があります。これは、どのバージョン変更が「危険」であるかを知る方法として役立ちます:
class RequireCompanyAttachedForPayment(VersionChangeWithSideEffects): description = ( "User must now have a company_id in their account " "if they want to make new payments" )
API メンテナーは、クライアント リクエストが次の副作用を含む API バージョンを使用しているかどうかを確認することもできます:
if RequireCompanyToBeAttachedForPayment.is_applied: validate_company_id_is_attached(user)
Cadwyn には多くの利点があります。開発者の負担が大幅に軽減され、インフラストラクチャに統合して変更ログを自動的に生成し、API ドキュメントを改善できます。
しかし、バージョン管理の負担は依然として存在しており、洗練されたフレームワークであっても特効薬ではありません。 API のバージョン管理は絶対に必要な場合にのみ使用するよう最善を尽くしています。また、特別な「API Council」を設置することで、最初の試行で API が正しくなるように努めています。すべての重要な API 変更は、実装が開始される前に、当社の優秀な開発者、テスター、技術ライターによってそこでレビューされます。
Stripe での API バージョン管理に関する記事と、Cadwyn を実装する際に私に手を差し伸べてくれた Brandur Leach に特別に感謝します。彼の助けなしでは不可能でした。
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3