Versi API di Monite

WBOY
WBOYasal
2024-08-29 20:30:31848semak imbas

Kita semua suka mempunyai alatan baharu yang berkilat tetapi tidak suka tugas untuk sentiasa mengemas kininya. Ini terpakai kepada apa sahaja: sistem pengendalian, apl, API, pakej linux. Ia menyakitkan apabila kod kami berhenti berfungsi kerana kemas kini dan ia adalah dua kali ganda kesakitan apabila kemas kini itu tidak dimulakan oleh kami.

Dalam pembangunan API web, anda sentiasa berisiko untuk memecahkan kod pengguna anda dengan setiap kemas kini baharu. Jika produk anda ialah API, maka kemas kini ini akan menakutkan setiap masa. Produk utama Monite ialah API kami dan SDK label putih kami. Kami adalah syarikat yang mengutamakan API jadi kami mengambil berat untuk memastikan API kami stabil dan mudah digunakan. Oleh itu, masalah pecah perubahan berada di bahagian atas senarai keutamaan kami.

Penyelesaian biasa ialah mengeluarkan amaran penamatan kepada pelanggan anda dan jarang mengeluarkan perubahan yang melanggar. Tiba-tiba, keluaran anda kini boleh mengambil masa berbulan-bulan dan beberapa ciri perlu kekal tersembunyi atau tidak digabungkan sehingga setiap keluaran seterusnya. Ini memperlahankan pembangunan anda dan memaksa pengguna anda mengemas kini penyepaduan mereka setiap beberapa bulan.

Jika anda membuat keluaran lebih cepat, pengguna anda perlu mengemas kini penyepaduan mereka terlalu kerap. Jika anda memanjangkan masa antara keluaran, anda akan bergerak lebih perlahan sebagai sebuah syarikat. Lebih menyusahkan anda untuk pengguna -- lebih mudah untuk anda, dan begitu juga sebaliknya. Ini sememangnya bukan senario yang optimum. Kami mahu bergerak mengikut kadar kami sendiri tanpa melanggar apa-apa untuk pelanggan sedia ada yang mustahil dengan pendekatan penamatan biasa. Inilah sebabnya kami memilih penyelesaian alternatif: Versi API.

Idea yang agak mudah: lepaskan sebarang perubahan yang rosak pada bila-bila masa tetapi sembunyikannya di bawah versi API baharu. Ia memberikan anda yang terbaik dari kedua-dua dunia: Pengguna tidak akan mempunyai integrasi mereka secara rutin dipecahkan dan anda akan dapat bergerak pada sebarang kelajuan yang anda suka. Pengguna akan berhijrah pada bila-bila masa yang mereka mahu -- tanpa sebarang tekanan.

Memandangkan kesederhanaan idea itu, rasanya sesuai untuk mana-mana syarikat. Itulah yang anda harapkan untuk membaca dalam blog kejuruteraan biasa. Malangnya, ia tidak begitu mudah.

Berhati-hati dengan harga

Versi API adalah sukar, sangat sukar. Kesederhanaan ilusinya cepat pudar sebaik sahaja anda mula melaksanakannya. Malangnya, internet tidak pernah memberi amaran kepada anda kerana terdapat sedikit sumber mengenai topik itu. Majoriti mutlak daripada mereka berhujah tentang tempat untuk meletakkan versi API tetapi hanya beberapa artikel yang jarang cuba menjawab: "Bagaimana kita melaksanakannya?". Yang paling biasa ialah:

  • meletakkan versi berbeza aplikasi web yang sama ke dalam penempatan berasingan
  • menyalin laluan tunggal yang telah berubah antara versi
  • menyalin keseluruhan aplikasi versi untuk setiap versi

Penyerahan yang berasingan boleh menjadi sangat mahal dan sukar untuk disokong, menyalin laluan tunggal tidak berskala sangat baik kepada perubahan besar, dan menyalin keseluruhan aplikasi menghasilkan begitu banyak kod tambahan yang anda akan mula tenggelam di dalamnya selepas hanya beberapa versi.

Walaupun anda cuba memilih yang paling murah, beban versi akan menyusul tidak lama lagi. Pada mulanya, ia akan berasa mudah: tambahkan skema lain di sini, cabang lain dalam logik perniagaan di sana, dan salin beberapa laluan pada penghujungnya. Tetapi memandangkan versi yang mencukupi, logik perniagaan anda akan menjadi tidak terurus dengan cepat, ramai pembangun anda akan tersalah anggap versi aplikasi dan versi API, dan akan mula membuat versi data dalam pangkalan data anda dan aplikasi anda akan menjadi mustahil untuk dikekalkan.

Anda mungkin berharap bahawa anda tidak akan mempunyai lebih daripada dua atau tiga versi API pada masa yang sama; bahawa anda akan dapat memadamkan versi lama setiap beberapa bulan. Memang benar jika anda hanya menyokong sebilangan kecil pengguna dalaman. Tetapi pelanggan di luar organisasi anda tidak akan menikmati pengalaman dipaksa untuk meningkatkan setiap beberapa bulan.

Versi API boleh menjadi salah satu bahagian infrastruktur anda yang paling mahal dengan cepat, jadi penting untuk melakukan penyelidikan yang gigih terlebih dahulu. Jika anda hanya menyokong pengguna dalaman, maka anda mungkin mempunyai masa yang lebih mudah dengan sesuatu seperti GraphQL tetapi ia boleh menjadi sama mahal dengan pembuatan versi.

Jika anda seorang pemula, adalah bijak untuk menangguhkan versi API sehingga peringkat akhir pembangunan anda apabila anda mempunyai sumber untuk melakukannya dengan betul. Sehingga itu, penamatan dan strategi perubahan aditif mungkin mencukupi. API anda tidak akan sentiasa kelihatan hebat tetapi sekurang-kurangnya anda akan menjimatkan banyak wang dengan mengelakkan versi eksplisit.

Bagaimanakah kami melaksanakan versi API?

Selepas beberapa percubaan dan banyak kesilapan, kami berada di persimpangan: pendekatan versi terdahulu kami yang kami nyatakan di atas terlalu mahal untuk diselenggara. Hasil daripada perjuangan kami, saya telah mencipta senarai keperluan berikut yang diperlukan untuk rangka kerja versi yang sempurna:

  1. "Mengekalkan sejumlah besar versi adalah mudah" untuk memastikan versi tidak melambatkan pembangunan ciri kami
  2. "Memadam versi lama adalah mudah" untuk memastikan kami boleh membersihkan pangkalan kod kami tanpa usaha
  3. "Mencipta versi baharu bukanlah terlalu mudah" untuk memastikan pembangun kami masih diberi insentif untuk cuba menyelesaikan masalah tanpa versi.
  4. "Mengekalkan log perubahan antara versi adalah mudah" untuk memastikan bahawa kami dan pelanggan kami sentiasa yakin tentang perbezaan sebenar antara versi

Malangnya, terdapat sedikit atau tiada alternatif kepada pendekatan sedia ada kami. Pada masa inilah idea gila muncul di fikiran saya: bagaimana jika kita mencuba dan membina sesuatu yang canggih, sesuatu yang sesuai untuk kerja itu -- sesuatu seperti versi API Stripe?

Hasil daripada banyak percubaan, kami kini mempunyai Cadwyn: rangka kerja versi API sumber terbuka yang bukan sahaja melaksanakan pendekatan Stripe tetapi membina dengan ketara di atasnya. Kami akan bercakap tentang pelaksanaan Fastapi dan Pydantic tetapi prinsip terasnya ialah bahasa dan rangka kerja agnostik.

Bagaimana Cadwyn berfungsi

Perubahan Versi

Masalah semua pendekatan versi lain ialah kami terlalu banyak menduplikasi. Mengapakah kami akan menduplikasi keseluruhan laluan, pengawal atau juga aplikasi apabila hanya sebahagian kecil kontrak kami dipecahkan?

Dengan Cadwyn, setiap kali penyelenggara API perlu membuat versi baharu, mereka menggunakan perubahan penting pada skema, model dan logik perniagaan mereka yang terkini. Kemudian mereka membuat perubahan versi -- kelas yang merangkumi semua perbezaan antara versi baharu dan versi terdahulu.

Sebagai contoh, katakan sebelum ini pelanggan kami boleh mencipta pengguna dengan alamat tetapi kini kami ingin membenarkan mereka menentukan berbilang alamat dan bukannya satu alamat. Perubahan versi akan kelihatan seperti ini:

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_migrate_to_previous_version digunakan oleh Cadwyn untuk menjana kod untuk versi API yang lebih lama bagi skema dan kedua-dua fungsi penukar ialah helah yang membolehkan kami mengekalkan seberapa banyak versi yang kami mahu. Prosesnya kelihatan seperti berikut:

  1. Cadwyn menukar semua permintaan pengguna daripada versi API lama kepada versi API terkini dengan menggunakan change_address_to_multiple_items converter dan menyalurkannya ke logik perniagaan kami
  2. Logik perniagaan, respons APInya dan model pangkalan data sentiasa disesuaikan dengan versi API terkini (sudah tentu, ia mesti masih menyokong ciri lama walaupun ia dikeluarkan dalam versi baharu)
  3. Selepas logik perniagaan menghasilkan respons, Cadwyn menukarnya kepada versi API yang lebih lama yang sedang dihidupkan oleh peminta pelanggan dengan menggunakan penukar_tukar_alamat_kepada_item_tunggal.

Selepas penyelenggara API kami membuat perubahan versi, mereka perlu menambahkannya pada VersionBundle kami untuk memberitahu Cadwyn bahawa VersionChange ini akan disertakan dalam beberapa versi:

VersionBundle(
    Version(
        date(2023, 4, 27),
        ChangeUserAddressToAList
    ),
    Version(
        date(2023, 4, 12),
        CollapseUserAvatarInfoIntoAnID,
        MakeUserSurnameRequired,
    ),
    Version(date(2023, 3, 15)),
)

Itu sahaja: kami telah menambah perubahan besar tetapi logik perniagaan kami hanya mengendalikan satu versi -- terkini. Walaupun selepas kami menambah berdozen versi API, logik perniagaan kami masih bebas daripada logik versi, penamaan semula berterusan, if's dan penukar data.

Rantaian Versi

Perubahan versi bergantung pada antara muka awam API dan kami hampir tidak pernah menambah perubahan pecah ke dalam versi API sedia ada. Ini bermakna sebaik sahaja kami mengeluarkan versi – ia tidak akan rosak.

Oleh kerana perubahan versi menerangkan perubahan pecah dalam versi dan tiada perubahan pecah dalam versi lama, kami boleh yakin bahawa perubahan versi kami tidak berubah sepenuhnya – perubahan itu tidak akan mempunyai sebab untuk berubah. Entiti tidak berubah adalah lebih mudah untuk dikekalkan berbanding jika mereka adalah sebahagian daripada logik perniagaan kerana ia sentiasa berkembang. Perubahan versi juga digunakan satu demi satu -- membentuk rantaian transformer antara versi yang boleh memindahkan sebarang permintaan kepada mana-mana versi yang lebih baharu dan sebarang respons kepada mana-mana versi yang lebih lama.

API Versioning at Monite

Kesan sampingan

Kontrak API jauh lebih kompleks daripada sekadar skema dan medan. Ia terdiri daripada semua titik akhir, kod status, ralat, ralat mesej dan juga gelagat logik perniagaan. Cadwyn menggunakan DSL yang sama yang kami nyatakan di atas untuk mengendalikan titik akhir dan kod status tetapi ralat dan gelagat logik perniagaan adalah cerita yang berbeza: ia adalah mustahil untuk diterangkan menggunakan DSL, ia perlu dibenamkan ke dalam logik perniagaan.

This makes such version changes much more expensive to maintain than all others because they affect business logic. We call this property a "side effect" and we try to avoid them at all costs because of their maintenance burden. All version changes that want to modify business logic will need to be marked as having side effects. It will serve as a way to know which version changes are "dangerous":

class RequireCompanyAttachedForPayment(VersionChangeWithSideEffects):
    description = (
        "User must now have a company_id in their account "
        "if they want to make new payments"
    )

It will also allow API maintainers to check that the client request uses an API version that includes this side effect:

if RequireCompanyToBeAttachedForPayment.is_applied:
    validate_company_id_is_attached(user)

No silver bullets

Cadwyn has many benefits: It greatly reduces the burden on our developers and can be integrated into our infrastructure to automatically generate the changelog and improve our API docs.

However, the burden of versioning still exists and even a sophisticated framework is not a silver bullet. We do our best to only use API versioning when absolutely necessary. We also try to make our API correct on the first try by having a special "API Council". All significant API changes are reviewed there by our best developers, testers, and tech writers before any implementation gets moving.

Special thanks to Brandur Leach for his API versioning article at Stripe and for the help he extended to me when I implemented Cadwyn: it would not be possible without his help.

Atas ialah kandungan terperinci Versi API di Monite. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn