Jaringan Kotlin untuk koneksi lambat: timeout dan retry aman
Jaringan Kotlin praktis untuk koneksi lambat: atur timeout, cache dengan aman, retry tanpa duplikat, dan lindungi aksi kritis di jaringan mobile yang fluktuatif.

Apa yang rusak pada koneksi lambat dan fluktuatif
Di mobile, ālambatā biasanya bukan berarti ātidak ada internet.ā Seringkali artinya koneksi yang bekerja, tapi hanya dalam ledakan singkat. Satu permintaan bisa memakan 8ā20 detik, macet setengah jalan, lalu selesai. Atau berhasil satu saat lalu gagal karena ponsel berpindah dari WiāFi ke LTE, masuk area sinyal rendah, atau OS memasukkan app ke background.
āFluktuatifā lebih buruk. Paket hilang, lookup DNS timeout, handshake TLS gagal, dan koneksi reset secara acak. Anda bisa menulis kode dengan benar dan tetap melihat kegagalan di lapangan karena jaringan berubah di bawah Anda.
Di sinilah pengaturan default cenderung gagal. Banyak aplikasi mengandalkan default library untuk timeout, retry, dan caching tanpa memutuskan apa yang ācukup baikā untuk orang nyata. Default sering disetel untuk WiāFi stabil dan API cepat, bukan untuk kereta komuter, lift, atau kedai kopi ramai.
Pengguna tidak menyebutkan āsocket timeoutā atau āHTTP 503.ā Mereka memperhatikan gejala: spinner yang tak berujung, error tibaātiba setelah menunggu lama (lalu berhasil saat dicoba lagi), tindakan ganda (dua pemesanan, dua pesanan, double charge), pembaruan yang hilang, dan status campuran di mana UI mengatakan āgagalā tapi server sebenarnya berhasil.
Jaringan lambat mengubah celah desain kecil menjadi masalah uang dan kepercayaan. Jika aplikasi tidak memisahkan dengan jelas āmasih mengirimā dari āgagalā dan āselesai,ā pengguna akan mengetuk lagi. Jika client retry secara membabi buta, itu bisa membuat duplikat. Jika server tidak mendukung idempotensi, satu koneksi goyah bisa menghasilkan beberapa penulisan yang āberhasil.ā
āTindakan kritisā adalah apa saja yang harus terjadi paling banyak sekali dan harus benar: pembayaran, pengiriman checkout, pemesanan slot, transfer poin, ganti kata sandi, menyimpan alamat pengiriman, mengajukan klaim, atau mengirim persetujuan.
Contoh realistis: seseorang mengirim checkout di LTE lemah. Aplikasi mengirim request, lalu koneksi putus sebelum respons tiba. Pengguna melihat error, mengetuk āBayarā lagi, dan sekarang dua request sampai ke server. Tanpa aturan jelas, aplikasi tidak tahu apakah harus retry, menunggu, atau berhenti. Pengguna juga tidak tahu apakah harus mencoba lagi.
Tentukan aturan sebelum menyetel kode
Saat koneksi lambat atau fluktuatif, sebagian besar bug muncul dari aturan yang tidak jelas, bukan dari HTTP client. Sebelum Anda menyentuh timeout, caching, atau retry, tuliskan apa arti ābenarā untuk aplikasi Anda.
Mulailah dengan tindakan yang tidak boleh dijalankan dua kali. Ini biasanya berkaitan dengan uang dan akun: buat pesanan, charge kartu, kirim payout, ganti kata sandi, hapus akun. Jika pengguna mengetuk dua kali atau aplikasi retry, server harus tetap memperlakukan itu sebagai satu permintaan. Jika Anda belum bisa menjamin itu, tangani endpoint tersebut sebagai ātidak autoāretryā sampai Anda bisa.
Selanjutnya, tentukan apa yang boleh dilakukan setiap layar saat jaringan buruk. Beberapa layar masih berguna saat offline (profil terakhir, pesanan sebelumnya). Lainnya harus menjadi bacaāsaja atau menampilkan status ācoba lagiā yang jelas (jumlah inventori, harga langsung). Mencampur ekspektasi ini menghasilkan UI membingungkan dan caching yang berisiko.
Tetapkan waktu tunggu yang dapat diterima per tindakan berdasarkan cara pengguna berpikir, bukan apa yang terasa rapi di kode. Login bisa mentoleransi tunggu singkat. Upload file butuh lebih lama. Checkout harus terasa cepat tapi juga aman. Timeout 30 detik mungkin āandalā di atas kertas dan tetap terasa rusak.
Terakhir, putuskan apa yang akan Anda simpan di perangkat dan berapa lama. Data cached membantu, tapi data usang bisa menyebabkan pilihan yang salah (harga lama, kelayakan yang kadaluarsa).
Tulis aturan itu di tempat yang dapat diakses semua orang (README cukup). Buat sederhana:
- Endpoint mana yang ātidak boleh duplikatā dan membutuhkan penanganan idempotensi?
- Layar mana yang harus bekerja offline, dan mana yang bacaāsaja saat offline?
- Berapa waktu tunggu maksimum per tindakan (login, refresh feed, upload, checkout)?
- Apa yang boleh di-cache di perangkat, dan kapan kedaluwarsa?
- Setelah kegagalan, apakah Anda menampilkan error, mengantri untuk nanti, atau meminta retry manual?
Setelah aturan ini jelas, nilai timeout, header caching, kebijakan retry, dan status UI jauh lebih mudah diimplementasikan dan diuji.
Timeout yang sesuai ekspektasi pengguna nyata
Jaringan lambat gagal dalam berbagai cara. Setup timeout yang baik tidak hanya āmemilih angka.ā Ia mencerminkan apa yang pengguna coba lakukan dan gagal dengan cepat cukup agar aplikasi bisa pulih.
Tiga timeout, dengan istilah sederhana:
- Connect timeout: berapa lama menunggu untuk membangun koneksi ke server (lookup DNS, TCP, TLS). Jika ini gagal, request tidak benarābenar dimulai.
- Write timeout: berapa lama menunggu saat mengirim body request (upload, JSON besar, uplink lambat).
- Read timeout: berapa lama menunggu server mengirim data kembali setelah request dikirim. Ini sering muncul di jaringan mobile yang tidak stabil.
Timeout harus mencerminkan layar dan taruhannya. Feed bisa lebih lambat tanpa bahaya besar. Tindakan kritis harus selesai atau gagal dengan jelas sehingga pengguna bisa memutuskan langkah selanjutnya.
Titik awal praktis (sesuaikan setelah pengukuran):
- Memuat daftar (risiko rendah): connect 5ā10s, read 20ā30s, write 10ā15s.
- Pencarian sambil mengetik: connect 3ā5s, read 5ā10s, write 5ā10s.
- Tindakan kritis (risiko tinggi, seperti āBayarā atau āKirim pesananā): connect 5ā10s, read 30ā60s, write 15ā30s.
Konsistensi lebih penting daripada kesempurnaan. Jika pengguna mengetuk āKirimā dan melihat spinner dua menit, mereka akan mengetuk lagi.
Hindari āloading tak hinggaā dengan menambahkan batas atas yang jelas di UI juga. Tunjukkan progres segera, izinkan batal, dan setelah (misalnya) 20ā30 detik tampilkan āMasih mencobaā¦ā dengan opsi untuk retry atau memeriksa koneksi. Itu menjaga pengalaman jujur meski library jaringan masih menunggu.
Saat timeout terjadi, log informasi yang cukup untuk men-debug pola nanti, tanpa mencatat rahasia. Field yang berguna termasuk path URL (bukan query lengkap), HTTP method, status (jika ada), rincian waktu (connect vs write vs read jika tersedia), tipe jaringan (WiāFi, seluler, mode pesawat), perkiraan ukuran request/response, dan request ID agar Anda dapat mencocokkan log client dengan server.
Setup jaringan Kotlin sederhana dan konsisten
Saat koneksi lambat, inkonsistensi kecil di setup client berubah menjadi masalah besar. Baseline yang rapi membantu Anda debugging lebih cepat dan memberi setiap request aturan yang sama.
Satu client, satu kebijakan
Mulailah dengan satu tempat di mana Anda membangun HTTP client (sering satu OkHttpClient yang digunakan Retrofit). Letakkan dasar di sana sehingga setiap permintaan berperilaku sama: header default (versi app, locale, token auth) dan UserāAgent yang jelas, timeout diatur sekali (tidak disebar di berbagai panggilan), logging yang bisa diaktifkan untuk debugging, dan satu keputusan kebijakan retry (walau itu ātidak ada retry otomatisā).
Berikut contoh kecil yang menjaga konfigurasi di satu file:
val okHttp = OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.callTimeout(30, TimeUnit.SECONDS)
.addInterceptor { chain ->
val request = chain.request().newBuilder()
.header("User-Agent", "MyApp/${BuildConfig.VERSION_NAME}")
.header("Accept", "application/json")
.build()
chain.proceed(request)
}
.build()
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttp)
.addConverterFactory(MoshiConverterFactory.create())
.build()
Penanganan error pusat yang memetakan ke pesan pengguna
Error jaringan bukan sekadar āsebuah exception.ā Jika setiap layar menangani secara berbeda, pengguna mendapat pesan acak.
Buat satu mapper yang mengubah kegagalan menjadi beberapa outcome ramahāpengguna: tidak ada koneksi/mode pesawat, timeout, error server (5xx), error validasi atau auth (4xx), dan fallback tak dikenal.
Ini menjaga copy UI konsisten (āTidak ada koneksiā vs āCoba lagiā) tanpa membocorkan detail teknis.
Tag dan batalkan request saat layar ditutup
Di jaringan fluktuatif, panggilan bisa selesai terlambat dan memperbarui layar yang sudah hilang. Jadikan pembatalan aturan standar: saat layar ditutup, pekerjaan juga berhenti.
Dengan Retrofit dan Kotlin coroutines, membatalkan coroutine scope (misalnya di ViewModel) membatalkan call HTTP yang mendasarinya. Untuk panggilan nonācoroutine, simpan referensi ke Call dan panggil cancel(). Anda juga bisa menandai request dan membatalkan grup panggilan saat fitur keluar.
Pekerjaan latar belakang tidak bergantung pada UI
Apa pun yang penting dan harus selesai (mengirim laporan, menyinkronkan antrian, menyelesaikan pengajuan) sebaiknya dijalankan di scheduler yang dirancang untuk itu. Di Android, WorkManager adalah pilihan umum karena bisa retry nanti dan bertahan saat app restart. Buat tindakan UI ringan, dan serahkan pekerjaan lebih lama ke background job bila masuk akal.
Aturan caching yang aman di mobile
Caching bisa sangat membantu pada koneksi lambat karena mengurangi unduhan ulang dan membuat layar terasa instan. Tapi juga bisa jadi masalah jika menampilkan data usang pada waktu yang salah, seperti saldo akun lama atau alamat pengiriman kadaluarsa.
Pendekatan aman adalah cache hanya yang pengguna tahan jika sedikit usang, dan paksa pengecekan segar untuk apa pun yang memengaruhi uang, keamanan, atau keputusan akhir.
Dasar CacheāControl yang bisa Anda andalkan
Sebagian besar aturan turun ke beberapa header:
max-age=60: Anda dapat menggunakan kembali response cached selama 60 detik tanpa menanyakan ke server.no-store: jangan simpan response ini sama sekali (terbaik untuk token dan layar sensitif).must-revalidate: jika sudah kedaluwarsa, Anda harus memeriksa ke server sebelum menggunakannya lagi.
Di mobile, must-revalidate mencegah data āsalah diamādiamā setelah periode offline sementara. Jika pengguna membuka app setelah naik kereta bawah tanah, Anda menginginkan layar cepat, tapi juga ingin app mengonfirmasi apa yang masih benar.
Refresh ETag: cepat, murah, dan andal
Untuk endpoint baca, validasi berbasis ETag sering lebih baik daripada max-age panjang. Server mengirim ETag dengan response. Kali berikutnya, app mengirim If-None-Match dengan nilai itu. Jika tidak ada perubahan, server membalas 304 Not Modified, yang kecil dan cepat di jaringan lemah.
Ini bekerja baik untuk daftar produk, detail profil, dan layar pengaturan.
Aturan praktis singkat:
- Cache endpoint āreadā dengan
max-agesingkat plusmust-revalidate, dan dukungETagbila memungkinkan. - Jangan cache endpoint āwriteā (POST/PUT/PATCH/DELETE). Perlakukan mereka selalu bergantung jaringan.
- Gunakan
no-storeuntuk apa pun yang sensitif (respons auth, langkah pembayaran, pesan privat). - Cache aset statis (ikon, config publik) lebih lama karena risiko usang rendah.
Jaga keputusan caching konsisten di seluruh app. Pengguna lebih mudah melihat ketidaksesuaian daripada penundaan kecil.
Retry aman tanpa memperburuk keadaan
Retry terasa seperti perbaikan mudah, tapi bisa berbalik jika salah diterapkan. Retry request yang salah bisa menambah beban, menguras baterai, dan membuat app terasa tersangkut.
Mulailah dengan meāretry hanya kegagalan yang cenderung sementara. Koneksi yang terputus, timeout baca, atau outage server singkat bisa berhasil pada percobaan berikutnya. Password salah, field hilang, atau 404 tidak akan.
Aturan praktis:
- Retry timeout dan kegagalan koneksi.
- Retry 502, 503, dan kadang 504.
- Jangan retry 4xx (kecuali 408 atau 429, jika Anda punya aturan tunggu jelas).
- Jangan retry request yang sudah mencapai server dan mungkin sedang diproses.
- Batasi jumlah retry (sering 1 sampai 3 percobaan).
Backoff + jitter: kurangi gelombang retry
Jika banyak pengguna terkena outage yang sama, retry instan bisa menciptakan gelombang lalu lintas yang memperlambat pemulihan. Gunakan exponential backoff (menunggu lebih lama tiap kali) dan tambahkan jitter (delay acak kecil) supaya perangkat tidak retry serentak.
Contoh: tunggu ~0.5 detik, lalu 1 detik, lalu 2 detik, dengan random +/- 20% setiap kali.
Batasi total waktu retry
Tanpa batas, retry bisa menjebak pengguna dalam spinner selama menit. Pilih waktu total maksimum untuk seluruh operasi, termasuk semua jeda. Banyak app menargetkan 10ā20 detik sebelum berhenti dan menampilkan opsi jelas untuk mencoba lagi.
Sesuaikan juga konteks. Jika seseorang mengirim formulir, mereka ingin jawaban cepat. Jika sinkronisasi background gagal, Anda bisa retry nanti.
Jangan pernah autoāretry tindakan nonāidempotent (seperti membuat pesanan atau mengirim pembayaran) kecuali Anda memiliki perlindungan seperti kunci idempotensi atau pemeriksaan duplikat di server. Jika Anda belum bisa menjamin keamanan, gagallah dengan jelas dan biarkan pengguna memutuskan langkah selanjutnya.
Pencegahan duplikat untuk tindakan kritis
Di koneksi lambat atau fluktuatif, pengguna mengetuk dua kali. OS mungkin retry di background. Aplikasi Anda mungkin mengirim ulang setelah timeout. Jika tindakan itu āmembuat sesuatuā (buat pesanan, kirim uang, ganti kata sandi), duplikat bisa merugikan.
Idempotensi berarti permintaan yang sama harus menghasilkan hasil yang sama. Jika permintaan diulangi, server tidak boleh membuat pesanan kedua. Ia harus mengembalikan hasil pertama atau mengatakan āsudah dilakukan.ā
Gunakan kunci idempotensi untuk setiap percobaan kritis
Untuk tindakan kritis, buat kunci idempotensi unik saat pengguna memulai percobaan, dan kirimkan dengan request (sering sebagai header seperti Idempotency-Key, atau sebagai field di body).
Alur praktis:
- Buat UUID kunci idempotensi saat pengguna mengetuk āBayarā.
- Simpan secara lokal dengan catatan kecil: status = pending, createdAt, hash payload request.
- Kirim request dengan kunci tersebut.
- Saat mendapat respons sukses, tandai status = done dan simpan ID hasil server.
- Jika perlu retry, gunakan kembali kunci yang sama, bukan yang baru.
Aturan āgunakan kembali kunci yang samaā inilah yang menghentikan double charge tidak sengaja.
Tangani restart aplikasi dan jeda offline
Jika app dimatikan saat request berlangsung, peluncuran berikutnya masih harus aman. Simpan kunci idempotensi dan status request di penyimpanan lokal (mis. baris kecil di database). Saat restart, baik retry dengan kunci yang sama atau panggil endpoint ācek statusā menggunakan kunci yang tersimpan atau ID hasil server.
Di sisi server, kontraknya harus jelas: saat menerima kunci duplikat, server harus menolak percobaan kedua atau mengembalikan respons asli (ID pesanan yang sama, struk yang sama). Jika server belum bisa melakukan itu, pencegahan duplikat di sisi client tidak akan pernah sepenuhnya andal, karena app tidak bisa melihat apa yang terjadi setelah mengirim request.
Sentuhan yang bagus untuk pengguna: jika percobaan masih pending, tampilkan āPembayaran diprosesā dan nonaktifkan tombol sampai Anda mendapat hasil final.
Pola UI yang mengurangi pengiriman ulang tidak sengaja
Koneksi lambat tidak hanya merusak request. Mereka mengubah cara orang mengetuk. Saat layar membeku dua detik, banyak pengguna menganggap tidak terjadi apaāapa dan menekan tombol lagi. UI Anda harus membuat āsatu ketukanā terasa andal meski jaringan tidak.
Optimistic UI paling aman untuk aksi yang dapat dibatalkan atau berisiko rendah, seperti memberi bintang, menyimpan draft, atau menandai pesan sudah dibaca. Confirmed UI lebih baik untuk uang, inventori, hapus yang tak bisa dikembalikan, dan apa pun yang bisa membuat duplikat.
Default yang baik untuk tindakan kritis adalah status pending yang jelas. Setelah ketuk pertama, segera ubah tombol utama menjadi status āSubmittingā¦ā, nonaktifkan, dan tampilkan baris singkat yang menjelaskan apa yang sedang terjadi.
Pola yang bekerja baik di jaringan fluktuatif:
- Nonaktifkan aksi utama setelah ketukan dan pertahankan nonaktif sampai hasil final.
- Tampilkan status āPendingā yang terlihat dengan detail (jumlah, penerima, jumlah item).
- Tambahkan tampilan āAktivitas terbaruā agar pengguna bisa mengonfirmasi apa yang sudah mereka kirimkan.
- Jika app diābackground, pertahankan status pending saat mereka kembali.
- Utamakan satu tombol utama yang jelas daripada beberapa target ketuk pada layar yang sama.
Kadang request berhasil tapi respons hilang. Perlakukan ini sebagai hasil normal, bukan error yang mengundang ketukan ulang. Alihāalih āGagal, coba lagi,ā tampilkan āKami belum yakinā dan tawarkan langkah aman seperti āPeriksa status.ā Jika Anda tidak bisa memeriksa status, simpan catatan pending secara lokal dan beri tahu pengguna bahwa Anda akan memperbarui saat koneksi kembali.
Buat āCoba lagiā eksplisit dan aman. Tampilkan hanya saat Anda bisa mengulangi request menggunakan ID request sisi client yang sama atau kunci idempotensi.
Contoh realistis: pengiriman checkout yang fluktuatif
Seorang pelanggan berada di kereta dengan sinyal yang fluktuatif. Mereka menambahkan barang ke keranjang dan mengetuk Bayar. Aplikasi harus sabar, tapi juga tidak boleh membuat dua pesanan.
Urutan aman terlihat seperti ini:
- Aplikasi membuat ID percobaan sisiāclient dan mengirim request checkout dengan kunci idempotensi (mis. UUID disimpan bersama keranjang).
- Request menunggu connect timeout yang jelas, lalu read timeout yang lebih panjang. Kereta masuk terowongan, dan call timeout.
- Aplikasi retry sekali, tapi hanya setelah jeda pendek dan hanya jika tidak pernah menerima respons server.
- Server menerima request kedua dan melihat kunci idempotensi yang sama, sehingga mengembalikan hasil asli alihāalih membuat pesanan baru.
- Aplikasi menampilkan layar konfirmasi final saat menerima respons sukses, bahkan jika itu datang dari retry.
Caching mengikuti aturan ketat. Daftar produk, opsi pengiriman, dan tabel pajak dapat diācache singkat (GET). Pengiriman checkout (POST) tidak pernah diācache. Bahkan jika Anda menggunakan cache HTTP, anggap itu sebagai bantuan baca untuk browsing, bukan sesuatu yang bisa āmengingatā pembayaran.
Pencegahan duplikat adalah campuran pilihan jaringan dan UI. Saat pengguna mengetuk Bayar, tombol dinonaktifkan dan layar menampilkan āMengirim pesanan...ā dengan opsi Cancel tunggal. Jika aplikasi kehilangan jaringan, ia beralih ke āMasih mencobaā dan mempertahankan ID percobaan yang sama. Jika pengguna menutup paksa dan membuka lagi, app dapat melanjutkan dengan memeriksa status pesanan menggunakan ID itu, alihāalih meminta mereka membayar lagi.
Daftar periksa cepat dan langkah selanjutnya
Jika aplikasi Anda terasa ālumayan baikā di WiāFi kantor tapi berantakan di kereta, lift, atau daerah pedesaan, anggap ini sebagai gerbang rilis. Pekerjaan ini lebih soal aturan jelas yang dapat diulang daripada kode cerdas.
Daftar periksa sebelum rilis:
- Tetapkan timeout per tipe endpoint (login, feed, upload, checkout) dan uji pada jaringan dengan throttling dan latensi tinggi.
- Retry hanya di tempat yang benarābenar aman, dan batasi dengan backoff (beberapa kali coba untuk baca, biasanya tidak untuk tulis).
- Tambahkan kunci idempotensi untuk setiap write kritis (pembayaran, pesanan, pengiriman formulir) sehingga retry atau double tap tidak membuat duplikat.
- Buat aturan caching eksplisit: apa yang boleh disajikan usang, apa yang harus segar, dan apa yang tidak boleh diācache.
- Buat status terlihat: pending, failed, dan completed harus berbeda, dan app harus mengingat tindakan yang selesai setelah restart.
Jika salah satu poin ini masih ānanti kita putuskan,ā Anda akan berakhir dengan perilaku acak di berbagai layar.
Langkah selanjutnya agar ini bertahan
Tulis kebijakan jaringan satu halaman: kategori endpoint, target timeout, aturan retry, dan ekspektasi caching. Terapkan di satu tempat (interceptor, shared client factory, atau pembungkus kecil) sehingga setiap anggota tim mendapat perilaku yang sama secara default.
Kemudian lakukan drill duplikat singkat. Pilih satu tindakan kritis (mis. checkout), simulasikan spinner membeku, tutup paksa app, aktifkan mode pesawat, dan tekan tombol lagi. Jika Anda tidak bisa membuktikan itu aman, pengguna pada akhirnya akan menemukan cara untuk memecahkannya.
Jika Anda ingin menerapkan aturan yang sama antar backend dan client tanpa menulis semuanya secara manual, AppMaster (appmaster.io) bisa membantu dengan menghasilkan backend siap produksi dan kode native mobile. Meski begitu, kuncinya tetap kebijakan: definisikan idempotensi, retry, caching, dan status UI sekali, lalu terapkan secara konsisten di seluruh alur.
FAQ
Mulailah dengan mendefinisikan apa arti ābenarā untuk setiap layar dan tindakan, terutama yang harus terjadi paling banyak sekali seperti pembayaran atau pemesanan. Setelah aturan jelas, atur timeout, retry, caching, dan status UI agar sesuai dengan aturan tersebut, jangan bergantung pada nilai default library.
Pengguna biasanya melihat spinner tak berujung, error setelah menunggu lama, tindakan yang berhasil pada percobaan kedua, atau hasil duplikat seperti dua pesanan atau double charge. Ini sering disebabkan oleh aturan retry dan status āpending vs failedā yang tidak jelas, bukan hanya sinyal yang buruk.
Gunakan connect timeout untuk seberapa lama menunggu membuka koneksi, write timeout untuk mengirim body request (upload), dan read timeout untuk menunggu respons setelah mengirim. Default yang masuk akal: timeout lebih pendek untuk bacaan berisiko rendah, dan read/write lebih panjang untuk pengiriman kritis, serta batasi UI supaya pengguna tidak menunggu selamanya.
Ya ā jika Anda hanya bisa mengatur satu, gunakan callTimeout untuk membatasi operasi keseluruhan sehingga Anda menghindari menunggu ātak hinggaā. Setelah itu, tambahkan connect/read/write timeout sesuai kebutuhan untuk kontrol yang lebih baik, terutama untuk upload dan respons lambat.
Mulailah dengan me-retry hanya kegagalan sementara seperti putus koneksi, masalah DNS, dan timeout, serta kadang-kadang 502/503/504. Hindari retry untuk error 4xx dan hindari auto-retry pada operasi tulis kecuali Anda punya perlindungan idempotensi, karena retry bisa menghasilkan duplikat.
Gunakan sedikit percobaan (sering 1ā3) dengan exponential backoff dan sedikit jitter acak sehingga banyak perangkat tidak retry bersamaan. Juga batasi total waktu retry agar pengguna mendapat hasil jelas, bukan spinner yang berlangsung berjam-jam.
Idempotensi berarti mengulangi permintaan yang sama tidak akan menghasilkan hasil kedua, jadi double tap atau retry tidak akan double-charge atau double-book. Untuk tindakan kritis, kirim kunci idempotensi per percobaan dan gunakan kembali pada retry agar server bisa mengembalikan hasil asli daripada membuat yang baru.
Buat kunci unik ketika pengguna memulai aksi, simpan secara lokal dengan catatan kecil āpendingā, dan kirimkan bersama request. Jika Anda retry atau aplikasi dimulai ulang, gunakan kembali kunci yang sama dan retry atau periksa status, sehingga niat pengguna tidak berubah menjadi dua penulisan di server.
Cache hanya data yang relatif aman jika sedikit kadaluarsa, dan paksa pengecekan segar untuk hal-hal yang menyangkut uang, keamanan, dan keputusan akhir. Untuk read, utamakan kesegaran singkat plus revalidasi dan pertimbangkan ETag; untuk write, jangan cache, dan gunakan no-store untuk respons sensitif.
Nonaktifkan tombol utama setelah ketukan pertama, tampilkan status āSubmittingā¦ā segera, dan pertahankan status pending yang terlihat yang tetap ada saat aplikasi di-background atau setelah restart. Jika respons bisa hilang, jangan memancing pengguna untuk mengetuk ulang; tunjukkan ketidakpastian (āKami belum yakinā) dan tawarkan langkah aman seperti memeriksa status.


