Giter Site home page Giter Site logo

featurevisor / featurevisor Goto Github PK

View Code? Open in Web Editor NEW
512.0 4.0 26.0 3.5 MB

Feature flags, experiments, and remote config management with GitOps

Home Page: https://featurevisor.com

License: MIT License

Makefile 0.17% JavaScript 2.48% TypeScript 97.27% HTML 0.05% CSS 0.03%
experimentation experiments feature flags javascript nodejs segmentation feature-flags feature-management feature-toggles

featurevisor's Introduction

Featurevisor

Feature management for developers

Manage your feature flags and experiments declaratively from the comfort of your Git workflow.


What is Featurevisor?

Featurevisor is a solution for managing your feature flags, experiments, and remote config. It's built for developers, by developers.

Workflow can be broken down into 3 steps:

  • 1️⃣ Manage feature flags, experiments, and remote config in your Git repository
  • 2️⃣ Trigger CI/CD pipeline to generate and upload datafiles (static JSON files) to your CDN
  • 3️⃣ Fetch and evaluate datafiles in your applications using Featurevisor SDKs

More documentation available at https://featurevisor.com.

Supported features:

  • 🚩 Feature flags: classic on/off switches
  • 🆎 Experimentation: a/b and multivariate tests
  • 👥 Segmentation: reusable conditions for targeting groups of users
  • ↗️ Gradual rollouts: avoid big bang releases, and go from 0% to 100% rollout gradually
  • 🔧 Variables: store key/value pairs for remote config namespaced under each feature
  • 🎯 Consistent bucketing: makes sure same user gets same variation across devices/sessions
  • 🌏 Multiple environments: Go beyond just staging and production to fit your needs
  • 🏷️ Tagging: Load only features relevant to your application, avoiding bloat
  • 👁️ Tracking: Track experiment activations with your favourite analytics tool

SDK support:

Swift and Kotlin SDKs are coming soon for native apps.

Quick start

You are recommended to see a more detailed quick start guide here: https://featurevisor.com/docs/quick-start/.

The whole process can be broken down into 3 steps:

Step 1: Create a Featurevisor project

Use npx to scaffold a new Featurevisor project:

$ mkdir my-featurevisor-project && cd my-featurevisor-project

$ npx @featurevisor/cli init
$ npm install

You can now create and manage your feature flags, experiments, and remote config in this directory expressed as YAMLs.

See the building block guides here:

  • Attributes: building block for conditions
  • Segments: conditions for targeting users
  • Features: feature flags and variables with rollout rules

Step 2: Build and deploy datafiles

Once the project is ready, you can build your datafiles (JSON files containing configuration of your feature flags):

$ npx featurevisor build

You will find the output in dist directory, that you can upload to your CDN.

See further guides here:

A fully functioning example for deploying with Cloudflare and GitHub Actions (for free) is available here.

Step 3: Consume datafiles with Featurevisor SDKs

You can now consume the datafiles from your CDN in your applications directly using Featurevisor SDKs.

For Node.js and browser environments, install the JavaScript SDK:

$ npm install --save @featurevisor/sdk

Now you can initialize the SDK with the URL of your datafile, and evaluate your feature flags:

import { createInstance } from "@featurevisor/sdk";

// Initialize the SDK
const f = createInstance({
  datafileUrl: "https://cdn.yoursite.com/datafile.json",
  onReady: () => console.log("Datafile has been fetched and SDK is ready"),
});

// Evaluate a feature flag
const featureKey = "myFeature";
const context = {
  userId: "user-123",
  country: "nl",
};

const isEnabled = f.isEnabled(featureKey, context);
const variation = f.getVariation(featureKey, context);
const variable  = f.getVariable(featureKey, "someVariableKey", context);

Learn more about SDK usage here: https://featurevisor.com/docs/sdks/.

Packages

Package Version Description
@featurevisor/cli CLI package
@featurevisor/core Core package used by CLI
@featurevisor/react React package
@featurevisor/sdk Universal SDK for both Node.js and browser
@featurevisor/site Static site generator for your project
@featurevisor/types Common typings
@featurevisor/vue Vue.js package

License

MIT © Fahad Heylaal

featurevisor's People

Contributors

1e9y avatar aliahmad490 avatar bchelkowski avatar caioquirino avatar dependabot[bot] avatar dev-hans-programmer avatar fahad19 avatar florianrappl avatar hiddeco avatar jagadeeshgade008 avatar joanrm20 avatar leimonio avatar mattmoreira avatar maxgallo avatar meirroth avatar pawdat avatar sarkurd avatar threepalmtrees avatar tumnius avatar vaddisreeharsha avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

featurevisor's Issues

Support NOT operator in Segments

Currently

and & or conditions are supported both in Segment's conditions, and also when defining Segments themselves in Features.

Acceptance criteria

We want to introduce also a not operator.

Status site generator

Challenge

It is difficult to see the current set of features that are active in any given environment, since all the info are scattered around YAML files.

We wish to be able to get an overview of the current state in a website, that can be internally hosted in an organization like at https://featurevisor-status.yourcompany.com

API directions

Upon PR merge in the workflow, the CI pipeline can take care of building a static website based on the available YAMLs.

Can happen via command line:

$ featurevisor build-status-site

And it will output generated website content at ./out directory.

We will not reuse the ./dist directory since that is reserved for generated datafiles.

Afterwards, it is up to you to decide how you wish to deploy this static website for your internal organization access.

Static site behaviour

  • Allow switching environments
  • Allow switching tags
  • List features
  • Upon clicking a feature, expand with targeted segments info
  • Quick searching by feature keys

Everything expected to be accessible in the same single page.


This has to be done entirely in a different package, either within or outside of this monorepo.

Generated site: introduce sidebar/detail view

Currently

There's top navigation that has links to Feature, Segments, Attributes, and History.

Docs: https://featurevisor.com/docs/site/

Problem

It does not quickly give you an overview of the total number of Features, Segments, and Attributes that exist in the Featurevisor project.

Solution

We can change the UI to be sidebar-based, where top nav becomes sidebar nav.

And the links in sidebar can also show total number of items, like:

  • Features (10)
  • Segments(4)
  • Attributes(5)
  • History

Technical notes

More info: https://featurevisor.com/docs/site/

All components found here: https://github.com/fahad19/featurevisor/tree/main/packages/site/src/components

Local development tips

$ npm ci
$ npm run bootstrap

Make changes in packages/site, then do:

$ (cd ./packages/site && npm run build)

See the changes in example app:

$ (cd ./examples/example-1 && npm start)

Generated site: add pagination

Currently

Generate site currently lists all entities in one large list.

Problem

This can result into laggy experience in the browser.

Solution

We wish to add pagination for the list view for Features, Segments, and also Attributes.

History page does not have this issue, since it has a "Load more" button at the bottom.

Technical notes

More info: https://featurevisor.com/docs/site/

All components found here: https://github.com/fahad19/featurevisor/tree/main/packages/site/src/components

Local development tips

$ npm ci
$ npm run bootstrap

Make changes in packages/site, then do:

$ (cd ./packages/site && npm run build)

See the changes in example app:

$ (cd ./examples/example-1 && npm start)

Explore Featurevisor integration with PartyKit

Quick summary of how Featurevisor works

  • Manage feature flags via a git repo
  • Merging PR to main/master branch triggers CI/CD pipeline
  • CI/CD pipeline generates datafiles (JSON files with features config) and uploads them to CDN
  • Clients use Featurevisor SDKs to fetch datafiles from CDN and evaluate flags

See quickstart guide: https://featurevisor.com/docs/quick-start/

See example with GitHub Actions and Cloudflare here for deployment here: https://featurevisor.com/docs/integrations/cloudflare-pages/

Refreshing datafile

Featurevisor SDKs can refresh datafile by fetching it again without reloading/restarting the whole application: https://featurevisor.com/docs/sdks/#refreshing-datafile

This can happen either:

  • manually refreshing when application wants to, or
  • setting an interval to refresh every X seconds

Challenge

Given the whole operation is based on static files in a CDN, there's no way to notify already running applications that there are new changes in features configuration, and they should refresh to pull in latest changes.

PartyKit

PartyKit is an SDK designed for creating real-time collaborative applications.

It leverages WebSockets, which can be a way for notifying applications for triggering a new refresh.

What to explore

  • Create a new PartyKit server
  • From a Featurevisor project's CI/CD pipeline, see if we can emit a new event like refresh
  • From clients, listen the refresh event, and call featurevisorInstance.refresh()

API directions

In client apps, the solution may look like this:

import PartySocket from "partysocket";
import { createInstance } from "@featurevisor/sdk";

const socket = new PartySocket({
  host: "localhost:1999", // for local development
  // host: "my-party.username.partykit.dev", // for production
  room: "my-room",
});

const f = createInstance({
  datafileUrl: "https://cdn.mysite.com/datafile.json",

  onReady: () => "Featurevisor SDK has initialized and is ready for use",
  onRefresh: () => "Featurevisor SDK has fetched datafile again",
  onUpdate: () => "Featurevisor SDK has fetched datafile again, and it has new config changes",
});

socket.addEventListener("message", (message) => {
  // trigger a new refresh in Featurevisor SDK
  f.refresh();
});

cc @threepointone

Datafile size reduction: check if Traffic.key is needed

When generating datafiles, we also include the rule keys.

The keys are needed when generating datafiles keeping bucketing consistent, but they may not be needed when evaluating them via SDKs.

If we can remove them, we can reduce the datafile sizes then.

Areas to check

See Traffic.key type as available in @featurevisor/types package.

Expand SDK to fetch datafiles

Currently

SDK is initialilzed by feeding the datafile content during construction, therefore the whole process is synchronous.

Desired

  • SDK instance creation can accept URL for datafile
  • Take the responsibility of fetching it
  • Emit a ready event once datafile is fetched and parsed

API directions

Instance

Given this direction moves towards an asynchronous operation (fetching datafile), SDK initialization can happen via a function call than a class construction.

import { createFeaturevisorInstance } from "@featurevisor/sdk";

const sdk = createFeaturevisorInstance({
  datafileUrl: "https://cdn.yoursite.com/production/datafile-tag-all.json",
});

On ready event

The ready event can be listened to in a few different ways:

sdk.onReady(function () {
  // datafile has been fetched, and sdk is ready to use
});

Or, the ready event listener can also be part of the instance creation options:

const sdk = createFeaturevisorInstance({
  datafileUrl: "https://cdn.yoursite.com/production/datafile-tag-all.json",
  onReady: function () {
    // do your thing...
  },
});

Initial features

Since the SDK instantiation is a non-blocking process, it would be useful if the instance creation can also accept a set of features configuration coming directly from the application itself:

const sdk = createFeaturevisorInstance({
  datafileUrl: "https://cdn.yoursite.com/production/datafile-tag-all.json",
  onReady: function () {
    // do your thing...
  },
  initialFeatures: {
    featureKey: {
      variation: "b",
      variables: {
        foo: "foo value",
      },
    },
    // more features...
  },
});

Until the SDK is ready, if the application starts using the SDK already, the values will be returned based on the initialFeatures option.

FEATUREOWNERS

Background

GitHub has a CODEOWNERS functionality here

Example:

# .github/CODEOWNERS

##
# all files
#
* @global-owner1 @global-owner2

##
# specific file
#
some/file/path.txt @username 

This helps automatically assign code reviewers, and wait for their approval before merging any PRs if affected files belong to one of the path patterns defined in CODEOWNERS file.

Enter Featurevisor

Featurevisor, by default, is filesystem based.

You can fine the definitions spec here:

Features also have the concept of environments, and if you have multiple environments (like staging and production), those rules are defined in one single file together:

# features/my_feature.yml
description: My feature
tags:
  - all

bucketBy: userId

environments:
  production:
    rules:
      - key: "1"
        segments: "*"
        percentage: 100

  staging:
    rules:
      - key: "1"
        segments: "*"
        percentage: 100

Challenge

Having all environments rules defined in one file makes it difficult to make use of CODEOWNERS, because you can't let one group of users own staging rules, while letting another group to own production rules.

There was an exploration of solving this problem in #180, but that still leaves room for abuse.

Proposal

Similar to CODEOWNERS, a new FEATUEOWNERS file can be created.

Assuming we care about GitHub only for now, it would be placed in .github/FEATUREOWNERS next to CODEOWNERS file.

The spec of the file could be like this:

# .github/FEATUREOWNERS

##
# Everything 
#
* @username 

##
# Specific feature (all environments)
#
my_feature @username

##
# Specific feature, with specific environments
#
my_feature staging @user-1
my_feature production @user-2

Workflows set up

These are several events that this solution should react to:

Expectation

I am less opinionated about the full implementation, but my expectation is to have a reusable solution somehow.

So that people who set up Featurevisor projects (which are private repos) can plug this behaviour in with ease.

Once we make it work for GitHub, we can explore a plugin-based solution to this, where GitHub becomes one of the plugins. Leaving room for adding more plugins for other Git hosting providers like GitLab, BitBucket etc.

The solution can live outside of this monorepo.

.Net SDK

@fahad19: As much as I would love to have cross-platform SDK support for Featurevisor, I would suggest we hold on until the JavaScript SDK matures further.
#11 #12

I like the idea. And when the JavaScript SDK becomes mature, I would like to contribute for .Net SDK if possible :)

Semver operators

Introduce new operators in segments:

  • semvergGeaterThan
  • semverGreaterThanOrEquals
  • semverLessThan
  • semverLessThanOrEquals

EventEmitter-like API in SDK

Currently

Consumers can subscribe to onReady, onActivation, onRefresh, and onUpdate events by passing a handler when creating the SDK instance.

Problem

This does not help when we wish to subscribe to any of the mentioned events above AFTER the SDK has already been initialized.

We want to be able to subscribe to them after as well.

API direction

Introduce EventEmitter-like API for subscribing to events from the SDK instance afterwards:

sdk.on('update', function () {
  // do something
});

sdk.removeAllListeners();

Kotlin SDK

As much as I would love to have cross-platform SDK support for Featurevisor, I would suggest we hold on until the JavaScript SDK matures further.

Make SDK more debuggable

Currently

Featurevisor SDK can evaluate variations and variables.

Problem

But it won't tell you how it is computing those values, which you might want to know for a specific feature against a given set of attributes.

Solution

Introduce a logger API, which can be manually set at different levels, outputting logs from various parts of the SDK that allows Users to know what's happening internally.

Show test coverage report

Currently

We do not have any coverage report when we run our tests.

We use Jest, so can we enable that easily.

Changes needed

  • Introduce coverage option when running tests
  • Show coverage output when running tests on GitHub Actions
  • Take the overall coverage number, and then use it as a threshold so future PRs cannot decrease the coverage

Backstage Plugin

I'd like to have a Backstage Plugin that works in a very similar way to the static website output feature already present in Featurevisor.

The Plugin will need to have access to the following

  • Data files
  • Git History (this maybe could be an optimisation after the first version)

I've presented the idea on Backstage as well → backstage/community-plugins#223

Make datafiles refreshable in SDK

Currently

When SDK is provided with a datafileUrl, it fetches the content only once.

Expected

We can make the datafile refreshable, without requiring the application to restart/reload.

API directions

Allow manual refresh:

sdk.refreshDatafile();

Allow automatic interval-based refreshes when creating the SDK instance:

import { createInstance } from "@featurevisor/sdk";

const sdk = createInstance({
  datafileUrl: "https://...",
  refreshInterval: 60 // every 60 seconds
});

This may also lead to a new onRefresh handler similar to onReady:

const sdk = createInstance({
  datafileUrl: "https://...",
  refreshInterval: 60 // every 60 seconds,

  onRefresh: function () {
    // datafile has been refreshed successfully
  },
});

If the newly fetched datafile content's revision property is different that the current datafile content, a new onUpdate event can also be triggered:

const sdk = createInstance({
  datafileUrl: "https://...",
  refreshInterval: 60 // every 60 seconds,

  onUpdate: function () {
    // datafile has been refreshed successfully,
    // and new datafile is of a different version than the one before
  },
});

We should also allow the SDK to stop/start refreshing:

sdk.stop();
sdk.start();

Swift SDK

As much as I would love to have cross-platform SDK support for Featurevisor, I would suggest we hold on until the JavaScript SDK matures further.

Remove variable description from datafiles

Currently

After #165, Variables can have descriptions.

And they are now being also exposed in generated datafiles, even though they do not add any further value there while increasing the datafile size.

Proposal

Remove description of variables when generating datafiles.

Globally accessible SDK instance

Currently

We create a new SDK instance like this:

// featurevisorInstance.js
import { createInstance } from "@featurevisor/sdk";

export const instance = createInstance({
  datafileUrl: "...",
});

If we wish to use this SDK anywhere, we have to pass the instance by reference.

This is somewhat manageable when using other frameworks, since they popularize the idea of passing around values by reference:

Challenges

While the API is clean and does not pollute any globals anywhere, it might be beneficial for many if:

  • there was a quick way to instantiate the SDK in one place, and then
  • access the same instance from anywhere else in the app
  • without needing any deep require() paths

From above code snippet, we might import the Featurevisor SDK instance as follows:

// somewhere/deep/in/my/app.js
import { instance } from "../../../featurevisorInstance";

Proposed API

We could introduce a new global option in createInstance():

// featurevisorInstance.js
import { createInstance } from "@featurevisor/sdk";

// no need to export
const instance = createInstance({
  datafileUrl: "...",
  
  // new option
  global: true,
});

Now from anywhere in the app, we could import the instance directly from the package:

// somewhere/deep/in/my/app.js
import { instance } from "@featurevisor/sdk";

or we could introduce a new getInstance() function:

// somewhere/deep/in/my/app.js
import { getInstance } from "@featurevisor/sdk";

const instance = getInstance();

I am still very undecided about whether this is a good or a bad approach, but I am all for developer convenience.

Allow variables to be set at rules level

Currently

Variables can be set at:

  • variablesSchema: for default values
  • variations.[n].variables: for variation level variables
  • variations.[n].variables.[n].overrides: for further conditional overrides
  • environments.[environment].force.variables: for forcing against specific conditions

API directions

Introduce a new level under rules as well:

  • environments.[environment].rules.[n].variables

Priority of overrides

First available value is evaluated and returned by SDK:

  • environments.[environment].force.variables
  • environments.[environment].rules.[n].variables
  • variations.[n].variables.[n].overrides
  • variations.[n].variables
  • variablesSchema

OpenFeature providers

Hello! I'm one of the maintainers in OpenFeature - a vendor-neutral feature flagging standard (https://openfeature.dev/, https://github.com/open-feature/). I saw your project and I think it's really compelling.

Basically, OpenFeature may be a shortcut for featurevisor to get SDKs in additional languages. Somebody familiar with the communication layer of featurevisor could write a provider for a target language we support (JS, .NET, Java, Go, PHP, etc) and avoid having to write an entire SDK. There's also be the benefits of OpenFeature's extensions/integrations (OpenTelemetry, etc). Our contrib repos will even handle the publishing/CI for you if you'd like to avoid that burden (we publish community-maintained providers to maven central, nuget, etc).

There's already a number of providers available from vendors and OSS projects. Let us know if this sounds interesting to you.

Generated site: update URL with search params

Currently

Generated site allows Users to search for their features, segments, and attributes quickly.

Problem

Specific search queries are not shareable via URLs.

Solution

The search params can be appended to the URL as query params, so that they can be shared with others within the team/organization with ease.

We can make use of React Router API here: https://reactrouter.com/en/main/hooks/use-search-params

Technical notes

More info: https://featurevisor.com/docs/site/

All components found here: https://github.com/fahad19/featurevisor/tree/main/packages/site/src/components

Local development tips

$ npm ci
$ npm run bootstrap

Make changes in packages/site, then do:

$ (cd ./packages/site && npm run build)

See the changes in example app:

$ (cd ./examples/example-1 && npm start)

Return defaultVariation when Feature cannot be bucketed

Currently

SDK returns undefined if the Feature cannot be bucketed into any Variation. Can happen when rollout rule is set to 0%.

API direction

If the Feature is already known and available in datafile, return the defaultVariation instead.

Ability to observe that datafile fetching fails when initializing the `FeaturevisorInstance`

Currently

Currently when we initialize FeaturevisorInstance and the fetching datafile fails the only thing that our SDKs do (at least Swift which was built based on JS SDK) is log the error.

Problem

Currently, to meet our project requirements we are wrapping the initialization process under observer and taking further actions based on the success or error event. To know that all went fine we have onReady but we don't have such a thing in case of error.

API direction

Maybe we can introduce another listener option like onError (we already have onReady, onUpdate, onRefresh)

Support parent-child relationship in Features

Use case

Coordinating multiple feature flag rollouts can be tricky and prone to errors.

Instead of having to enable them all in one go manually, a parent/child relationship can help get more features deployed (not released) in various different parts of an organization at multiple teams' own pace.

A single parent Feature can have multiple children Features, and the children will depend on whether parent is enabled or not before evaluating their own variation values in the runtime.

YAML change

  • Introduce a new optional parent key in Feature YAMLs

Linting behaviour

  • parent (string) key can only have another existing feature's name
  • Avoid more than one level deep nesting (only parents, no grandparents)

Acceptance criteria

If a Feature to be evaluated has a parent, check if the parent Feature is truthy or not.

If not truthy, return falsy (see how it affects defaultVariation).

Print SDK bundle size in CI

When building in GitHub Actions, print the file size (minified and non-minified) of the @featurevisor/sdk package so we can keep track of its growth over time.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.