Feature Stores Without a Platform Team: A Pragmatic Blueprint
Machine Learning·By Islam Gamal··25 min read

Feature Stores Without a Platform Team: A Pragmatic Blueprint

You don't need Tecton or Feast on day one. Here's how to get 80% of a feature store's benefits with dbt, Postgres, and a Redis cache — and when to graduate.

Islam Gamal
Islam Gamal
AI · Data · Automation Engineer
TL;DR

A real feature store solves two problems: training/serving skew and reusing features across models. You can solve both with dbt for the offline layer, Postgres or Redis for the online layer, and a thin Python wrapper — no platform team required. Adopt Feast or Tecton only when the pain justifies the tax.

Key takeaways
  • Training/serving skew is the top ML bug — features computed differently at train time and serve time.
  • dbt models are your offline feature definitions; use them to populate the online store.
  • The online store can be Postgres for <10ms lookups at moderate scale; Redis when you need <2ms.
  • A tiny Python client with a shared feature registry prevents feature drift across teams.
  • Adopt a full feature store when 3+ teams share features or you need point-in-time correctness at scale.

The problem, minus the marketing

A feature store exists because ML models fail in two predictable ways:

  1. Training/serving skew. At training you compute 'orders in last 30 days' via SQL. At serving, the app calls a Python function. Two implementations, subtle differences, model in production performs worse than in the notebook.
  2. Feature duplication. Team A builds a 'customer LTV' feature. Team B builds a slightly different one. Team C builds a third. Now there are three definitions of the same thing.

Everything a feature store does is in service of these two problems. You can solve both with tools you already have.

Offline features live in dbt

Every batch feature is a dbt model. One SQL definition, one source of truth, materialized to your warehouse for training, exported to the online store for serving:

-- models/features/customer_features.sql
select
  customer_id,
  count(*) filter (where order_date > current_date - 30) as orders_30d,
  sum(total_cents) filter (where order_date > current_date - 90) as gmv_90d,
  max(order_date) as last_order_at,
  current_timestamp as computed_at
from {{ ref('stg_orders') }}
group by 1

This model is your feature contract. Any team using orders_30d reads it from this table. Change the definition, everyone sees it. Version via dbt's -- @deprecated comment or renaming.

Online store — Postgres first

For serving latencies of 10-50ms and QPS below a few thousand, a Postgres table with a simple sync job is enough:

create table online_customer_features (
  customer_id  uuid primary key,
  orders_30d   int,
  gmv_90d      bigint,
  last_order_at timestamptz,
  computed_at   timestamptz
);

-- nightly sync from warehouse
insert into online_customer_features
select * from warehouse.customer_features
on conflict (customer_id) do update set
  orders_30d = excluded.orders_30d,
  gmv_90d = excluded.gmv_90d,
  last_order_at = excluded.last_order_at,
  computed_at = excluded.computed_at
where excluded.computed_at > online_customer_features.computed_at;

When p99 lookup latency needs to be <5ms, add Redis as a read-through cache in front. Do not start with Redis — you'll spend weeks on cache invalidation for a problem you don't yet have.

The shared client

Ship a tiny Python package that every model consumes. It hides the storage and enforces the registry:

class FeatureClient:
    def get(self, entity: str, entity_id: str, features: list[str]) -> dict:
        table = REGISTRY[entity].table
        cols = ','.join(features)
        return db.fetch_one(f'select {cols} from {table} where {entity}_id = %s', entity_id)

REGISTRY = {
    'customer': FeatureSet('online_customer_features', ['orders_30d', 'gmv_90d', 'last_order_at']),
    'product':  FeatureSet('online_product_features', ['view_count_7d', 'purchase_count_7d']),
}

Any code that asks for a feature not in the registry raises. Any dbt model that produces a feature registers it via a macro. Two-way enforcement, zero platform team.

Training/serving skew, solved

Training reads from the warehouse table. Serving reads from the online table populated by exporting the same warehouse table. There is one implementation of every feature — the dbt model — and it runs once per day. Skew becomes structurally impossible for batch features.

For real-time features (last click, current cart) you still need to be careful — a separate section. But for the 90% that are 'aggregate over the last N days', this pattern eliminates the class of bug entirely.

When to graduate to Feast, Tecton, or Vertex Feature Store

  • You have 3+ teams sharing features and the ad-hoc registry becomes a coordination bottleneck.
  • You need point-in-time correctness for training — 'give me the value each feature had at the moment of each historical prediction' is genuinely hard to do in dbt alone.
  • You need streaming features (last 60 seconds of activity) with sub-second freshness.
  • You need feature lineage, monitoring, and drift detection out of the box.

Feast is the cheapest step up — open source, works with Postgres/Redis, no cloud lock-in. Tecton and Vertex Feature Store are managed but expensive; only adopt when the head count you save exceeds the license cost, which is usually at a 20+ ML engineer scale.

#Feature Store#MLOps#dbt#Redis#ML Engineering

Frequently asked

Isn't this a feature store already?

In everything but marketing terms — yes. Which is the point.

What about training-time joins?

For basic training sets, join the feature table to your labels in SQL. For point-in-time correctness, use dbt's snapshots or a lightweight event log; only migrate to a real store when this hurts.

Does this work for real-time features?

Partially. Batch features (last 7d aggregates) are perfect. For truly real-time features (last 60s clicks), you'll need a streaming layer — Kafka + a materialized store — but you can add that just for the 2-3 features that need it.

Building something similar?

I help teams ship production-grade AI agents, n8n workflows, and data platforms. Let's talk about what you're building.

Work with me
Islam Gamal
Written by

Islam Gamal

AI, Data & Automation Engineer. I design and ship production AI agents, n8n workflows, and cloud data platforms — with a focus on reliability, cost, and measurable business impact. Founder of Tashghil and Tek bil Arabi.

More from Islam Gamal

Browse every article by Islam Gamal on the author page.