Es ist sinnvoll, einen Schritt zurückzutreten und zu abstrahieren, was mit A/B-Tests erreicht werden soll, bevor man in den Code eintaucht. Was genau brauchen wir, um einen Test durchzuführen?
- Ein Ziel, das eine Bedingung hat
- Mindestens zwei verschiedene Wege zur Erfüllung der Zielbedingung
- Ein System, das die Zuschauer auf einen der Pfade schickt
- Ein System zur Aufzeichnung der Ergebnisse der Prüfung
In diesem Sinne sollten wir über die Umsetzung nachdenken.
Die Zielsetzung
Wenn wir im Web von einem Ziel sprechen, meinen wir in der Regel, dass ein Nutzer eine bestimmte Seite erreicht oder eine bestimmte Aktion durchführt, z. B. sich erfolgreich als Nutzer registriert oder zur Kasse geht.
In Django können wir das auf verschiedene Arten modellieren - vielleicht naiv innerhalb einer Ansicht, indem wir eine Funktion aufrufen, wenn ein Ziel erreicht wurde:
def checkout(request):
a_b_goal_complete(request)
...
Aber das hilft uns nicht weiter, weil wir den Code überall dort hinzufügen müssen, wo wir ihn brauchen - und wenn wir irgendwelche pluggable Anwendungen verwenden, möchten wir deren Code nicht bearbeiten, um unseren A/B-Test hinzuzufügen.
Wie können wir A/B-Ziele einführen, ohne den Code der Ansicht direkt zu bearbeiten? Was ist mit einer Middleware?
class ABMiddleware:
def process_request(self, request):
if a_b_goal_conditions_met(request):
a_b_goal_complete(request)
Das würde uns ermöglichen, A/B-Ziele überall auf der Website zu verfolgen.
Woher wissen wir, dass die Bedingungen eines Ziels erfüllt sind? Der Einfachheit halber schlage ich vor, dass wir wissen, dass die Bedingungen eines Ziels erfüllt sind, wenn ein Benutzer einen bestimmten URL-Pfad erreicht. Als Bonus können wir dies messen, ohne uns die Hände in einer Ansicht schmutzig zu machen. Um auf unser Beispiel der Registrierung eines Benutzers zurückzukommen, könnten wir sagen, dass dieses Ziel erfüllt ist, wenn der Benutzer den URL-Pfad erreicht:
/registrierung/vollständig
Wir definieren also a_b_goal_conditions_met
:
a_b_goal_conditions_met(request):
return request.path == "/registration/complete":
Pfade
Wenn man über Pfade in Django nachdenkt, ist es naheliegend, auf die Idee zu kommen, verschiedene Vorlagen zu verwenden. Ob es einen anderen Weg gibt, muss noch erforscht werden. Bei A/B-Tests macht man kleine Unterschiede zwischen zwei Seiten und misst die Ergebnisse. Daher sollte es eine Best Practice sein, ein einziges Basis-Path-Template zu definieren, von dem alle Paths zum Goal ausgehen sollten.
Wie sollen diese Vorlagen dargestellt werden? Ein Dekorator ist wahrscheinlich ein guter Anfang - es ist eine bewährte Praxis in Django, einen Parameter einzuschließen template_name
zu Ihren Ansichten kann ein Dekorator diesen Parameter zur Laufzeit ändern.
@a_b
def registration(request, extra_context=None, template_name="reg/reg.html"):
...
Man könnte sehen, dass dieser Dekorator entweder die umhüllte Funktion introspektiert und die template_name
oder die richtigen Vorlagen von irgendwoher (z. B. aus einem Modell) nachschlagen. Wenn wir den Dekorator nicht zu jeder Funktion hinzufügen wollten, könnten wir dies als Teil unserer ABMiddleware implementieren:
class ABMiddleware:
...
def process_view(self, request, view_func, view_args, view_kwargs):
if should_do_a_b_test(...) and "template_name" in view_kwargs:
# Modify the template name to one of our Path templates
view_kwargs["template_name"] = get_a_b_path_for_view(view_func)
response = view_func(view_args, view_kwargs)
return response
Wir müssten auch eine Möglichkeit hinzufügen, um zu verfolgen, in welchen Ansichten A/B-Tests laufen usw.
Ein System zum Senden von Zuschauern auf einen Pfad
Theoretisch ist dies einfach, aber es gibt viele verschiedene Implementierungen, so dass es nicht klar ist, welche die beste ist. Wir wissen, dass ein gutes System die Benutzer gleichmäßig auf die Pfade aufteilen sollte - es muss eine Hash-Methode verwendet werden - vielleicht könnte man den Modulus des Memcache-Zählers geteilt durch die Anzahl der Pfade verwenden - vielleicht gibt es einen besseren Weg.
Ein System zur Aufzeichnung der Testergebnisse
Wir müssen aufzeichnen, wie viele Benutzer ging, was Pfad - wir brauchen auch den Zugriff auf diese Informationen, wenn der Benutzer das Ziel erreicht (wir müssen in der Lage sein, zu sagen, welche Pfad sie kamen, um die Bedingung des Ziels erfüllt) - wir verwenden eine Art von Model (s), um die Daten und entweder Django Sessions oder Cookies, um die Pfad-Informationen zu persistieren, bis der Benutzer die Goal-Bedingung erfüllt.
Abschließende Überlegungen
Ich habe eine Menge Pseudo-Code für die Implementierung von A/B-Tests in Django gegeben - das oben genannte ist keineswegs eine vollständige Lösung, sondern ein guter Anfang zur Schaffung eines wiederverwendbaren Frameworks für A/B-Tests in Django.
Als Referenz können Sie sich Paul Mar's Seven Minute A/Bs auf GitHub ansehen - es ist die ROR-Version des oben genannten! http://github.com/paulmars/seven_minute_abs/tree/master
Update
Bei näherer Betrachtung und Untersuchung des Google Website Optimizer wird deutlich, dass die obige Logik klaffende Löcher aufweist. Durch die Verwendung von verschiedenen Vorlagen, um Pfade darzustellen, brechen Sie alle Zwischenspeicherung auf der Ansicht (oder wenn die Ansicht zwischengespeichert wird, wird es immer den gleichen Pfad dienen!). Statt der Verwendung von Pfaden würde ich stattdessen GWO-Terminologie stehlen und die Idee von Combinations
- d.h. ein bestimmter Teil einer Vorlage wird geändert - z.B. wird die <h1>
Tag einer Website.
Die Lösung wäre die Verwendung von Template-Tags, die in JavaScript umgewandelt würden. Wenn die Seite im Browser geladen wird, stellt das JavaScript eine Anfrage an Ihren Server, der eine der möglichen Kombinationen abruft.
Auf diese Weise können Sie mehrere Kombinationen pro Seite testen und gleichzeitig das Caching beibehalten!
Update
Es gibt immer noch Raum für Template-Switching - sagen wir zum Beispiel, Sie führen eine völlig neue Homepage ein und wollen deren Leistung im Vergleich zur alten Homepage testen - Sie würden immer noch die Template-Switching-Technik verwenden wollen. Dabei müssen Sie sich allerdings überlegen, wie Sie zwischen einer bestimmten Anzahl von zwischengespeicherten Versionen der Seite umschalten können. Um dies zu tun, müssten Sie die Standard-Cache-Middleware außer Kraft setzen, um zu sehen, ob ein A/B-Test auf der angeforderten URL läuft. Dann könnte es die richtige gecachte Version zum Anzeigen auswählen!!!
Update
Unter Verwendung der oben beschriebenen Ideen habe ich eine pluggable App für grundlegende A/B-Tests Django implementiert. Sie können es von Github bekommen:
http://github.com/johnboxall/django-ab/tree/master