Mert Özen Her Satırda Daha İleri
Frontend UI/UX Tasarım İlkeleri — #01

UI ve UX Farkı ve Geliştirici İçin Tasarım: Usability Heuristic'leriyle Bir Ekranı Değerlendirmek

Mert Özen 14 Kas 2026 11 dk 8 görüntülenme
UI ve UX Farkı ve Geliştirici İçin Tasarım: Usability Heuristic'leriyle Bir Ekranı Değerlendirmek

UI ekranın görünen yüzeyi, UX o yüzeyi kullanırken yaşanan akışın tamamı; tasarım kararı zevkle değil, ölçülebilir gerekçeyle verilir. Nielsen'in on heuristic'ini dört soruda toplayıp Bookstore'un checkout ekranını değerlendiriyor, bulguları 0–4 şiddetle sıralıyor ve çift tıklamada iki sipariş oluşturan, hatada formu silen ekranı Playwright ile ölçüp düzeltiyoruz.

Sürüm notu: Bu yazıdaki örnek ekranlar Playwright 1.65 ile Chromium 158'de render edilip ölçülmüştür; script'ler Node.js 24 ile çalıştırılmıştır. Heuristic'ler Nielsen Norman Group'un 2020'de güncellediği listeye göre anlatılır.

Önce Tahmin Et

Sitedeki serilerin ortak örnek uygulaması Bookstore (bir online kitapçı) için Angular istemcisi bookstore-web'in checkout ekranını bir geliştirici iki günde yazmış. Ekran çalışıyor: alanlar var, ödeme butonu var, sipariş oluşuyor. Ama satış ekibi "sepette kalan sipariş çok" diyor, müşteri hizmetlerine de "iki kez ödeme alındı" şikâyeti geliyor. Formun submit kodu şu:

const form = document.querySelector('#checkout');
const message = document.querySelector('#message');

form.addEventListener('submit', async (event) => {
  event.preventDefault();
  const data = Object.fromEntries(new FormData(form));
  if (!/^\d{5}$/.test(data.postcode)) {
    message.textContent = 'Invalid input.';
    message.hidden = false;
    form.reset();
    return;
  }
  const order = await window.placeOrder(data);
  message.textContent = `Purchase complete: ${order.id}`;
  message.hidden = false;
});

placeOrder sahte bir API; her çağrıda 1,2 saniye sonra bir sipariş oluşturuyor. İki soru: kullanıcı posta kodunu "347" diye eksik yazıp Pay now'a bastığında ekranda ne görür ve az önce yazdığı e-posta adresine ne olur? Posta kodu doğruyken butona çift tıklarsa kaç sipariş oluşur? Cevabı kodda arama; birazdan aynı görevi bir script'e yaptırıp ölçeceğiz.

Bu seri tasarımcı olmayan geliştiriciler için. Hedef, "çirkin değil ama bir şey eksik" denen ekranı gerekçesiyle düzeltebilmek. Her yazıda Bookstore'un bir ekranını küçük bir HTML sayfası olarak kuruyor, Playwright ile render edip ölçüyor, sonra iyileştiriyoruz. CSS mekaniğinin kendisi "CSS3: Flexbox, Grid ve Modern CSS" serisinde, Sass tarafı "Sass/SCSS" serisinde anlatıldı; burada o mekanikleri tekrar etmeyecek, hangi tasarım kararını neden verdiğimize odaklanacağız.

UI ve UX Aynı Şey Değil

UI, kullanıcının gördüğü ve dokunduğu yüzey: butonlar, input'lar, renkler, yazı boyutları, boşluklar. UX ise o yüzeyi kullanırken yaşanan deneyimin tamamı: kullanıcı ne yapmak istiyordu, kaç adımda yaptı, nerede takıldı, sonunda ne hissetti. Kargo ücretinin ödeme butonundan sonra ortaya çıkması bir UX sorunu; gri yardım metninin okunmaması bir UI sorunu. İkisi birbirine bağlı ama aynı soruya cevap vermiyor.

Ayrımı bilmek pratikte şuna yarıyor: bir ekrana "güzelleştirelim" diye girdiğinde yalnız UI'ya dokunursun. Renkleri değiştirirsin, köşeleri yuvarlarsın, ekran daha modern görünür, ama sepette kalan sipariş sayısı değişmez. Bookstore'un checkout'undaki asıl sorunlar akışta: kullanıcı toplam tutarı göremiyor, hata yapınca baştan başlıyor, ödeme sürerken bir şey olup olmadığını bilmiyor. Bunlar renk değiştirerek düzelmez.

Tasarım Zevk Meselesi Değil: Gerekçeli Karar

Geliştiricinin tasarıma uzak durmasının bir sebebi, tasarımı zevk meselesi sanması. "Bence bu buton büyük olmalı" ile "bence olmamalı" arasında karar veremezsin. Ama arayüz kararlarının çoğu zevkle değil, insanların nasıl algıladığı, neyi hatırladığı ve nerede hata yaptığıyla ilgili gözlemlere dayanıyor. Bu gözlemler yıllardır test edilip kurallara dönüştürülmüş durumda.

Bu kuralların en çok kullanılan seti, Nielsen Norman Group'un (kısaca NN/g; Jakob Nielsen ve Don Norman'ın kurduğu kullanılabilirlik araştırma şirketi) yayımladığı on usability heuristic'i. NN/g'ye göre Jakob Nielsen bu listeyi 1990'da oluşturdu, 1994'te son hâline getirdi; 2020'de tanımlar örneklerle yenilendi. "Heuristic" kelimesi bilerek seçilmiş: bunlar kesin kural değil, bir ekranı değerlendirirken sorulacak soruların başlıkları. Bir heuristic'in ihlal edilmesi her zaman sorun demek değil; bağlam ve alternatifler önemli.

Gerekçeli karar şöyle görünür: "Pay butonu istek sürerken pasif görünmeli, çünkü sistem ne yaptığını göstermezse kullanıcı tekrar basar ve bunu ölçtük: iki sipariş oluşuyor." Bu cümleyle tartışılabilir; "daha şık olur" cümlesiyle tartışılamaz.

On Heuristic'i Dört Soruda Toplamak

Listeyi madde madde ezberlemek yerine, bir ekrana bakarken sorduğum dört soruya böldüm. Her başlıkta heuristic'lerin İngilizce adları geçiyor, çünkü literatürde ve araçlarda bu adlarla anılıyorlar.

Kullanıcı ne olduğunu biliyor mu?

Visibility of system status bu grubun merkezinde: sistem ne yapıyorsa, makul bir sürede kullanıcıya göstermeli. Butona basıldıktan sonra hiçbir şey değişmiyorsa kullanıcı isteğin gidip gitmediğini bilemez ve tekrar basar. Toplam tutarın ödeme anına kadar görünmemesi de aynı başlığa giriyor: kullanıcı neye onay verdiğini bilmiyor. Sistemin konuştuğu dil de bu soruya dahil; match between the system and the real world, ekranın iç kodlar yerine kullanıcının bildiği kelimelerle konuşmasını istiyor. STD depo sisteminin kodu, müşterinin değil.

Hata yapmak zor, düzeltmek kolay mı?

Üç heuristic burada birleşiyor. Error prevention, hatayı oluşmadan engellemeyi söylüyor: yıkıcı bir butonu ana aksiyonun yanına aynı görünümle koymamak, bir isteği iki kez gönderememek. Help users recognize, diagnose, and recover from errors, hata olduğunda mesajın ne olduğunu ve nasıl düzeleceğini sade bir dille söylemesini istiyor. User control and freedom ise yanlış bir adımdan kolayca geri dönebilmek: geri alma, iptal, önceki adıma dönüş. "Invalid input." yazıp formu silmek üçünü birden ihlal ediyor.

Ekran kendi içinde ve dışarıyla tutarlı mı?

Consistency and standards iki yönlü: ürünün kendi içinde aynı şeye aynı adı vermek ve kullanıcının başka sitelerden alıştığı kalıplara uymak. Header'da "Basket", özet kutusunda "Your cart", sonuç mesajında "Purchase complete" yazıyorsa kullanıcı bunların aynı şey olup olmadığını bir an düşünür. Tek tek küçük, toplandığında ekranın güvenilirliğini düşüren bir sorun.

Kullanıcı hatırlamak zorunda mı?

Recognition rather than recall, bilgiyi kullanıcının aklında tutmasını beklemek yerine ekranda göstermeyi söylüyor. Label yerine placeholder kullanan bir input'ta kullanıcı yazmaya başladığı an alanın adı kayboluyor; alanların hangi sırayla ne istediğini hatırlamak ona kalıyor. Aesthetic and minimalist design, flexibility and efficiency of use ve help and documentation bu soruyu tamamlıyor: ekranda gereksiz bilgi olmasın, deneyimli kullanıcı için kısayollar olsun, yardım gerekiyorsa işin yapıldığı yerde dursun.

Bookstore Checkout'unu Değerlendirmek

Heuristic review'da (heuristic'lere göre uzman değerlendirmesi) NN/g'nin önerdiği süreç şu: üç ila beş kişi aynı arayüzü birbirinden bağımsız inceler, önce görevi bir kez yaparak ekranı tanır, ikinci turda her ihlali not eder, sonra bulgular birleştirilip tartışılır. Tek kişinin her şeyi bulması beklenmiyor; farklı kişiler farklı sorunları yakalıyor. Küçük bir ekip için pratik karşılığı: geliştirici, product owner ve bir test uzmanı yarım saat ayırır, sonra bulguları tek tabloda toplar.

Değerlendirmeyi bir görev üzerinden yap: "Ada iki kitabı express kargoyla satın alıyor." Ekrana genel olarak bakmak yerine bu görevin her adımında ne gördüğünü kontrol et. Görevin bir kısmını otomatikleştirmek de mümkün; sayılabilen bulguları bir Playwright script'i doğrulayabilir. Lab klasöründe:

npm install
npx playwright install chromium
node scripts/01-review.mjs web/01-checkout/before.html
web/01-checkout/before.html
  wrong postcode -> message: "Invalid input.", email kept: false
  total before paying: (not shown)
  double-click Pay -> feedback within 100 ms: false, orders created: 2

Tahminin cevabı: eksik posta koduyla gelen mesaj yalnız "Invalid input.", hangi alanın neden yanlış olduğunu söylemiyor ve form.reset() yüzünden e-posta dahil her şey siliniyor. Toplam tutar ödemeden önce hiçbir yerde yok. Çift tıklamada buton 100 milisaniye içinde hiçbir değişiklik göstermiyor ve iki sipariş oluşuyor. Script ne yaptığını gizlemiyor: alanları doldurup butona basıyor, ardından görünen mesajı, input değerini ve window.ordersCreated sayacını DOM'dan (tarayıcının sayfadan kurduğu element ağacı) okuyor. Görsel bulgular için ise ekrana bakmak gerekiyor:

01-checkout-review-tr.png

Bulguları Şiddete Göre Sıralamak

Bulgu listesi önceliklendirilmezse ekip en kolayından başlar, en önemlisi kalır. Nielsen'in 1994 tarihli önerisinde şiddet 0'dan 4'e bir ölçekle verilir: 0 sorun değil, 1 kozmetik, 2 küçük, 3 büyük, 4 yayından önce mutlaka düzeltilmeli. Puanı üç şey belirler: sorun ne sıklıkla yaşanıyor, yaşandığında aşması ne kadar zor ve kullanıcı her seferinde yeniden mi karşılaşıyor. Bookstore checkout'unun tablosu:

#BulguHeuristicŞiddet
1Pay basıldığında geri bildirim yok; çift tıklama iki sipariş oluşturuyorVisibility of system status, Error prevention4
2Kargo ücreti ve toplam tutar ödemeden önce görünmüyorVisibility of system status4
3Hata mesajı "Invalid input." ve form tamamen siliniyorRecognize, diagnose, recover from errors3
4Cancel, Pay ile aynı görünümde ve formu geri alınamaz şekilde siliyorError prevention, User control and freedom3
5Kargo seçenekleri STD, EXP, PUP kodlarıyla; fiyat ve süre yokMatch with the real world2
6Label yerine placeholder; yazınca alanın adı kayboluyorRecognition rather than recall2
7Basket, Your cart ve Purchase complete aynı akışta karışıkConsistency and standards1

İlk iki satırın 4 alması tesadüf değil. Çift sipariş paraya dokunuyor ve her ödemede tekrar yaşanabiliyor. Gizli toplam tutar ise sepet terk etmenin bilinen sebeplerinden: Baymard Institute'un Ekim 2026'da güncellenen listesinde, alışverişi yarıda bırakanların yüzde 40'ı beklenmedik ek maliyetleri, yüzde 12'si toplam tutarı baştan görememeyi sebep gösteriyor. Altıncı satır form tasarımının konusu; bu serinin form yazısında ayrıntılı ele alacağız. Hint metninin düşük kontrastı da (gri #999999 beyaz üzerinde, script ile ölçülen oran 2.84:1) renk yazısının konusu, o yüzden tabloya ayrı satır olarak girmedi.

Düzeltmeler: Önce En Yüksek Şiddet

İlk dört bulgu kodla çözülüyor. Yeni submit handler'ı istek sürerken ikinci gönderimi engelliyor, hatayı ilgili alanın altında söylüyor ve kullanıcının yazdıklarına dokunmuyor:

form.addEventListener('submit', async (event) => {
  event.preventDefault();
  if (submitting) return; // istek sürerken gelen ikinci tıklama hiçbir şey yapmaz
  if (!/^\d{5}$/.test(postcode.value.trim())) {
    postcodeError.textContent = 'Enter a 5-digit postcode, like 34710';
    postcodeError.hidden = false;
    postcode.setAttribute('aria-invalid', 'true');
    postcode.focus();
    return;
  }
  postcodeError.hidden = true;
  postcode.removeAttribute('aria-invalid');
  submitting = true;
  pay.setAttribute('aria-disabled', 'true');
  pay.textContent = 'Processing payment...';
  const order = await window.placeOrder(Object.fromEntries(new FormData(form)));
  status.textContent = `Order ${order.id} placed. A confirmation email is on its way.`;
  pay.textContent = 'Paid';
});

Buton disabled yerine aria-disabled alıyor; böylece focus'ta kalıyor ve screen reader kullanıcısı da durumu duyuyor. Bu ayrıntının accessibility tarafı "Accessibility (a11y)" serisinin konusu. Asıl koruma submitting bayrağı; görsel değişiklik kullanıcıya, bayrak sisteme ikinci isteği göndermemeyi söylüyor. Gerçek uygulamada sunucu da aynı siparişi iki kez kabul etmemeli; sunucu tarafındaki idempotency "External Service Integration ve Resilience" serisinde ele alınıyor.

Kargo seçimi koddan kelimeye dönüyor ve her seçenek fiyatını yanında taşıyor. Seçim değişince özet kutusundaki toplam ve butonun üzerindeki tutar birlikte güncelleniyor:

<fieldset>
  <legend>Delivery</legend>
  <label class="option"><input type="radio" name="shipping" value="49.90" checked> Standard, 3-5 business days <span>₺49.90</span></label>
  <label class="option"><input type="radio" name="shipping" value="89.90"> Express, next business day <span>₺89.90</span></label>
  <label class="option"><input type="radio" name="shipping" value="0"> Pick up in store <span>Free</span></label>
</fieldset>

Cancel butonu kaldırıldı; yerine ana aksiyondan uzakta, link görünümünde "Back to cart" geldi. Kullanıcı yine geri dönebiliyor ama yanlışlıkla formu silemiyor. Header'daki "Basket" de "Cart" oldu. Script'i iki sayfa için birlikte çalıştırınca:

node scripts/01-review.mjs
web/01-checkout/before.html
  wrong postcode -> message: "Invalid input.", email kept: false
  total before paying: (not shown)
  double-click Pay -> feedback within 100 ms: false, orders created: 2
web/01-checkout/after.html
  wrong postcode -> message: "Enter a 5-digit postcode, like 34710", email kept: true
  total before paying: ₺924.90
  double-click Pay -> feedback within 100 ms: true, orders created: 1

Express seçildiğinde toplam 835 + 89,90 = ₺924.90 olarak ödemeden önce görünüyor, mesaj neyin nasıl düzeleceğini söylüyor, e-posta korunuyor ve çift tıklama tek sipariş oluşturuyor. Bu ekran hâlâ güzel değil; hiyerarşi, tipografi ve renk sonraki yazıların işi. Ama artık kullanıcıyı yarı yolda bırakmıyor.

Kullanıcıyla Doğrulamak: Beş Kişi Yeterli mi?

Heuristic review uzmanın tahmini; gerçek kullanıcının ne yaptığını göstermez. Bookstore'un checkout'unda "Back to cart" linkini kimse fark etmiyor olabilir, ya da müşteriler kargo seçimini hiç beklemediğimiz bir sebeple karıştırıyor olabilir. Bunu yalnız birini ekranın başına oturtup bir görev vererek görürsün.

Nielsen'in 2000 tarihli yazısındaki hesaba göre tek bir test kullanıcısı sorunların ortalama yüzde 31'ini ortaya çıkarıyor ve beş kullanıcıyla bu oran yaklaşık yüzde 85'e varıyor. Yazının asıl önerisi beş sayısından çok yöntemle ilgili: on beş kişilik tek bir test yerine beşer kişilik üç küçük test yap, her turdan sonra düzelt ve tekrar ölç. Kullanıcı görüşmesi ve discovery'nin nasıl yapılacağı Product Management'a Giriş serisinin "Product Discovery: Continuous Discovery, Kullanıcı Görüşmesi ve Opportunity Solution Tree" yazısında anlatıldı; iki tasarımı sayılarla karşılaştırmak istersen aynı serinin A/B test yazısı var.

Üretimde Dikkat

Heuristic review'u bir kerelik iş olarak görme. Checkout gibi kritik akışlar her büyük değişiklikte yeniden değerlendirilmeli, çünkü yeni bir alan, yeni bir ödeme yöntemi ya da yeni bir kampanya kutusu eski bulguları geri getirebilir. Sayılabilen bulguları (çift sipariş, korunmayan input, görünmeyen toplam) bu yazıdaki gibi script'e çevirip CI'da çalıştırmak, regresyonu bir kullanıcı şikâyetinden önce yakalar.

Bulgu tablosunu ekibin backlog'una bağla. Şiddeti 4 olan bulgu bir sonraki sürüme girmeden release çıkmamalı; 1 ve 2 puanlılar ise diğer işlerle birlikte planlanabilir. Tabloda heuristic sütununu tutmak, tartışmayı "bence" seviyesinden çıkarıyor.

Ölçtüğün şeyin sahadaki karşılığını da izle: sepette kalan sipariş oranı, ödeme adımında görülen hata sayısı, aynı müşteriden birkaç saniye arayla gelen ikinci sipariş. Bu ölçümlerin nasıl kurulacağı ilerideki "Analytics ve Ölçüm" serisinin konusu.

Sık Düşülen Tuzaklar

Birincisi, review'u görevsiz yapmak. "Ekrana genel olarak baktım" dediğinde gözün estetik ayrıntılara gider; "Ada iki kitap alıyor" dediğinde akıştaki boşlukları görürsün.

İkincisi, heuristic'i kural sanmak. Minimalist tasarım, kullanıcıya gereken bilgiyi saklamak değil; bizim checkout'ta özet kutusunu "sade kalsın" diye kaldırmak toplam tutar sorununu geri getirirdi.

Üçüncüsü, yalnız UI'yı düzeltmek. Before ekranına yeni renkler ve yuvarlak köşeler versen ilk iki bulgu yerinde kalırdı; şiddeti en yüksek sorunlar genellikle akışta.

Dördüncüsü, double submit'i yalnız butonu pasif yaparak çözmek. Görsel durum kullanıcı içindir; ağ yavaşken ya da sayfa yeniden denediğinde sunucu tarafında da aynı isteği tanıyan bir koruma gerekir.

Küçük Bir Deneme

★☆☆ after.html'de Pick up in store seçeneğini işaretle ve toplamın ne olacağını tahmin et. Sonra 01-review.mjs'e bu seçimi yapan bir adım ekleyip #total değerini yazdır.

★★☆ Before ekranında tabloya girmeyen en az iki bulgu daha bul (örneğin kart son kullanma tarihi alanının biçimi ya da başarı mesajının yeri). Her biri için heuristic'i ve şiddeti yaz, gerekçesini bir cümleyle ver.

★★★ Script'e bir kontrol daha ekle: yanlış posta kodundan sonra focus'un hangi element'te olduğunu document.activeElement ile oku. Before ve after için sonucu karşılaştır ve bu farkın klavyeyle gezinen bir kullanıcı için ne anlama geldiğini yaz.

Bir sonraki yazıda Bookstore'un kitap detay sayfasına geçiyoruz. Fiyat, "Add to cart" butonu, stok bilgisi ve meta veri aynı ağırlıkta durduğunda göz nereye gidiyor, boyut yerine ağırlık ve renkle hiyerarşi nasıl kurulur ve Gestalt prensipleri ilişkili bilgiyi nasıl gruplar, önce/sonra ekranlarıyla bakacağız.