Branches

What are branches?

Branches are another way to filter and separate data. Similar to organizations and environments, data is written to a branch by changing the branch_id associated with the row. Unlike these other filters, branches are not intended to create a fully isolated view on the data. Branches are intended to function as an extension of the “root” (a.k.a “main”) branch. The concept of a root branch in bframe is akin to the trunk of a tree. Each additional branch is built off of the root and shares all the data associated with the root.

Once a branch is set in the bframe configuration, the library will union all the data associated with it and the root branch. Querying any non-main branch includes the entire root and the branch, but when analyzing the root no other branches are included. This combined dataset can then be used as the source for bframe to create invoices, line items and rated events.

image0

Why are branches important?

A system of record, like a ledger or a billing system, can be challenging to test or simulate. Branches offer a simple way to make safe modifications to the business model, since the changes are not included in the root branch. Realistic large scale simulations and scenario analysis become possible with branches, since production data can be used as a base. Easily model updates to pricing and packaging, test out new provisioning mechanisms or rehash usage formulae. Once satisfied it’s easy to revert back to the root branch, or better yet keep the data fully local and never write to a production database at all.

How does branching work?

There are a few steps to creating and working with a branch. Let’s go through an example of how we could change the pricing within the Wikipedia case study.

Wikipedia has mostly seen success in it’s new pricing and packaging. This has left the executive team wondering how much money has been left on the table. To hit their growth goals they want to increase the amount of revenue by 20%. Leadership thinks that the “Updates” product can be a bit more expensive, and would like to see an analysis based off this premise.

State of the world

Before we get started let’s see what we are working with. If the analysis is being run as of 6/1/2024, then last month’s revenue would be around $3.4M.

revenue

3418011.0

SELECT FLOOR(SUM(total)) as revenue
FROM bframe.invoices
WHERE started_at = '2024-05-01';

Create the branch

The first action we need to take is creating a branch to hold our new modifications.

INSERT INTO src.branches (id, org_id, env_id, name, created_at) values (2, 2, 20, 'Wikipedia revenue scenario', '2024-12-04');

Count

1

Make modifications

Since this is entirely a test branch, we will make a direct edit to the list price. Let’s increase the price of “Updates” product by 20%.

INSERT INTO src.list_prices (org_id, env_id, branch_id, id, product_uid, pricebook_uid, price, version) values (2, 2, 20, 1, 1, 1, '0.12', 1);
INSERT INTO src.list_prices (org_id, env_id, branch_id, id, product_uid, pricebook_uid, price, version) values (2, 2, 20, 4, 1, 2, '0.06', 1);

Count

1

Explore the changes

Now that the branch and the modifications are ready, we can update the bframe configuration to the new branch. This just means we update the branch_id that the bframe library is using.

Once updated, all bframe transformations should respect the new change. Querying the active prices for a specific contract confirms this is the case.

products.name

products.id

prices.list_price_uid

prices.price

Updates

1

4

0.06

-- The active branch within bframe has been updated to 20
SELECT
    pdct.name AS 'products.name',
    pdct.id AS 'products.id',
    p.list_price_uid AS 'prices.list_price_uid',
    p.price AS 'prices.price'    
FROM bframe.prices AS p
JOIN bframe.products AS pdct ON p.product_uid = pdct.id 
WHERE 
    p.contract_uid = 1323
    AND p.product_uid = 1;

Now that we are sure the branch is in good shape, let’s see if our goal of increasing revenue by 20% has been accomplished.

revenue

3444426.0

SELECT FLOOR(SUM(total)) as revenue
FROM bframe.invoices
WHERE started_at = '2024-05-01';

This doesn’t quite get us to what we were expecting. Let’s increase the price until we get to the 20% growth in overall revenue. We can do this by updating the list prices again, with a new version.

INSERT INTO src.list_prices (org_id, env_id, branch_id, id, product_uid, pricebook_uid, price, version) values (2, 2, 20, 1, 1, 1, '0.50', 2);
INSERT INTO src.list_prices (org_id, env_id, branch_id, id, product_uid, pricebook_uid, price, version) values (2, 2, 20, 4, 1, 2, '0.40', 2);

Count

1

Once updated we can run the same query to get an idea of the revenue.

revenue

4138982.0

SELECT FLOOR(SUM(total)) as revenue
FROM bframe.invoices
WHERE started_at = '2024-05-01';

This gets us pretty close to the goal the Wikipedia executives had in mind.

Revert or apply the updates

Let’s assume that once the executives see the analysis, they are OK with the tradeoffs of a large price increase.

To follow the will of leadership, we need to these changes into production. This can be accomplished by scheduling the update we made previously, but on the main branch. Since this analysis is as of 6/1/2024, the price change will be scheduled for the start of the next quarter (9/1/2024).

-- Cut short current pricing
INSERT INTO src.pricebooks (org_id, env_id, branch_id, id, durable_id, name, invoice_delivery, invoice_schedule, effective_at, ineffective_at, version) values (2, 2, 1, 1, 'a', 'Standard', 'ARREARS', 1, '2023-11-01', '2024-09-01', 1);
INSERT INTO src.pricebooks (org_id, env_id, branch_id, id, durable_id, name, invoice_delivery, invoice_schedule, effective_at, ineffective_at, version) values (2, 2, 1, 2, 'b', 'Enterprise', 'ARREARS', 1,  '2023-11-01', '2024-09-01', 1);

-- Create new pricing for Standard pricebook
INSERT INTO src.pricebooks (org_id, env_id, branch_id, id, durable_id, name, invoice_delivery, invoice_schedule, effective_at) values (2, 2, 1, 1001, 'a', 'Standard', 'ARREARS', 1, '2024-09-01');
INSERT INTO src.list_prices (org_id, env_id, branch_id, id, product_uid, pricebook_uid, price) values (2, 2, 1, 1001, 1, 1001, '0.50');
INSERT INTO src.list_prices (org_id, env_id, branch_id, id, product_uid, pricebook_uid, price) values (2, 2, 1, 1002, 2, 1001, '0.05');
INSERT INTO src.list_prices (org_id, env_id, branch_id, id, product_uid, pricebook_uid, price, invoice_delivery, invoice_schedule) values (2, 2, 1, 1003, 3, 1001, '1000.00', 'ADVANCED', 12);

-- Create new pricing for Enterprise pricebook
INSERT INTO src.pricebooks (org_id, env_id, branch_id, id, durable_id, name, invoice_delivery, invoice_schedule, effective_at) values (2, 2, 1, 1002, 'b', 'Enterprise', 'ARREARS', 1, '2024-09-01');
INSERT INTO src.list_prices (org_id, env_id, branch_id, id, product_uid, pricebook_uid, price) values (2, 2, 1, 1004, 1, 1002, '0.40');
INSERT INTO src.list_prices (org_id, env_id, branch_id, id, product_uid, pricebook_uid, price) values (2, 2, 1, 1005, 2, 1002, '0.04');
INSERT INTO src.list_prices (org_id, env_id, branch_id, id, product_uid, pricebook_uid, price, invoice_delivery, invoice_schedule) values (2, 2, 1, 1006, 4, 1002, '5000.00', 'ADVANCED', 12);

Count

1

To confirm that the changes are live, we can switch bframe’s current branch to the root and pull active prices.

products.name

products.id

prices.list_price_uid

prices.price

effective_at

ineffective_at

Updates

1

4

0.05

2023-11-01

2024-09-01

Updates

1

1004

0.40

2024-09-01

-- The active branch within bframe has been updated to the root
SELECT
    pdct.name AS 'products.name',
    pdct.id AS 'products.id',
    p.list_price_uid AS 'prices.list_price_uid',
    p.price AS 'prices.price',
    p.effective_at,
    p.ineffective_at
FROM bframe.prices AS p
JOIN bframe.products AS pdct ON p.product_uid = pdct.id 
WHERE 
    p.contract_uid = 1323
    AND p.product_uid = 1
ORDER BY effective_at ASC;