Skip to content

Async Backend'ler İçin UX Rehberi: Optimistic, Decoupled, ya da Hiçbiri

Async backend'le çalışan tasarımcılar için pragmatik rehber: üç etkileşim şekli, hangisi ne zaman, ve karşı durmanız gereken dört anti-pattern.

Spinner'lar Backend'inizden Gelen Bir Hata Raporudur

Event-based bir backend, işi istek/yanıt döngüsünün dışına taşır: istemci gönderir, sunucu kabul edip kuyruğa alır ve durum değişikliği bir süre sonra gerçekleşir. Spinner, sistemin bu oturum için aktif olarak sonucu ürettiği sinyalini verir; ancak event-based bir akışta client tarafında beklenen bir iş yoktur. UI iki farklı durumu birleştirir (backend işi sıraya aldı vs. backend şu anda sonucu hesaplıyor); oysa bu iki durum kullanıcıya farklı sözler verir.

Backend bir değişikliği kaydettiği an ile frontend'in bunu yansıttığı an arasındaki boşluk mimari olarak kaçınılmazdır. Bu boşluğu spinner ile kapatmak yanlış hamledir: spinner ya durum gelmeden biter (ve kullanıcı "oluşturuldu" yazısının ardından boş bir liste görür) ya da oturumun ötesine sarkar.

Bu yazı, async backend'le çalışan UX tasarımcıları için bir rehberdir. İki geçerli yanıtı ele alır: geri alınabilir aksiyonlar için optimistic UI, geri kalan her şey için decoupled bildirim; ayrıca makul çözüm gibi görünen ama olmayan dört anti-pattern'i (refresh döngüsü dahil) işler.

Üç Seçenek: Ne Zaman Hangisi

Async backend'le çalışırken tasarımcının seçeceği üç etkileşim şekli var.

Optimistic UI, geri alınabilir her aksiyon için varsayılandır. Decoupled bildirim akışları; yavaş, geri alınamaz veya oturumdan uzun yaşayacak her şey için varsayılandır. Bloklayıcı spinner neredeyse her zaman yanlıştır; UI'ı kilitler, kullanıcıyı terk etmek zorunda bırakır, ve backend yine de alttan async-güvenli olmak zorundadır. Spinner'ı haklı çıkaracak kadar uzun süren iş, zaten decoupled'a aittir.

Karar tek soruya bağlı: sunucu reddederse kullanıcı bu aksiyonu ekranda tek başına geri alabilir mi?

Ağaç, varsayılandan kök alıyor. Geri kalan her şey isimlendirilmiş bir geçersiz kılmadır.

Optimistic UI'ı Tasarlamak: Dört Durum

Optimistic UI dört durumlu bir makinedir. Tasarımcının her birine bir ekran muamelesi vermesi gerekir; atlanan durum genelde rollback olur.

Idle. Varsayılan durum. Buton aktif, form geçerli, imleç hazır. Kullanıcı henüz bir şey yapmamış.

In-flight / Optimistic. Kullanıcının tıkladığı an. Sonucu başarılı olmuş gibi gösterin, ama gizli bir hedge ile: hafif opaklık, öğenin üstünde küçük bir "gönderiliyor" işareti (tam ekran spinner değil), "geri al" gibi ikincil aksiyonlar devre dışı. Kullanıcı başarıyı hisseder; rollback ihtimaline karşı görsel bir tampon bırakılmıştır.

Confirmed. Sunucu yeşil döndü. Hedge kalkar. Geçiş neredeyse görünmez olmalı; kullanıcı zaten başarılı olduğunu düşünüyor, şimdi onu gerçek yapıyorsunuz.

Rolled-back. Sunucu reddetti. Değişikliği geri alın, ama görünür şekilde ve öğenin kendi üzerinde. Toast değil; satırın yanında "gönderilemedi, yeniden dene" yazısı, aynı yerde. Kullanıcının zihinsel modeli: "yaptığım şey burada, aynı yerden tekrar deneyebilirim."

Üç kural her dört durumu birbirine bağlar:

  1. Toast rollback için değildir. Toast kaybolur; kullanıcının neyin başarısız olduğunu ve ne yapabileceğini bilmesi gerekir. Hata, öğenin kendi üzerinde ve yerinde.
  2. Başarısızlık görsel dili, başarı görsel diliyle aynı olmalı. Aynı satır, aynı etkileşim; sadece durum etiketi ters dönsün.
  3. Retry, aksiyonun gerçekleştiği yerde. Kullanıcı başka bir sayfaya gidip geri dönmek zorunda kalmasın.

Decoupled Akışı Tasarlamak: İki Kanal ve Bir Yuva

Decoupled akışlarda aksiyon kullanıcının bakışından çıkar. UI'ın üç sorumluluğu vardır: kabul edildi mi, nerede görülecek, bittiğinde nasıl haber verilecek?

1. Anlık onay ekranı. Gönderdikten sonra kullanıcıyı bir "aldık" ekranına çıkarın. Kopyada "Başarılı" ya da "Oluşturuldu" değil, "Aldık" ya da "Kabul edildi." Verebiliyorsanız bir ETA: "Yaklaşık 2 dakika sürecek." Ve nerede bulacağı: "İşlemlerim sayfasından takip edebilirsin."

2. Listedeki pending rozeti. Rezervasyon, yükleme, rapor, her neyse, listede hemen görünsün. Ama görünür şekilde pending olarak işaretli: grileşmiş, bir "işleniyor" rozeti, ölçülebiliyorsa satır içi ilerleme çubuğu. Kullanıcı orada olduğunu görür, henüz gerçek olmadığını görür. Refresh döngüsü burada biter.

3. Bildirim yüzeyi. İş bittiğinde kullanıcıya söyleyin. Uygulamadaysa in-app toast, arka planda ise push, değilse e-posta. Her yüzey için hem başarı hem başarısızlık kopyası olmalı: "Rezervasyonun hazır: görüntüle" ve "Rezervasyonun tamamlanamadı: nedeni ve ne yapabilirsin."

4. Durum sayfası. 30 saniyeden uzun süren her iş için kullanıcının geri dönebileceği özel bir sayfa. Her bildirimden bu sayfaya deep link düşsün. Sayfa tam geçmişi göstermeli: gönderildi → işleniyor → tamamlandı / başarısız.

Bir kural bu dördünü birbirine bağlar: pending sonsuza dek pending kalamaz. 30 dakikadır "işleniyor" yazan bir satır bir özellik değil, bir hatadır. Tasarım zamanında bir zaman limiti koyun; o limite ulaşıldığında kullanıcıya açıkça hata gösterin veya eskalasyon yolu verin.

Kaçınmanız Gereken Dört Şablon

  1. Uzun işlemin üzerinde tam ekran spinner. UI'ı kilitler, kullanıcıyı terk etmek zorunda bırakır, geri dönüş yolu bırakmaz. Üç saniyenin üstündeki her işlemi decoupled akışa çekin.

  2. Sahte "başarılı" toast'u. "Siparişin oluşturuldu" deyip sonra "ödeme reddedildi" maili göndermek. Kabul ≠ başarı. Kopyanızı gerçeğe göre seçin: "aldık" farklı, "tamamlandı" farklı.

  3. Kullanıcı sayfayı bıraktıktan sonra fırlayan hata toast'ı. Toast 3 saniye sonra görünür; kullanıcı başka sayfadadır. Hatalar sayfaya değil, aksiyonun ait olduğu öğeye bağlanmalı. Toast yerine, öğenin üzerinde yerinde durum değişikliği.

  4. Detail ekranı ile liste ekranı arasında state drift. Modal'da "oluşturuldu" dedi, kullanıcı listeye gitti, orada yok. Klasik read-after-write açığı. Ya optimistic olarak listeye yerleştirin, ya da modal'ı liste güncellenene kadar açık tutun. "Tamam" dedikten sonra kullanıcı "peki nerede?" diye aramak zorunda kalmasın.

Kapanış

Backend async ise, UX'in işi aradaki boşluğu tasarlamaktır. Üç seçeneğiniz var: geri alınabilir ise optimistic, aksi halde decoupled, çok kısa sync işlemler için kısa bir loading. Bloklayıcı spinner bu listenin dışındadır; hem backend'in ne yaptığı konusunda dürüst değildir hem de kullanıcıya karşı naziksizdir.

Seçimi bilinçli yapın. Varsayılanı seçin, geçersiz kılmaları isimlendirin, happy path'i göndermeden önce rollback ekranını kurun. Kullanıcılar bir boşluğu refresh döngüsüyle sessizce telafi etmek zorunda kalmamalı.

Dikişin backend tarafını, yani taşıma katmanı seçimini, yoldaş yazıda ele aldık: Web ve Mobil için Asenkron API Desenleri.

Kaynaklar

İlgili Yazılar