2 puan yazan GN⁺ 2024-02-07 | 1 yorum | WhatsApp'ta paylaş
  • Geleneksel UNIX felsefesini modern bir yaklaşımla yeniden yorumlayan, CLI programları için tasarım ilkelerini ve somut yönergeleri açık kaynaklı bir belge olarak derleyen bir referans; temel hedef kitlesi komut satırı araçları geliştiren yazılımcılar
  • CLI, basit bir betikleme platformu olmaktan çıkıp insan merkezli bir metin tabanlı UI olarak evrildi; bu değişime uygun olarak tasarım ilkelerinin de güncellenmesi gerekiyor
  • Birleştirilebilirlik (composability) ve insan dostuluğu birbiriyle çelişmez; standart giriş/çıkış, pipe ve çıkış kodu gibi UNIX geleneklerine uyularak ikisi aynı anda sağlanabilir
  • Yardım metinleri, hata mesajları, çıktı biçimi, etkileşimlilik, yapılandırma sistemi gibi pratikte sık gözden kaçan ayrıntılar için de somut öneriler sunuyor
  • CLI araçlarının geleceğe uyumluluğu ve kullanıcı güveni, arayüz istikrarı ve analiz verisi şeffaflığıyla belirlenir; bu rehber de bunun için bir temel çizgi sunuyor

Felsefe (Philosophy)

İnsan merkezli tasarım

  • Geleneksel UNIX komutları çoğunlukla diğer programlar tarafından kullanılacak şekilde tasarlanmıştı; ancak bugün CLI çoğunlukla insanlar tarafından doğrudan kullanıldığı için insan öncelikli tasarım gerekiyor
  • Geçmişte CLI "machine-first" idi, bugün ise "human-first" metin tabanlı bir UI'ye dönüştü

Birleştirilebilir küçük parçalar

  • UNIX felsefesinin özü, küçük ve basit programları birleştirerek daha büyük sistemler kurmaktır ve bu anlayış bugün de geçerlidir
  • Standart stdin/stdout/stderr, sinyaller ve çıkış kodları programlar arası bağlantıyı garanti eder; JSON ise daha yapılandırılmış veri alışverişini destekler
  • Yazılım mutlaka daha büyük bir sistemin parçası olur; iyi çalışan bir parça olup olmayacağı tasarım aşamasında belirlenir

Tutarlılık

  • Terminal kullanıcıları mevcut konvansiyonlara alışkındır; bu nedenle CLI'nin mevcut kalıpları izlemesi önerilir
  • Ancak tutarlılık kullanılabilirliğe zarar veriyorsa, gelenek dikkatle bozulabilir

Uygun bilgi miktarı

  • Bir komut birkaç dakika boyunca hiçbir çıktı vermeden bekliyorsa "çok az", büyük miktarda debug log döküyorsa "çok fazla" bilgi veriyordur
  • Bilgi miktarındaki denge, yazılımın kullanıcıyı desteklemesi açısından kritik öneme sahiptir

Keşfedilebilirlik (Ease of Discovery)

  • GUI tüm işlevleri ekranda açıkça gösterirken, CLI'nin hafızaya dayalı olduğu yönünde yanlış bir algı vardır
  • Kapsamlı yardım metinleri, zengin örnekler ve sonraki komut önerileri gibi GUI tekniklerinden yararlanılarak CLI de öğrenmesi kolay hale getirilebilir

Bir diyalog olarak CLI

  • CLI kullanımı, tekrar eden deneme-yanılma üzerinden ilerleyen bir diyalog yapısına sahiptir; hata düzeltme önerileri, ara durum gösterimi ve riskli işlemler öncesi onay bu özelliği kullanan tasarım teknikleridir
  • En kötü etkileşim, kullanıcıyı çaresiz hissettiren düşmanca bir diyalogdur; en iyisi ise başarı hissi veren keyifli bir alışveriştir

Sağlamlık (Robustness)

  • Yazılım hem gerçekte hem de hissettirdiği şekilde sağlam olmalıdır
  • Beklenmeyen girdilerin zarif biçimde ele alınması, idempotence'ın korunması, ilerleme durumunun bildirilmesi ve stack trace'in gereksiz yere gösterilmemesi temel unsurlardır
  • Karmaşık özel durumlar azaltılıp tasarım sade tutulursa sağlamlık artar

Empati

  • CLI araçları, geliştiricinin yaratıcı araçlarıdır; bu yüzden kullanımı keyifli olmalıdır
  • Kullanıcının aracın kendi tarafında olduğunu hissetmesini sağlayacak şekilde sorunlar yeterince düşünülerek tasarlanmalıdır

Kaos

  • Terminal dünyası tutarsızlıklarla doludur; ancak bu kaos aynı zamanda özgür yaratıcılığın da kaynağıdır
  • "Bir standart üretkenliğe ya da kullanıcı memnuniyetine açıkça zarar veriyorsa, o standardı terk edin." — Jef Raskin

Yönergeler — Temeller (The Basics)

  • Argüman ayrıştırma kütüphanesi kullanın: dil bazında önerilen kütüphaneler arasında Go(Cobra, cli), Python(Click, Typer, Argparse), Rust(clap), Node(oclif) ve daha fazlası bulunur
  • Başarı durumunda çıkış kodu 0, hata durumunda ise 0 dışı bir kod döndürün — betikler başarıyı ve hatayı buna göre ayırt eder
  • Varsayılan çıktı stdout'a, log ve hata gibi mesajlar ise stderr'a gönderilmelidir

Yönergeler — Yardım (Help)

  • -h veya --help bayrağıyla ayrıntılı yardım metni gösterin; aynı ilkeyi alt komutlarda da uygulayın
  • Argümansız çalıştırıldığında kısa yardım gösterin (açıklama, 1-2 örnek, bayrak açıklamaları ve --help yönlendirmesi dahil)
    • jq, bunun iyi uygulanmış bir örneği olarak anılıyor
  • --help / -h / help subcommand gibi farklı yardım isteme biçimlerinin tümünü destekleyin
  • Yardım metninin üst kısmında web dokümantasyonu bağlantısı ve geri bildirim kanalı sağlayın
  • Örnekleri önce gösterin — karmaşık kullanım senaryolarına kademeli biçimde ilerleyen bir anlatı önerilir
  • Sık kullanılan bayrakları ve komutları yardım metninin üst kısmına yerleştirin (gitin yapısı örnek alınabilir)
  • Kalın başlıklar gibi biçimlendirmeleri taramayı kolaylaştıracak şekilde kullanın; ancak terminalden bağımsız yöntemler tercih edin
  • Kullanıcı yanlış giriş yaptığında niyetini tahmin edip düzeltme önerebilirsiniz — ancak otomatik çalıştırma kararı dikkatle verilmelidir
    • Yanlış giriş basit bir yazım hatası değil, mantıksal bir hata olabilir; ayrıca otomatik düzeltme yapılırsa o sözdizimini kalıcı olarak destekleme yükü doğar

Yönergeler — Dokümantasyon (Documentation)

  • Web tabanlı dokümantasyon sağlayın — aranabilirlik ve bağlantı paylaşımı için gereklidir
  • Terminal tabanlı dokümantasyon sağlayın — kurulu sürümle senkron kalır ve çevrimdışı erişilebilir
  • man sayfası sunmayı değerlendirin — ronn gibi araçlarla üretilebilir; npm help ls gibi alt komut üzerinden erişim önerilir

Yönergeler — Çıktı (Output)

  • İnsan tarafından okunabilirlik önceliklidir — çıktının insan için mi olduğunu TTY olup olmamasına göre belirleyin
  • Metin akışları UNIX'in evrensel arayüzüdür; bu nedenle makine tarafından okunabilir çıktı da desteklenmelidir
  • İnsan dostu çıktı pipe uyumluluğunu bozuyorsa, --plain bayrağıyla düz metin çıktı sunun
  • --json bayrağı verildiğinde JSON biçiminde çıktı destekleyin
  • Başarı durumunda çıktı kısa olsun; gerekmiyorsa hiç çıktı üretmeyin — betik kullanımı için -q seçeneğiyle çıktıyı bastırmayı destekleyin
  • Durum değiştiğinde kullanıcıyı bilgilendiringit pushun uzak branch durumunu göstermesi iyi bir örnektir
  • git status gibi, mevcut sistem durumunu kolayca görmeyi ve sonraki adımı anlamayı sağlayan çıktılar tasarlayın
  • Renkleri bilinçli kullanın; pipe durumu, NO_COLOR, TERM=dumb, --no-color gibi koşullarda renkleri devre dışı bırakmak zorunludur
  • TTY olmayan ortamlarda animasyon veya spinner göstermeyin (CI loglarının kirlenmesini önlemek için)
  • Emoji ve sembolleri yalnızca açıklığı artırdıklarında kullanın (yubikey-agent örnek olarak veriliyor)
  • Yalnızca geliştiricinin anlayacağı bilgiler varsayılan çıktıdan çıkarılmalı, sadece verbose modunda gösterilmelidir
  • stderr'ı log dosyası gibi kullanmayın — varsayılan olarak ERR, WARN gibi log seviyesi etiketlerini göstermeyin
  • Büyük hacimli çıktılarda less gibi bir pager kullanmayı değerlendirin — yalnızca TTY ortamında etkinleştirin ve less -FIRX seçeneğini önerin

Yönergeler — Hatalar (Errors)

  • Öngörülebilir hataları insanın anlayabileceği mesajlarla yeniden yazın (ör. "chmod +w file.txt çalıştırmanız gerekiyor")
  • Sinyal/gürültü oranını koruyun — aynı türden hataları tek bir başlık altında gruplayarak gösterin
  • Önemli bilgileri çıktının son kısmına yerleştirin — kırmızı metni bilinçli ve nadiren kullanın
  • Beklenmeyen bir hata oluştuğunda debug bilgisi ve bug raporu gönderme yöntemi de ekleyin
  • Bug raporu URL'sine bilgileri otomatik doldurarak gönderimi kolaylaştırın

Yönergeler — Argümanlar ve Bayraklar (Arguments and Flags)

  • Argümanlar (args) konuma dayalıdır, bayraklar (flags) ise isme dayalıdır — argümanlara kıyasla bayrakları tercih edin
  • Tüm bayraklar için uzun ad sürümü sağlayın (ör. -h ile birlikte --help)
  • Tek karakterli bayrakları yalnızca sık kullanılanlar için ayırın
  • Bir standart varsa standart bayrak adlarını kullanın (-f/--force, -q/--quiet, -v, --json vb.)
  • Varsayılan değerleri çoğu kullanıcıya uygun olacak şekilde belirleyin
  • Argüman veya bayrak verilmediğinde prompt ile giriş isteyin, ancak etkileşimsiz ortamlarda prompt'u zorlamayın
  • Riskli işlemlerden önce onay isteyin — risk düzeyine göre y/n onayı, dry-run ya da doğrudan metin yazdırma gereksinimi kullanın
    • Risk düzeyi mild (dosya silme), moderate (dizin silme, uzak kaynağı değiştirme), severe (tüm sunucuyu silme) olarak ayrılır
  • Dosya girdi/çıktısında - ile stdin/stdout üzerinden okuma ve yazmayı destekleyin (ör. curl ... | tar xvf -)
  • Bayraklar üzerinden gizli bilgi doğrudan almayın--password-file bayrağı veya stdin kullanımı önerilir (ps çıktısı ve shell geçmişine sızma riski)

Yönergeler — Etkileşimlilik (Interactivity)

  • Prompt ve etkileşimli öğeleri yalnızca stdin bir TTY ise gösterin
  • --no-input verildiğinde tüm prompt'ları devre dışı bırakın
  • Parola girişinde echo'yu kapatın (girilen içerik ekranda görünmemeli)
  • Kullanıcının her zaman kaçış yolu olduğunu açıkça gösterin — Ctrl-C her zaman çalışır durumda kalmalıdır

Yönergeler — Alt Komutlar (Subcommands)

  • Alt komutlar arasında bayrak adları ve çıktı biçimi tutarlılığını koruyun
  • Karmaşık araçlarda noun verb veya verb noun biçiminde iki seviyeli alt komut yapısı kullanın (ör. docker container create)
  • Belirsiz ya da birbirine benzeyen adlı alt komutlardan kaçının (ör. update ile upgradeı birlikte kullanmamak)

Yönergeler — Sağlamlık (Robustness Guidelines)

  • Girdi doğrulamasını erken aşamada yapın; hatalı verilerde anlaşılır bir hata verip erkenden çıkın
  • Tepkisellik hızdan daha önemlidir — 100 ms içinde bir şeyler gösterin
  • Uzun süren işlerde ilerleme çubuğu (progress bar) sağlayın — Python(tqdm), Go(schollz/progressbar), Node(node-progress) kütüphaneleri kullanılabilir
  • Paralel işlem sırasında çıktının birbirine karışmamasına dikkat edin
  • Ağ zaman aşımı ayarlayın — varsayılan değerlerle birlikte, sonsuza kadar beklemeyi önleyin
  • Geçici bir hatadan sonra yeniden denendiğinde önceki durumdan devam edebilecek şekilde tasarlayın
  • Crash-only tasarım benimseyin — temizleme işlemine gerek kalmadan hemen sonlanabilen yapılarla idempotence sağlayın

Yönergeler — Geleceğe Uyumluluk (Future-proofing)

  • Değişiklikleri geriye dönük uyumlu, eklemeli (additive) biçimde tutun
  • Uyumluluğu bozacak bir değişiklikten önce program içinde önceden uyarı gösterin
  • İnsanlara yönelik çıktının değişmesi genelde kabul edilebilir — betikler için --plain ve --json kullanımını teşvik edin
  • Catch-all alt komutları yasaklayın — ileride aynı adda alt komut eklenememesi sorununa yol açar
  • Alt komut kısaltmalarını otomatik kabul etmeyin — yalnızca açıkça tanımlanmış alias'lara izin verin ve bunları kararlı biçimde koruyun
  • "Time bomb" tasarımlardan kaçının — 20 yıl sonra da çalışabilecek şekilde dış bağımlılıkları en aza indirin

Yönergeler — Sinyaller ve Kontrol Karakterleri (Signals)

  • Ctrl-C (INT sinyali) alındığında hemen çıkın; temizleme işlemleri için zaman aşımı belirleyin
  • Temizleme sırasında Ctrl-C tekrar girilirse zorla çıkılabileceğini belirtin (Docker Compose örneğine bakılabilir)
  • Programı, temizleme işlemleri tamamlanmamış bir durumda başlatılabileceği varsayımıyla tasarlayın

Yönergeler — Yapılandırma (Configuration)

Yapılandırma uygulama önceliği (yüksek → düşük):

  • Bayraklar → mevcut shell ortam değişkenleri → proje düzeyi yapılandırma (.env) → kullanıcı düzeyi yapılandırma → sistem geneli yapılandırma

Yapılandırma türüne göre öneriler:

  • Her çağrıda değişen ayarlar (debug seviyesi, dry-run): bayrak kullanın

  • Proje ya da makineye göre değişen ayarlar (yollar, renk, HTTP proxy): bayrak + ortam değişkeni kombinasyonu

  • Proje genelinde paylaşılan ayarlar (Makefile, package.json türü): sürüm kontrolündeki dosyaları kullanın

  • XDG Base Directory spec'e uyun — ~/.config tabanlı yapılandırma yolları önerilir (yarn, fish, neovim, tmux gibi araçlar destekler)

  • Başka programların yapılandırma dosyalarını otomatik değiştirirken mutlaka kullanıcı onayı alın


Yönergeler — Ortam Değişkenleri (Environment Variables)

  • Ortam değişkenleri, çalışma bağlamına göre değişen davranışlar için uygundur
  • Adlarda yalnızca büyük harf, rakam ve alt çizgi kullanın; rakamla başlamayın
  • Tek satırlık değerler önerilir — çok satırlı değerler env komutuyla uyumluluk sorunlarına yol açabilir
  • NO_COLOR, DEBUG, EDITOR, HTTP_PROXY, SHELL, TMPDIR, HOME, PAGER gibi genel amaçlı ortam değişkenlerini önce kontrol edin
  • Proje bazlı .env dosyası okuma desteği önerilir — ancak .env, resmî yapılandırma dosyasının yerini tutmaz
    • .env sınırlamaları: sürüm kontrolüne girmez, geçmiş tutmaz, yalnızca string tipine sahiptir, kodlama sorunlarına açıktır
  • Gizli bilgileri ortam değişkenlerinden okumayın — tüm süreçlere yayılır, loglara sızabilir, Docker inspect veya systemctl show ile ortaya çıkabilir
    • Gizli bilgiler yalnızca kimlik bilgisi dosyaları, pipe, AF_UNIX socket'leri veya secret management servisleri üzerinden alınmalıdır

Yönergeler — Adlandırma (Naming)

  • Basit ve akılda kalıcı sözcükler kullanın — aşırı genel adlar başka komutlarla çakışabilir
  • Yalnızca küçük harf ve gerekirse tire kullanın (curl iyi bir örnek, DownloadURL kötü bir örnek)
  • Kısa tutun; ancak cd, ls, ps gibi aşırı kısa adlar genel amaçlı yardımcı programlar için ayrılmıştır
  • Docker Compose'un önceki adları olan plumfigdocker compose, yazma kolaylığının adlandırmada ne kadar önemli olduğunu gösteren gerçek bir örnektir

Yönergeler — Dağıtım (Distribution)

  • Mümkünse tek bir binary olarak dağıtınPyInstaller gibi araçlar kullanılabilir
  • Tek binary mümkün değilse platforma özgü yerel paket kurucularını kullanın
  • Kaldırma yöntemini kurulum talimatlarının altında açıkça belirtin

Yönergeler — Analiz Verileri (Analytics)

  • Kullanıcı onayı olmadan kullanım verisi veya crash verisi göndermeyin
  • Veri toplanıyorsa nelerin toplandığını, neden toplandığını, nasıl anonimleştirildiğini ve ne kadar süre saklandığını açıkça paylaşın
  • Varsayılan olarak opt-in önerilir — opt-out kullanılacaksa ilk çalıştırmada ya da web sitesinde açıkça bildirin
    • Angular.js (açık opt-in), Homebrew (Google Analytics, SSS açıklaması), Next.js (varsayılan etkin anonim istatistikler) olmak üzere üç örnek tanıtılıyor
  • Analize alternatif olarak web dokümantasyonu ölçümleme, indirme sayısı ölçümleme ve kullanıcılarla doğrudan görüşme kullanılabilir

1 yorum

 
GN⁺ 2024-02-07
Hacker News yorumu
  • Günümüzde birçok insan komut satırının ne olduğunu bilmiyor ve neden kullanmaları gerektiğiyle de ilgilenmiyor.

    • 1980'lerde de durum benzerdi, ancak bugün komut satırını bilen insan sayısı her zamankinden fazla. Buna CLI'nin (komut satırı arayüzü) altın çağı denebilir.
  • Betiklerde alt komutların keyfi kısaltmalarına izin vermeyin. Örneğin mycmd install yerine mycmd ins veya mycmd i kullanımına izin verirseniz, i ile başlayan yeni bir komut ekleyemezsiniz.

    • Betiklerde kısa argüman kullanmaktan kaçınılmalı. Kısa argümanlar insanlar için yazarken tuşlamayı azaltan bir kolaylık sağlar, ancak betiklerde açıkça yazmanın maliyeti düşüktür ve okuma/yazma oranı düşünüldüğünde daha tercih edilir.
  • --dry-run seçeneğini değerlendirin. Gerçek değişiklik yapmadan hangi işlemlerin gerçekleştirileceğini önceden gösteren bu özellik, aracı öğrenmek ve karmaşık seçeneklerin doğru ayarlanıp ayarlanmadığını kontrol etmek için çok faydalıdır.

  • stdout etkileşimli bir terminal değilse animasyon göstermeyin. Bu, CI günlük çıktısında ilerleme çubuğunun Noel ağacına dönmesini engeller.

    • stdout üzerinde asla animasyon göstermeyin. stderr günlükleme, bilgi verme vb. içindir; stdout ise tty olup olmadığına bakılmaksızın her zaman yararlı çıktı sağlamalıdır.
  • Sembolleri ve emojileri yalnızca açıklığı artırdıklarında kullanın.

    • Semboller ve emojiler terminaller arasında tutarlı şekilde render edilmeyebilir ve kullanıcı tercihine göre sevilebilir ya da sevilmeyebilir; bu yüzden çok dikkatli kullanılmalıdır.
  • Bugünkü Unix komut satırı bir yandan "şaşırtıcı derecede kullanışlı", diğer yandan da "tasarımsal kusurlara" sahip.

    • Unix komut satırının neden kullanışlı olduğu, aynı işi C veya Rust ile yapmanın ne kadar süreceği düşünülünce anlaşılır.
    • Tasarımsal kusurlar, komut satırı arayüzünün aynı anda hem insanlar hem de makineler tarafından okunabilir olması gerekliliğinden kaynaklanır. Bu sorunu çözmenin standart bir yolu yoktur.
  • CLI çok büyük ve iç içe geçme gerektiriyorsa (aws gibi) bunun dışında, çoğu uygulamanın tüm seçenekleri yardım çıktısında göstermesini ve kullanıcının gerekeni bulmak için less kullanmasını tercih ederim.

  • Geleneksel olarak UNIX komutları, esas olarak başka programlar tarafından kullanılacağı varsayımıyla yazılmıştır.

    • Aslında etkileşimli oturum kabuğunda interaktif kullanım için tasarlanmışlardı. Çıktı üreten programlar ile "sessiz" metin filtreleri olarak ayrılıyorlardı ve karmaşık programlar C ile yazılıyordu.
  • Parolaları ortam değişkenlerinden okumayın.

    • Parolalar yalnızca kimlik bilgisi dosyaları, pipe'lar, AF_UNIX soketleri, gizli bilgi yönetim servisleri veya diğer IPC mekanizmaları üzerinden alınmalıdır.
  • CLI yönergeleri hakkında en kapsamlı kitap Eric Raymond'un kitabıdır.

    • Epey zaman geçmiş olsa da, clig.deve göz atınca zamanla görüşlerin oldukça değiştiği görülüyor.