İçeriğe geç

Django Waffle & Feature Flags

Waffle, Django projelerinde kullanılan güçlü bir feature-flagging yada diğer bilinen adıyla feature-toggling yönetim aracı. Bugün bu pakete göz atacağız. Fakat üzerine konuşmadan önce;

Feature Flags nedir?

Feature flagler bir yazılım geliştirme tekniğidir. Projenizin, yeteneklerini dallandırmaya ve yürütme esnasında modifiye ederek davranışını değiştirmesine, birden fazla featureın, code base üzerinde branchlere dağıtılmadan var olmasına olanak sağlar.

Bir flage bağlı feature’ın çalışması yeni bir deployment gerektirmeksizin aktif/deaktif edilebilir, ya da sadece belirli bir kullanıcı grubunun kullanımına sunulabilir.

Test edilebilirlik, kullanıcıdan feedback toplamayı kolaylaştırmış, daha az deployment yapmış olursunuz. Uygulamanızın yeni feature geçişlerini daha yumuşak ve risksiz yönetebilirsiniz.

Yazılım dünyasında flagler, feature-flipper, feature-switch, feature-gate olarak anılabilir.

Teori tanımının ardından use-case bir kaç örnek verecek olursak,

Uygulamanıza kayıt sırasında kullanılacak olan maliyetli bir feature entegre etmek, aynı CRM’i kullanan müşterileriniz, CRM özelliklerini özelleştirmek istiyor olabilir veya footerda yenilikçi bir tasarım diline başvuracaksınız. Fakat Kullanıcılar tarafından iyi karşılanacağı ya da yatırım yapmaya değer bir feature olup olmadıgından emin değilsiniz. Dert değil. Uygulamanıza bir feature flag eklersiniz, daha sonra bunu sadece şirketinizin kullanıcılarına açarak feedback toplamaya başlayabilirsiniz.

Yukarıdaki örnekler, kapatılıp açılabilir özellik perspektifinden bakıldığında fazlasıyla genişletilebilir.

Kabaca;

def index(request):
    if feature_flag_is_active('awesome_feature_flag'):
        do_awesome()
    else:
        do_regular()

Özet olarak bu şekilde fakat bu teknik hakkında Atlassian’ın güzel bir blog yazısı var dahasını merak edenler için bu bir linktir.


Django Waffle.

Waffle, flagleriniz için bir yönetim paketi. Kolay bir kullanımı, django admin entegrasyonu ve toplu şekilde flaglerinizi yönetebilieceğiniz yönetim komutlarına sahip.

Not. Waffle, flaglerde kullanıcılara göre segmentasyon yaptığı için bir auth sistemine ve Django’nın kullanıcı grup yapısına ihtiyaç duyar.

Kurulum ve konfigürasyon.

$ pip install django-waffle

Ardından wafflei uygulama olarak kayıt edip, WaffleMiddleware’i tanımlayalım. Waffle flagleri veritabanı üzerinde barındırır, dolayısıyla gerekli veritabanı migrationlarını gerçekleştirelim.

INSTALLED_APPS = [
    ...
    'waffle',
]
MIDDLEWARE = [
    ...
    'waffle.middleware.WaffleMiddleware',
]
$ python manage.py migrate

Waffle feature-flagging icin 3 farklı model ve teknik kullanır.

  • Switches.
  • Samples.
  • Flags.

Sırasıyla kullanımlarına göz atalım.

Switches.

Switchler, basitçe birer boolean değerdir. Herhangi bir kullanıcı bazlı segmentasyon yapılmadan her zaman tüm kullanıcılar için ya açık ya kapalıdır.

Örnek bir switchin admin panelinden eklenmesi;

switch creation on admin panel

Bir switch oluşturulduğunda template ya da view üzerinde yürütme esnasında dallanmalar oluşturabilirsiniz.

Switchlerin view üzerinde kullanımı;

from django.views import View
from waffle import switch_is_active


class LandingPage(View):
    def get(self, request, *args, **kwargs):
        if switch_is_active("SHOW_NEW_LANDING_PAGE"):
            return render(request, "landing_page_v2.html", {})
        return render(request, "landing_page.html", {})

Switch sayesinde dinamik olarak render edilecek template değiştirilebilir.

Template üzerinde kullanımına göz atacak olursak;

FANCY_FOOTER adlı bir switch oluşturduğumuzu varsayalım.

Template üzerindeki kullanım için, ilk olarak waffleın sunduğu template tagleri içeri aktarmamız gerekiyor.

{% load waffle_tags %}

Daha sonra, kontrol edilecek bloğu aşağıdaki gibi kurgulayabiliriz.

{% switch 'FANCY_FOOTER' %}
    <footer>
        fancy footer content.
    </footer>
{% else %}
    <footer>
        regular footer content.
    </footer>
{% endswitch %}

Switchlerin basit birer bool değer olduğundan bahsetmiştik ve kullanımı bu kadar kolay 🙂

Not. Switchler az sonra inceleyeceğimiz Flaglerin aksine kontrol sırasında bir request nesnesine ihtiyaç duymadığından, farklı bağlamlarda doğrudan kullanılabilirler. Örneğin management commands.


Samples.

Samples, switchlere benzer bir yapıdadır ve basit bir bool değer üzerine kurgulanırlar. Tek farkı bu işlemin rastgelelik üzerine olmasıdır.

Admin paneli üzerinden ekleyecek olursak;

sample add on django adminView ve template üzerinde kullanımı yine switchler ile benzer.

from django.views import View
from waffle import sample_is_active

class LuckyPersonView(View):

    def get(self, request, *args, **kwargs):
        if sample_is_active('LUCKY_PERSON'):
            return render(request, 'lucky_person.html', {})
        return render(request, 'bad_luck.html', {})
{% load waffle_tags %}

{% sample 'LUCKY_PERSON' %}
    <h1>
      you won the free spin!
    </h1>
{% else %}
    <h1>
        nothing to see here :/
    </h1>
{% endsample %}

Not. Sampleların döndürdüğü değerler rastgeledir. Bir blok üzerinde birden fazla kere aynı sampleın değerini kontrol edip işlem yapacaksanız, tek bir kere kontrol yapmalı ve bir değişkene atamalısınız. Her çağrım birbirinden bağımsız değer döndürecektir ve aynı değeri döndüreceği garanti değildir.

Aynı switchler gibi request nesnesine ihtiyaç duymayan samplelar farklı bağlamlarda kullanılabilir. eg management commands.


Flags.

Son olarak flagler ile switch ve samplelardan çok daha kompleks ve esnek yapılar oluşturarak featurelarınızı kontrol edebilirsiniz.

Flagler ile özellikleri belirli kullanıcılara ya da gruplara kullanım için aktif hale getirebilirsiniz.

Admin paneli üzerinden bir flagi ekleyip hangi alanın neye karşılık geldiği üzerine konuşacak olursak.

Örnek bir flagin admin paneli üzerinden eklenmesi;

flag admin panel addName:  Flagin ismi.

Everyone: Flagin global ve diğer kritierlere bağlı olup olmayacağını belirtir. Yes&No değerlerinde diğer tüm krititerler devre dışı. Diğer sınırlamaları kullanacaksanız, Unknown.

Percent: Samplelar ile benzer. Yüzde kaç oranında bu flagin aktif olacağı.

Testing: Test modu. Eğer test modu açıksa testler sırasında flagin değeri query param, header üzerinden değiştirilebilir.

Superusers: Tüm süper kullanıcılar için aktif olacak mı?

Staff Users: Tüm staff kullanıcılar için aktif olacak mı?

Authenticated: Tüm giriş yapmış kullanıcılar için aktif olacak mı?

Languages: Hangi dil kodlarında aktif olacak? Çoklu dile sahip bir uygulamanız varsa , ile dil kodlarını ayırarak girebilirsiniz.

Groups: Hangi gruplar için flag aktif olacak? Örneğin test aşamasında sadece şirket üyeleri grubu oluşturup bağlayabilirsiniz.

Users: Hangi kullanıcılarda flag aktif olacak?


Yukarıda açıkladığımız kriterlere uygun bir kullanıcı bulunması durumunda flag aktif, feature kullanılabilir olacaktır.

View üzerindeki kullanımı yine switch ve samplelar ile benzer.

class LoginView(View):
    def get(self, request, *args, **kwargs):
        if flag_is_active(request, "TWO_FACTOR_AUTH"):
            return render(request, "two_factor_login.html", {})
        return render(request, "regular_login.html", {})

class TwoFactorLoginConfirm(View):
    def post(self, request, *args, **kwargs):
        # only demonstration dont use on production 😂
        if not flag_is_active(request, "TWO_FACTOR_AUTH"):
            return HttpResponseForbidden()
    
        user = authenticate(**request.POST)

        if "code" in request.POST:
            verify = verify_code(code)
            if verify:
                login(request, user)
                return redirect("landing_page")
            return render(request, "two_factor_auth_fail.html", {})
        
        code = generate_two_factor_code()
        send_confirmation_code_sms(user.phone, code)

        return render(request, "two_factor_auth_confirm.html", {})

Template üzerindeki kullanımı;

{% load waffle_tags %}

{% flag 'NEW_FLAG'  %}
<p> flag active.</p>
{% else %}
<p>flag deactive.</p>
{% endflag %}

Mixinler.

Son olarak bazı endpointleri bir flag, switch veya sample arkasına koyarak, featureın aktif olduğu durumda viewın yürütülmesini aksi durumdaysa 404 dönülmesini isteyebilirsiniz.

waffle’ın bu gereksinim içinde güzel bir çözümü var.

from waffle.mixins import WaffleFlagMixin, WaffleSwitchMixin, WaffleSampleMixin

class MyClass(WaffleFlagMixin, View):
    waffle_flag = "my_flag"

class MyClass(WaffleSwitchMixin, View):
    waffle_switch= "my_switch"

class MyClass(WaffleSampleMixin, View):
    waffle_sample= "my_sample"

Sonuç.

Feature Flag tekniğine ve Waffle üzerine genel bir resim çizdiğimizi düşünüyorum. Daha derinlemesine bilgi ve burada değinmediğimiz (örneğin testing) gibi konuları inceleyebilmeniz için döküman linki burada.

İyi günler!

Tarih:Blog

İlk Yorumu Siz Yapın

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.

Göster
Gizle