Misalnya kamu sudah membangun aplikasi to-do list lewat chat selama beberapa minggu. Sekarang bentuknya masih website — React, database, tidak neko-neko. Kamu ketik "buatkan versi Android-nya" lalu tekan enter. Inilah yang sebenarnya terjadi antara ketukan tombol itu dan file .aab yang muncul di folder unduhan kamu, karena kebanyakan platform tidak menunjukkan bagian ini, padahal bagian yang disembunyikan inilah sumber sebagian besar rasa sakitnya.
File .aab Android
Gradle yang mengambil alih lebih dulu. Modul native aplikasi kamu — akses kamera, penyimpanan lokal, plugin apa pun yang ikut ter-pull saat build — masing-masing mendeklarasikan versi NDK tempat mereka dikompilasi, dan deklarasi itu tidak selalu cocok satu sama lain. Saya pernah melihat modul yang dibangun dengan NDK r25 menolak untuk di-link dengan modul lain yang mengasumsikan r26, dan error yang muncul bukan "version mismatch", melainkan sesuatu tentang simbol yang hilang, tiga lapis di dalam file .so. Versi Kotlin lebih licik lagi: versi yang dipatok di dalam satu modul Gradle bisa diam-diam menimpa versi yang dideklarasikan di bagian atas skrip build kamu, dan build-nya tetap berhasil — hanya saja menghasilkan biner yang crash di versi Android tertentu ketika sudah dipakai pengguna. Semua ini bukan hal aneh. Ini adalah "pajak" standar dari merilis aplikasi Android native, dan itulah sebabnya banyak tim mempekerjakan orang yang tugasnya khusus mengetahui flag mana yang bisa menghilangkan error minggu ini.
Build di sini menjalankan toolchain yang sesungguhnya dan menangani sendiri pekerjaan resolusi tersebut:
- Konflik dependensi tertangkap sebelum berubah menjadi crash saat runtime
- Konfigurasi toolchain yang semakin baik setiap kali ada kegagalan baru yang memberi pelajaran
Versi tiruan yang lebih cepat dari ini — yang sekadar membentuk file .aab tanpa benar-benar menjalankan task Gradle — bisa rilis dalam waktu kurang dari semenit. Tapi versi itu juga akan langsung mati begitu aplikasi kamu butuh background service atau library kriptografi native, dan tinjauan Play Store akan menandainya dalam sehari. Kami lebih memilih menghabiskan waktu dua puluh menit itu.
File .dmg macOS
Dua build masuk ke dalam satu file ini. Alat command-line Xcode mengompilasi biner Apple Silicon dan biner Intel secara terpisah, lalu lipo menyatukannya menjadi satu executable universal.
Hal itu memang menggoda — merilis satu biner saja dan menganggapnya selesai — sampai kamu ingat bahwa banyak orang memakai laptop kantor, bukan pilihan hardware mereka sendiri, dan laptop itu bisa saja berusia tiga tahun dan berbasis Intel. Daripada membuat pengguna harus mencari tahu chip apa yang mereka pakai (kebanyakan tidak bisa menjawabnya), kami merilis keduanya dan membiarkan OS yang memilih secara diam-diam. Alternatifnya, yang pernah kami coba di awal, adalah cross-compile semuanya dari mesin Linux menggunakan toolchain emulasi. Caranya lebih cepat. Tapi itu juga cara kamu berakhir dengan kasus edge codesigning yang hanya muncul di hardware macOS 12 asli, enam minggu setelah rilis, dilaporkan oleh pengguna bingung yang tidak tahu kenapa aplikasinya tidak mau terbuka.
Installer Windows
Di sinilah pengalaman pertama kali menjalankan aplikasi menentukan apakah pengguna kamu percaya pada aplikasi tersebut sama sekali. Windows SmartScreen belum mengenal installer kamu — belum membangun reputasi di server Microsoft — sehingga muncul layar biru "Windows protected your PC" dengan tombol bertuliskan "Don't run" dalam huruf tebal dan tautan "More info" yang nyaris tak terlihat, yang kalau diklik akan menampilkan opsi "Run anyway". macOS punya tarian versinya sendiri: klik kanan, Open, konfirmasi, karena aplikasi di luar App Store juga tidak dipercaya secara default. Dulu kami menautkan keduanya ke halaman FAQ generik. Tapi tiket dukungan menunjukkan itu tidak berhasil — seseorang yang menatap layar bertuliskan unduhannya mungkin malware tidak akan pergi membaca dokumentasi, mereka akan mengambil screenshot dan bertanya apakah mereka baru saja diretas. Jadi alur instalasi mendeteksi OS dan menampilkan tiga klik yang tepat dibutuhkan, tanpa perlu FAQ. Detail kecil, tapi file itu sendiri juga penting: unduhan diberi nama sesuai produk kamu, bukan sesuai artefak build. Tidak ada yang seharusnya perlu menjelaskan lewat chat support bahwa mereka mengunduh "app-release-signed-v2-final.exe" dan tidak tahu apakah itu file yang benar.
Manifest ekstensi browser
Ini adalah bagian ganjil dalam seluruh pipeline — tidak ada Gradle, tidak ada NDK, tidak ada tahap kompilasi dalam pengertian biasa. Yang ada sebagai gantinya adalah manifest, dan manifest itu adalah negosiasi dengan reviewer Chrome Web Store yang tidak akan pernah kamu ajak bicara langsung.
| Izin yang diminta | Hasil tinjauan |
|---|---|
| <all_urls> (lebih luas dari yang dibutuhkan fitur) | Bolak-balik dua minggu dengan seseorang yang tidak mau menyebutkan secara persis apa keberatannya |
| activeTab (dibatasi sesuai kebutuhan sebenarnya) | Beres di hari yang sama |
MV3 juga mempersulit sesuatu yang dulu dipermudah oleh MV2: background service worker sengaja dimatikan di tengah task, sebuah keputusan kebijakan Google demi daya tahan baterai, dan fitur yang perlu bertahan dari itu harus dibangun mengikuti aturan itu, bukan melawannya. Kami secara default membuat setiap ekstensi menggunakan set izin paling sempit yang dibutuhkan fungsinya, dan hanya memperluasnya ketika ada fitur spesifik yang menuntutnya.
Keystore
Di balik build Android ada satu artefak yang tidak pernah kamu lihat dan tidak boleh sampai hilang: kunci penandatanganan (signing key). Kehilangan itu bukan cuma berarti kehilangan kemampuan untuk memperbarui aplikasi kamu — kamu kehilangan kemampuan memperbaruinya dengan identitas yang sama, selamanya, tanpa jalan pemulihan yang akan diberikan Google, sekalipun kamu memintanya. Ini bukan infrastruktur yang mentereng. Cuma sebuah file. Tapi itulah perbedaan antara merilis versi enam bulan dari sekarang sebagai update yang mulus, dengan merilisnya sebagai listing baru yang mulai dari nol instalasi dan nol ulasan. Kami membuat satu kunci per proyek dan menyimpannya, sehingga setiap build di masa depan tetap ditandatangani dengan kunci yang sama sejak hari pertama.
Thread chat di baliknya
Tidak ada satu pun dari semua ini yang hidup dalam "proyek mobile" terpisah. Ini adalah percakapan yang sama yang membangun aplikasi web-nya. Minta perubahan UI, aplikasi web ikut diperbarui; minta paket Android berikutnya, dan itu dikompilasi dari state terkini yang sama, bukan dari fork yang sudah menyimpang tiga minggu lalu. Kebanyakan tim yang saya lihat mencoba menambahkan native belakangan, akhirnya harus memelihara dua codebase yang makin menjauh — aplikasi web yang dirilis setiap hari, dan wrapper native yang harus diingat-ingat seseorang untuk dikejar sebelum tiap rilis. Celah itulah tempat aplikasi jadi usang, dan itu persis yang dihilangkan oleh satu riwayat build tunggal. Tapi ini berlaku dua arah: kalau belakangan chat-nya sedang serampangan, build Android-nya ikut mewarisi itu juga. Ini bukan proses polesan terpisah, melainkan kompilasi langsung dari apa pun yang sebenarnya ada — yang dalam praktiknya membuat orang tetap jujur, karena tidak ada "nanti kita rapikan sebelum submit" yang bisa dilewatkan begitu saja.
Saat sudah siap untuk masuk ke toko aplikasi, jalur perilisan menyerahkannya ke listing Play Store milikmu sendiri dan akun Apple Developer milikmu sendiri. Bukan milik kami. Kami tidak ingin berada di antara kamu dan distribusi milikmu sendiri.



