Router adalah registri
Hanzo Cloud tidak punya berkas OpenAPI yang di-check-in. Tabel rute yang hidup adalah sumbernya, diproyeksikan dengan tiga cara, dan uji bijeksi menggagalkan build jika ada proyeksi yang menyimpang. 1,362 operasi pada 963 path, dihasilkan per proses pada saat permintaan.

Di halaman ini
Tidak ada openapi.yaml di repositori Hanzo Cloud. Tidak ada registri kedua yang mendaftar endpoint, tidak ada berkas anotasi, dan tidak ada langkah generator di CI yang menulis spesifikasi ke disk. Jika Anda ingin tahu apa API itu, tanyakan pada proses yang sedang berjalan, dan ia menjawab dengan membaca routernya sendiri.
Tulisan ini tentang mengapa pengaturan itulah yang akhirnya bertahan, dan tentang bagian-bagiannya yang tidak bekerja, yang ternyata merupakan setengah yang lebih berguna.
Satu tabel, tiga proyeksi
serve.go menyusun satu tabel rute. Tiga hal kemudian membacanya, semuanya setelah MountAll, supaya masing-masing melihat tabel yang lengkap:
/zapmemutar ulang handler/v1di atas transport ZAP.- Konsol merendernya.
GET /v1/openapi.jsonmendeskripsikannya.
Tidak satu pun dari ketiganya menyimpan salinan apa pun. Itulah seluruh kendala desain, dan segala yang lain mengikutinya. Proyeksi yang menyimpan daftar endpoint sendiri akan menjadi sumber kebenaran kedua, dan sumber kebenaran kedua menyimpang, bukan kadang-kadang, selalu, pada jadwal yang ditentukan oleh seberapa sering seseorang lupa.
Proyeksi deskripsi adalah yang ditanyakan orang, jadi: openapi.Live(app) memanggil app.Fiber().GetRoutes(true), dan setiap fungsi lain di openapi/ adalah fungsi murni dari []Route yang kembali. Dokumen tidak dibangun dari router. Dokumen itu adalah router, dalam bentuk lain.
Mengapa membaca router yang hidup adalah satu-satunya sumber yang utuh
Alternatif yang jelas adalah memindai sumber. Itu tidak berhasil, dan alasannya layak dinyatakan dengan tepat, karena itulah alasan banyak perkakas API diam-diam melaporkan kurang.
POST /v1/kms/auth/login didaftarkan seperti ini:
Group("/v1/kms/auth").Post("/login")
Tidak ada grep yang menemukan path itu. String /v1/kms/auth/login tidak ada di basis kode. Ia hanya ada di router yang sudah dirakit, saat runtime, setelah prefiks grup dan daun digabung oleh framework.
Lalu menjadi lebih buruk, dengan cara yang sebenarnya benar. Himpunan rute adalah fungsi dari konfigurasi deployment, cfg.Enabled, plus gerbang internal seperti if kc != nil milik KMS. Deployment yang tidak memasang admin tidak menyajikan rute admin. Jadi spesifikasi yang jujur untuk deployment itu tidak mengiklankannya.
Inilah mengapa dokumen dihasilkan per proses, pada saat permintaan, bukan dibangun sekali di CI. Spesifikasi yang dibangun di CI menggambarkan deployment maksimal yang hipotetis, yang mungkin bukan yang sedang Anda ajak bicara. Spesifikasi yang dibaca dari router yang hidup menggambarkan proses yang menjawab Anda.
Pengujian yang membuatnya benar
Arsitektur tempat spesifikasi tidak dapat menyimpang adalah klaim, dan klaim butuh gerbang. Gerbang kami adalah bijeksi, di cmd/cloud/openapi_test.go:
func TestSpecDoesNotDriftFromLiveRouter(t *testing.T) {
Ia memasang setiap subsistem di apps.Wire(), menghasilkan dokumen, dan menegaskan pemetaan di kedua arah: setiap rute hidup muncul sebagai operasi, dan setiap operasi ditopang rute hidup. Bukan subset di salah satu arah.
Jalankan hari ini:
bijection holds over 1362 operations / 963 paths / 138 products
Ini satu-satunya pengujian di repositori yang kegagalannya berarti dokumen yang diterbitkan berbohong. Segala yang lain pecah membuat produk salah; yang ini membuat kontrak salah, dan itu lebih buruk, karena integrator sudah membangun terhadapnya.
Jumlah yang bergerak bukan masalah. Ia bergerak setiap kali subsistem ditambahkan. Angka yang penting adalah selisih kedua sisi, dan angka itu 0 atau build gagal.
Sumbu produk bersifat mekanis
Segmen path pertama setelah /v1/ adalah produk. openapi.Product membacanya dan menandai setiap operasi dengannya, yang memungkinkan CLI menyusun hanzo <product> <resource> <verb> tanpa ada yang menilai di mana sebuah perintah seharusnya tinggal.
Ini sengaja bukan nama subsistem. clients/billing juga melayani /v1/finance/*, dan URL adalah yang dilihat pemanggil, jadi URL yang menang. Menamai sumbu menurut paket internal akan membuat CLI artefak yang dikurasi, benda yang berisi pendapat dan harus dipelihara seseorang, alih-alih sebuah proyeksi.
Apa yang tidak dapat dikatakan router
Inilah batasnya, dan kami tidak bermaksud mengakalinya.
Metode, path, parameter path, dan produk dapat diturunkan dari router. Skema permintaan dan respons, parameter kueri dan header, kode status, serta auth bukan. Router menyimpan sebuah func(*zip.Ctx) error. Tipe permintaan adalah variabel lokal di dalam tubuh handler:
var req secretPutRequest
json.Unmarshal(ctx.Body(), &req)
Go tidak dapat merefleksi dari nilai fungsi ke dalam tubuhnya. Tidak ada perbaikan cerdik di sini, dan mencobanya di generator berarti menebak, dan begitulah spesifikasi memperoleh entri yang yakin tetapi salah.
Satu-satunya jalan ke skema adalah operasi bertipe:
zip.Get[In, Out](...)
In dan Out ada pada pendaftaran, jadi skema tersedia tanpa membaca pikiran siapa pun. Dan karena keduanya ada pada pendaftaran, entri yang sama juga menghasilkan alat MCP. zip/openapi.go dan zip/mcp.go adalah dua pembaca dari satu registri. Itulah proyeksi keempat, dan ia datang tanpa mekanisme keempat.
Migrasi bersifat inkremental menurut konstruksi: GetRoutes() adalah superset dari app.ops, jadi mengubah handler menjadi operasi bertipe menambah skema tanpa mengubah apa pun di hulunya.
Rute yang tidak dapat diberi tipe, dan mengapa
clients/integrations dikonversi sejauh yang diizinkan kawat: 20 dari 41 rute adalah operasi bertipe. 21 lainnya tetap handler mentah, dan masing-masing tercantum di peta rawRoutes dalam ops_projection_test.go beserta alasan ia tidak dapat pindah:
- 13 pengalihan peramban. Callback OAuth dan setiap kaki tautan menjawab
302. Operasi bertipe mengembalikan nilai, bukan headerLocationdan status di kisaran 300. - 6 webhook. HMAC Slack dan GitHub, Ed25519 Discord, JWT Teams Bot Framework, header token rahasia Telegram. Setiap satu mengautentikasi tanda tangan di atas byte permintaan mentah. Operasi bertipe menerima
Inyang sudah didekode dan tidak pernah melihat byte yang menjadi dasar tanda tangan, jadi memberi tipe pada ini akan merusak auth-nya. - 2 pembuat
202 Accepted.cloud.Createdmemberi kami sambungan 201. Tidak ada sambungan 202. Dua rute membutuhkannya, jadi dua rute menunggu.
Kami pikir daftar itu adalah artefak paling berharga di seluruh sistem. Ia adalah inventaris yang terenumerasi dan disematkan uji tentang tepat di mana abstraksi berhenti, dengan alasan per entri yang dapat diperdebatkan peninjau. Mode kegagalan yang dicegahnya adalah rute yang diam-diam kembali menjadi handler mentah dan lenyap dari dokumen, himpunan alat MCP, dan CLI sekaligus, tanpa terlihat, karena tidak ada yang menegaskan bahwa ia pernah ada di sana.
Arsitektur bukan bagian yang menggeneralisasi. Ia adalah bagian yang menggeneralisasi ditambah catatan yang jujur dan ditegakkan tentang bagian yang tidak.
Apa yang ini beli
Pemanggil yang berintegrasi dengan Hanzo Cloud membaca dokumen yang dihasilkan oleh proses yang menjawab permintaan mereka, di deployment yang benar-benar mereka ajak bicara, beberapa milidetik yang lalu. Agen yang memanggil lewat MCP mencapai handler yang sama, dengan pemeriksaan tenancy yang sama, seperti pemanggil REST. Panggilan /mcp anonim mendapat 403 yang sama dengan yang diberikan REST, karena itu jalur kode yang sama dan sebuah uji menyematkannya.
Tidak satu pun dari itu mengharuskan seseorang ingat untuk meregenerasi apa pun.
Bentuk umumnya: jika dua artefak harus sepakat, jangan hasilkan yang satu dari yang lain lalu berharap. Jadikan salah satunya pandangan dari yang lain, dan tulis uji yang gagal ketika keduanya tidak demikian.
Baca selengkapnya

Cloud-Native Commerce: Why Architecture Matters
How we built Crowdstart as a cloud-native platform from day one, and why this matters for commerce.

Menyiapkan perdagangan headless untuk era LLM yang bertindak sebagai agen
Pembeli berubah menjadi agen. Apa yang diperlukan agar sebuah bisnis dapat dibaca, ditandatangani, dan dibayar oleh LLM tanpa manusia di dalam lingkaran, dan apa yang Hanzo sediakan untuk itu hari ini.

Introducing Enso — one API, routed to the best frontier intelligence
Enso is Hanzo's learned router: no single frontier model leads every benchmark, so Enso routes each request to the model most likely to win it — and learns from your feedback as it goes. Microsecond routing on a CPU, a transparent per-request meter, and a 1% fee that pays for itself. Here is how it works, the measured results, and the cost-transparency angle a monolith cannot offer.