Best share market trading courses in Delhi

There are several share market trading courses available in Delhi that offer quality education and training. Here are some of the Best Share Market Trading Courses In Delhi : (BSMTC) is a renowned…

Smartphone

独家优惠奖金 100% 高达 1 BTC + 180 免费旋转




Building a rewards platform from scratch

The primary goal of our rewards program is to drive spend and, more generically, any sort of in-product behavior. In order to provide this flexibility from day one, our rewards platform ended up with a relatively complex design despite the simple scope of our initial rewards offering (we’re launching with a basic points system). However, we knew our growth team would rapidly come up with creative ideas around rewards, so we decided to be future-proof and design the platform correctly from the beginning. :)

Brex was built as a distributed system from day one, and different subsystems are responsible for different sets of features. Therefore, in order to reward any type of user action, we needed an easy way for any system on Brex to trigger Brex-defined actions that might generate rewards back to users, while being extra careful about idempotency and consistency (which we value tremendously as a financial company, even for a non-critical feature like rewards).

The overall idea of the system is: given a user behavior and an action, any sort of reward can be triggered — from actual dollars (cashback) to points, bottles of champagne, etc. Points, however, offer the most versatility as they decouple accrual from redemption (unlike “gift” rewards, for example like a fixed amount gift card).

Therefore, we decided to split our rewards platform in two major components: accruals, responsible for processing system-wide actions, matching them to campaigns and issuing rewards accordingly (primarily points); and redemptions, where we control which offers that can be redeemed using points previously accumulated.

By controlling all the parameters of accrual and redemption rates for each action/offer, we can give our growth team granularity to drive very specific user behaviors. Here’s how:

An Action is a rewards-specific event that, when received by the rewards platform, starts the process of accruing points. Examples of actions are:

Initially, we started by exposing a simple Action API to other Brex systems, which enabled registering ad-hoc Actions on the rewards platform:

Although simple, this approach would require every single system on Brex to manually dispatch every possibleAction to the rewards system. In order to increase flexibility, we also wanted our design to support consuming events from event streams (i.e. a Kafka streaming from a database replication slot, for instance) instead of triggering all events manually, so we added support for a conversion layer which takes generic system events (e.g. transaction cleared, user created) and transforms them in a meaningful Action (purchase, user referral).

To ensure idempotency, the conversion layer computes a unique token for the Action, based on the system event (e.g. the ID of the data entity affected in the originating system). The combination of the type of the Action and this token is a unique index in a ActionInstance table. When the Reward system receive an Action, it looks up the database to see whether this particular instance has already been processed or not. As the ActionInstance is stored in the database within the same transaction that processed it, this lookup ensures the Action will only get processed once.

In our initial design, Actions were directly associated with rewards. However, we realized that this was not flexible enough as some rewards should only be redeemed after a specific sequence of Actions happened. Therefore, we decided to introduce a new concept: Behaviors. Examples of behaviors are:

Rewards are obtained based on Behaviors, which are affected by incoming Actions. Those Behaviors are encoded using an implementation of a protocol, and a campaign-specific configuration that can customize how the incoming actions affect a behavior being triggered (named BehaviorTrigger). The Behavior then processes anAction and stores an internal state in each of their instances (per customer account/user), as a BehaviorTriggerInstance.

(Note that not all Actions trigger theBehavior right away: for instance, given a behavior of spending $10k in the first month, every Purchase Action has to be processed and the “total amount spent in the first month” has to be updated, thus the importance of having a BehaviorTriggerInstance to store internal state.)

The configuration of a BehaviorTrigger covers both how it is triggered based on the Action processed, as well as how the Reward is calculated based on its final state (it can be a rate based on the transaction amount, or a fixed points rewards configured in a campaign).

An Action Processor in Rewards processes all the incoming ActionInstance, and loads the SelectedTrigger that can process them, filtering out the Campaigns that don’t target the account/user.

Since the beginning, we also wanted to have the flexibility to target specific campaigns to cohorts of accounts on Brex. It turned out, however, that designing flexibleCohorts was a lot more complicated than we initially thought.

We started with two basic types of cohorts: a GlobalCohort that targets all accounts, and a StaticCohort, which contains a static list of customer account ids.

Soon we realized that we’d also need support for DynamicCohorts. For instance, imagine a campaign that gave 2x points for all new accounts in the first month on Brex. Accounts would have to be manually added and removed from StaticCohorts by hand, which was far from ideal.

After a couple of different designs, we decided to leverage the Action Processor mechanism to process incoming Actions and, based on CohortRules, determine whether the account should be added to a DynamicCohort, removed from it or simply ignore the Action for cohort attribution purposes.

A CohortRule has an internal state similar to a BehaviorTrigger, which enables support for complex assignment rules (i.e. adding an account to a cohort after they spent $1000 on the first two months).

CohortRules are implementations of the following CohortRuleprotocol:

With actions, behaviors, cohorts and campaigns, we have the overall flow for points accrual:

Once accrued, points can be used to redeem offers, a generic term describing anything of value to the user. Our initial design was simply having a table ofRedemptionOffers with a cost column storing the amounts of points required to redeem the offer. This design worked fine to redeem Brex points in promo codes or physical items, but not for offers with dynamic pricing like booking a flight or providing a statement credit (cashback) for a transaction.

As different types of offers have completely different implementations for calculating their cost and redeeming them, we needed a design that supported a runtime protocol (instead of simply a model) to aggregate offers of the same class under a same implementation. Thus, we introduced theRedemptionClass entity, which defines a specific class of offers that can be redeemed using points. For instance:

This mechanism is defined by the implementation of a protocol that performs the redemption action, and a configuration specific to the Redemption program.RedemptionOffer implements this protocol to provide rewards:

This way, we can define RedemptionClasses for each type of redemption offer available on Brex. This abstraction also provides flexibility to dynamically price our inventory of static deals (i.e. promo codes) depending on previous levels of spend, account history, etc., which is a nice addition :)

The final design for our rewards platform ended up much more complex than what we initially expected. However, anticipating the future needs of our business, it would be unwise to cut corners and deliver a MVP that, in order to support more advanced functionality, would need to be completely redesigned in the near future.

At Brex, we pride ourselves on building systems correctly from day one. We believe that spending the time to start with the right abstractions is the best investment to exponentially reduce technical debt in the long-run. When building financial infrastructure from scratch, the only certainty is that future engineers will build on top of the existing abstractions, so the time spent building a proper technical foundation is paramount.

Add a comment

Related posts:

Bought thoughts

Our language and thinking constantly gain (and lose) words and concepts. Sometimes this changes our world-view, sometimes it only adds an insignificant detail. “App”, “podcast” or “infotainment” does…

Flower in the Desert

Like a flower in the desert I had to grow in the cruelest weather, holding on to every drop of rain just to stay alive. But it’s not enough to survive, I want to bloom beneath the blazing sun, and…

Use of Metaphor in Haiku

What idea does this conjure up in your mind? It felt like the test nearly killed them as it was so tricky they did not feel they would get through it. A metaphor makes a comparison by stating that…