Skip to content

fbidu/feature-change

Repository files navigation

Feature Change

from feature_change import change

def log_diff(**kwargs):
    # This will be called if the results of
    # `new_function` and `current_function` differ

def log_call(**kwargs):
    # This will be called on every call to `current_function`

def new_function():
    # This is a new function we want to check if
    # it *really* behaves like the current

@change(new=new_function, on_diff=log_diff, on_call=log_call)
def current_function():
    # The current function. Just decorate it with `change` and everything
    # will work! No need to change the code!

Table of Contents


A Python decorator that helps you run two different versions of a function at the same time and track differences without breaking the current behavior.

It is based on Auth0's feature-change for Node.js. Original article

Usage

  1. Install with pip or your favorite package manager pip install feature-change

    a. If you can't or don't want to add a new dependency, feel free to just copy the current code from change.py. It is pretty straightforward.

  2. Define the logging functions you want to be called. They will receive two keyword arguments current and new, with the current and the new result.

    def log_diff(current, new):
        print(f"The current value is {current} but the new one is {new})
  3. Decorate your current function with @change, passing the new implementation that you want to test and any logging function you have defined. Currently you can define logging on two occasions ― on_diff will be called if the results are different and on_call will always be called

    @change(new=new_func, on_diff=log_diff)
    def current_function():
        ...

Defining the Logging Functions

The functions used for logging will receive two keyword arguments ― current and new. They will contain the return of both the current and the new functions.

change can call custom functions on two situations:

  • on_call ― everytime the old function is called

  • on_diff ― when there's a difference between the return of the current function and the new one.

Working Example

from random import random
from feature_change import change

def log_call(current, new):
    print("Call detected!")

def log_diff(current, new):
    print(f"Difference detected. Current = {current}; new = {new}")

def new_sum(a, b):
    """
    This function will be wrong on 50% of the calls
    """
    if random() > 0.5:
        return 0

    return a + b

@change(new=new_sum, on_call=log_call, on_diff=log_diff)
def current_sum(a, b):
    return a + b

for _ in range(10):
    current_sum(1, 41)

This code will return something like

Call detected!
Difference detected. Current = 42; new = 0
Call detected!
Call detected!
Call detected!
Difference detected. Current = 42; new = 0
Call detected!
Call detected!
Call detected!
Call detected!
Difference detected. Current = 42; new = 0
Call detected!
Call detected!
Call detected!
Difference detected. Current = 42; new = 0
Call detected!
Call detected!
Call detected!
Call detected!
Call detected!
Difference detected. Current = 42; new = 0
Call detected!
Difference detected. Current = 42; new = 0
Call detected!
Difference detected. Current = 42; new = 0
Call detected!
Call detected!

Keep in mind that the result may be different in your machine because the new function fails randomly.

Warnings

  1. Both functions will be called ― be aware of side effects like database writes. If both functions write to a database, they will both be executed, leading to possible inconsistencies.

  2. Be aware of slow logs ― the callables you define for on_diff and on_call should not be very expensive. You could, by example, just count how many times on_diff was called using some fast db like redis.

  3. Equality is simple ― currently the results are checked using simply the == operator. Keep that in mind if your results are complex objects whose equality is not well defined. An optional argument for a custom equality checker will be add on the future.

Other Libs

Auth0's feature-change is based on GitHub's scientist.

scientist itself has a lot more features than this lib and it also has implementations on different languages

About

A decorator that helps you run two different versions of a function at the same time and track differences *without* breaking the current behavior!

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages