most read
Software Engineering
Why We Killed Our End-to-End Test Suite Sep 24
Product
Product Managers: what they do and why we need them Feb 15
Software Engineering
The value of canonicity Oct 30
Careers
We are building diverse teams with the most creative and innovative professionals for each position we open.



Written by: Felipe Almeida, with contributions from Luiz Felix
This is part 2 of a 3-part series on Feature Stores for Real-time ML. In part 1 we talk about Introduction and also about Pros & Cons. In part 3 we conclude with end-to-end real-world architecture setups from Nubank.
A quick recap
In part 1, we explored what feature stores are and the role they play in real-time machine learning systems. We discussed how they help mitigate crucial problems such as training-serving skew and why, despite the adoption costs, they are a valuable investment for organizations operating ML at scale.
Over the past few years, we’ve used feature stores across a wide range of real-time ML applications at Nubank, including credit underwriting, fraud detection and customer service. Along the way, we’ve seen recurring patterns emerge, encountered unexpected challenges and gathered practical lessons that helped us avoid common pitfalls while maximizing the value delivered by the platform.
In this article, we’ll cover some we consider especially important.
Check our job opportunies
Lesson: Build vs buy is a spectrum
It’s common to refer to the “build-vs-buy” trade-off when discussing complex platforms such as feature stores. Some organizations may want to build everything inhouse, while others may want to shift some of the complexity away to more “complete” feature stores such as Chalk or Feast.
In our opinion, you don’t have to choose between building everything or buying everything. You can instead have a hybrid feature store: you build some parts, use open-source tools in other parts and delegate to third-party service providers where needed. This is similar to the approach we currently follow at Nubank.
Figure 1 below shows a high-level view of a real-time feature store and some of the components that may exist in each layer. Each of these components may be “built” or “bought”.
Figure 1: A real-time feature store has many components and many of them could theoretically be built inhouse, or bought as an “off-the-shelf” product. Interestingly, many open-source projects have actually given rise to companies that provide managed versions thereof (e.g. Kafka/Confluent, Spark/Databricks, Cassandra/Datastax).
At Nubank, we use open-source tools such as Kubernetes, Kafka, Pinot, Spark and Flink for core parts of the architecture, where our use-cases don’t depart too much from what the tools cater to. We also use fully-managed services from our cloud providers, while other components (such as our monitoring infrastructure) are built inhouse.
The key to deciding which parts are “built” vs “bought” is about the level of control you have, and where your organization’s workflow patterns are too different from the default use-cases the tools cater to: in these cases you may have no option but to build.
Lesson: Feature stores are products and should be managed as such
In addition to being systems, feature stores are also products. And products are living things that need to be managed, ideally by a dedicated Product Manager (PM). Everything related to internal product management applies here.
Many of the practices commonly associated with internal product management apply directly to feature stores, such as:
Note that feature stores differ from other internal products in that they naturally span multiple disciplines. Troubleshooting a training-serving skew issue, for example, often requires collaboration between software engineers, data engineers, data scientists and domain specialists. As a result, operating a feature store successfully involves coordinating expertise across several areas of the organization.
Lesson: Define a naming convention for features
Set a naming convention for features in the feature store. Naming conventions have very little downside (especially when they are lightweight enough to accommodate many use-cases).
Naming conventions have several advantages (as does standardization more generally):
It doesn’t really matter which naming convention you choose, as long as it’s versatile and allows for some customization. If the rules become too restrictive, users will have to bend and hack the naming convention until it no longer serves a purpose.
Examples include:
Some additional recommendations:
Lesson: Versioning features is hard
Suppose a team needs a small variation of an existing real-time feature in the Feature Store. The FS team can’t simply change the feature implementation, as that would cause training-serving skew for models trained on the current feature version. (They may not want to re-train their models with the newer version).
So one either needs to create a whole new feature or a new version of an existing feature. Most feature stores support feature versioning but, in practice, this means that clients must provide the version of the feature they want, in addition to its name.
It’s not so easy, however, to know what changes are better described as a new version of an existing feature or as a whole new feature on its own. Judgement must be applied to decide what action to take in each case.
For example: you have a feature called customer_cc_purchases__5d__count. Intuitively, it’s the number of CC purchases made by a given customer in the last 5 days. Now let’s see some scenarios of what can happen in a real-world situation, in Figure 2:
Figure 2: Examples of situations where teams may need to create a new version of an existing feature, create a different one altogether (depending on the semantics of the change) or, in some cases, do nothing at all.
The challenge is that versioning can quickly become difficult to manage. It’s easy to end up with dozens of versions of a feature, which means a complex and messy set of features (different teams have different retraining schedules and there are always models that are still bound to earlier feature versions and cannot be retrained right now).
One possible way out of this “versioning hell” is for the feature store team to take it upon themselves to migrate models using old feature versions into using new ones. The situation is similar to the work of updating applications to drop older, unsupported libraries and dependencies in favor of newer versions.
Data contracts can also be used to mitigate versioning problems: feature producers set the expectations and consumers are made aware when schema and semantic changes happen.
Lesson: Use a streaming architecture when you can and direct calls when you must
As discussed in Part 1, there are two primary ways to retrieve features for real-time inference. The first relies on direct calls to the systems that own the source data, while the second relies on an intermediary streaming platform that continuously receives and processes events.
These approaches can be summarized as:
“Direct-call” features
Features are retrieved via HTTP calls to the microservices that own the transactional “source-of-truth” databases. Depending on the use case, this may include multiple calls and joins.
Streaming-based features
Features are retrieved from an intermediary store instead: usually a short-term database that listens to events from an event bus such as Kafka.
As a rule-of-thumb, we recommend favoring streaming-based features whenever possible. There are several reasons for this:
It’s true that using streaming features requires a streaming “platform”, which is the lower-level infrastructure allowing source services to stream events to an asynchronous event bus (usually Apache Kafka). This is a complex and expensive platform in and of itself, and it’s a prerequisite for streaming features.
Lesson: Direct-call and streaming-based feature retrieval have different failure modes
Feature retrieval will fail from time to time, either when using “direct call” or streaming-based strategies. Some failure modes are “explicit” (i.e. exceptions and flow interruptions), while others are “silent”. This happens because they just change the feature distribution, but don’t trigger explicit exceptions. (Silent failures are very dangerous because you risk making bad decisions for days or weeks before anyone detects there’s a problem.)
Each strategy fails in different ways, and the asynchronous nature of the streaming strategy introduces some different failure modes one may not be used to. You must understand how each fails and what the implications are for your business.
Lesson: Training-serving skew monitoring is still necessary
Training-serving skew refers to undesired differences between how features are generated during training-time and how they are retrieved at inference- or serving-time.
In part 1, we argued that feature stores help mitigate this problem, because both data “paths” (batch and real-time) are defined in a single place. This reduces the chance of such deviations going unnoticed.
Feature stores do not, however, eliminate the problem. One still needs to monitor training-serving skew, as there are still many ways for batch and real-time data to fall out of sync even when both paths are defined within the feature store platform. Two examples: (1) Upstream services may change a field’s semantics in the real-time path without updating the batch equivalent. (2) There may be some systematic delays in the event bus, causing the real-time streaming features to always be late when compared with the instant they should have been available.
But what does monitoring training-serving skew actually look like in practice?
One can detect training-serving skew by programmatically generating the training-time feature extraction logic (minus the target) for those examples that were scored in real-time in the recent past,and then comparing those with the actual values used at inference-time (obtained from production logs). A visual representation can be seen in Figure 3 below:
Figure 3: One possible strategy to monitor training-serving skew is to extract features using batch-time logic for instances scored in real-time and then compare the values obtained in each “path” and see whether they match. This can be run in an ad-hoc manner or as a recurring daily job.
One important caveat applies here: This monitoring strategy assumes that teams are able to programmatically generate training datasets for models, to be used as the comparison benchmark versus real-time logs. Not only does this unlock training-serving skew monitoring, but it’s crucial to guarantee reproducibility.
Lesson: Pilot projects are one of the most effective ways to drive adoption
When introducing a feature store to an organization, not all teams may immediately want to use it, as they might lose some autonomy and control over features, lose relative power in the organization, have to deprioritize other projects, etc. The usual factors that contribute to resisting platformization efforts.
Feature store adoption should be encouraged via carrots, not sticks (e.g. top-down mandates). At the end of the day, the benefits of using a feature store (increased impact, more robustness, faster TTM, etc.) must be clear to encourage adoption. Figure 4 summarizes several common reasons why teams hesitate to adopt feature stores, along with potential mitigation strategies.
Figure 4: There are many reasons why teams resist adopting feature stores. Most of them can be mitigated if the Feature Store team shares some of the integration burden (i.e. pilot projects) and if there’s clear documentation and good support channels.
In Figure 4, we see that a great overall mitigation strategy is for the FS team to create the first few integrations for client teams, as pilot projects.
These projects greatly reduce the cost of adoption (from the PoV of client teams) and it’s a “win-win” strategy: the client team gets features built “for free” and the FS team has a pilot project to stress-test the platform, and to serve as an example for other teams to learn from.
Conclusion
As we claimed in part 1, feature stores require significant investment in platform engineering, operational maturity and organizational alignment. But for companies running real-time ML systems at scale, that investment unlocks substantial long-term benefits.
Our experience at Nubank has shown that successful feature store adoption is not only about technology choices. It also depends on product thinking, strong operational practices and a deep understanding of how real-world ML systems evolve over time.
Along the way, a few lessons consistently stand out:
This is Part 2 of our series on Feature Stores for Real-Time ML at Nubank. In Part 3, we’ll bring these concepts together and explore real-world architectures we use in practice.
Check our job opportunies