How we cut our Snowflake bill by 25% and won back our engineering time
Our Snowflake bill grows every year, and most of that is healthy: more data, more users, more of the business running on the warehouse. Underneath that, though, we were paying for two things we couldn't explain - tables that were poorly optimized, and jobs running on compute far larger than the data they were processing needed.
The same workloads were costing us more without providing any additional benefits, so we took two different AI driven approaches: implementing Espresso AI, which sits under our queries and decides what compute they run on, and a Snowflake Cortex agent that works inside our repo and optimizes the models themselves.
The treadmill
Warehouse optimization is never finished. A model that was well-tuned in January is badly tuned by June, because the data underneath it grew, the access pattern shifted, or someone added a join that broke pruning. Nothing was wrong with the original work - it just expired.
Warehouse optimization was taking 10 to 20 percent of our engineering time, and at the end of it our models did exactly what they did before. Just for less money.
The same work would always come back around. Someone would notice a warehouse running hot, or a nightly job creeping past its window, and the tuning pass would start over. Ultimately this meant that expensive engineering time was spent holding a line rather than moving one.
An example of where this hurt us - in March 2025 we identified one of our product models as being a major bottleneck for our data pipeline. We pushed a change to materialize this model incrementally which vastly improved performance, but ended up causing some downstream side effects which broke some of the models that depended on this. After several of our engineers spent most of a day working through that problem and fixing it, we pushed a second optimization PR several weeks later to finally resolve it.
None of it moved the team forward or gave our business users anything new. It was time spent on tech debt that we would rather have spent building.
We decided to get off the treadmill of eternal optimization and go after the two costs underneath it separately.
Solution one: Espresso AI
Espresso AI runs as a proxy in front of Snowflake, and it decides what warehouse each query actually lands on and how those warehouses scale. It operates directly within Snowflake and not against our codebase.
What it optimizes is the gap between the compute we provisioned and the compute the query actually needs. That gap is invisible in our dbt project, because it isn't a property of our SQL - it's a property of the moment the query runs.
Using this tool alone is saving us roughly 25% on our Snowflake bill by allowing us to run more queries on smaller warehouses and having warehouses scale horizontally as traffic increases.
Solution two: Cortex agents
A weekly job runs a Cortex Code agent against our dbt project. During this job it reads from our Snowflake query history and looks at how a table is physically laid out against how it's actually being queried, finds where those disagree, and opens a pull request. The PR explains what it changed and shows the build timings before and after. Most importantly, we have special instructions for the agent in place to make sure that no data values are changing as part of this optimization.
It's a similar process to what we were doing manually before, but with two big differences. First, this job is proactive, so it's identifying models before they become a problem and before someone notices that they're an issue. Second, because it's reading from the query history, it's looking at how the usage of this table has changed over time and optimizes for how that table is being used more recently.
With this job, we've already seen some of our heavy-hitting models drop considerably in runtime and cost. One of our main models that contains information about our shop and registry purchase data dropped from a 20-minute runtime to about a 4-minute runtime because of this optimization created by the agent.
How the two compare
Here's a quick breakdown of how these different solutions work:
| Espresso AI | Cortex Code | |
|---|---|---|
| Layer | Compute routing and scaling | Model code |
| Sees | Query history and warehouse metadata | Query history and our dbt repository |
| Output | Queries land on warehouses that are right-sized for the workload they contain | Pull request(s) in GitHub |
| Runs | Continuously for each query | Weekly |
| Human in the loop | Monitoring and SLA tuning | Code review |
| Fixes | Over-provisioned compute | Bad partitioning, pruning, materialization |
For more insight into what we did, here's a quick video Snowflake produced highlighting our relationship with them and Espresso AI.
What's next
We're routing more of our Snowflake workload through Espresso AI. It started with our dbt jobs and now covers our Streamlit apps, our BI traffic, and our AI workloads.
On the code generation side, the Cortex agents optimize models that already exist. Where we're trying to get to now is figuring out how we can apply that same logic to newly created models and changed models to see if we can implement these optimizations before they ever become a problem.
At the end of the day, our Snowflake bill is always going to increase as our usage increases and our data gets bigger. What we're after is a bill that grows more slowly than the usage driving it, and these two solutions are how we're getting there.