I built an embedded analytics solution with Omni, these are some of my learnings.
📚 Context
Hi, I’m Josh, I recently built an embedded analytics solution for a SaaS startup to showcase ROI insights in the product. I’ll start by acknowledging that I have over 10 years of Tableau expertise (I’m an accredited trainer and Tableau Desktop Certified). This blog will cover the business use case that led to using omni, why the tool was selected, and key learnings from the project. I’m new to writing blogs, and I’m certainly not an English major, please bear with me.
🔥 Hot take
After using omni.co throughout this project, I developed a strong conviction tools like omni are going to change the modern analytics landscape, particularly when it comes to embedded analytics and shipping data products like code.
đź§ The challenge: providing ROI in a tough market
In today’s economic rollercoaster, enterprise buyers have tight budgets and demand proof that your software delivers value. My client needed ROI dashboards embedded in their product to close deals, and help with renewals, but hard-coding those insights was a non-starter. Why? CRM implementations are like snowflakes and no two are alike. A “stage” field might be “S1”, “Stage 1”, or “Prospect”, at three different customers, however they all mean the same thing. Plus, every customer wants their own flavor of ROI. One customer might care about meetings booked, another’s all about self-serve PLG metrics. In addition, the closer the logic sits to the business, the faster the development can happen in most cases.
Enter embedded analytics. Analytics tools let you build flexible, customer-facing dashboards without needing to build visualizations from scratch.
⚖️ Why Omni Won the Analytics tool selection
After assessing Omni, Tableau, and Looker, Omni stood out for our embedded use case. Here’s some of the reasons why:
Seamless Integration: Omni plays nice with Git and dbt, letting me manage dashboards like code via branches, PRs, the works. While Tableau was great for prototyping and running deep dive analysis, it struggles with data lineage and integrating with CI/CD.
Data Lineage for Trust: Omni’s query debugger shows the exact SQL behind every metric, perfect for audits. Tableau limits the visibility to the underlying sql to users with creator licenses, and even then it’s difficult to get to the exact SQL that’s creating a chart. When running analysis, particularly with dynamic customer data, having clear data lineage is critical.
Web-Based Workflow: Omni’s 100% web-based and auto-saves every change. Tableau is desktop based, which can cause the logic to become stale, the occasional crashes during extracts, and most importantly: lack of version control for actively developed content. Anyone that’s spent a good chunk of time working in a Tableau workbook and forgot to save regularly knows this pain.
Dynamic Data Modeling: Omni’s semantic layer lets you do a bunch of really fun stuff like injecting multi-value filters into your SQL, making complex query requirements just… work. The data model is very much a part of the flow in Omni, vs being fairly obfuscated in Tableau. The just in time data modeling in omni lets you build out your semantic layer at the same time your developing content, and then push it upstream when needed. Tableau published data sources kind of accomplish this concept, however switching between live and extracted data sources can cause version control issues, and it makes your data consumers fully dependent upon a single tool for data access, with a large switching cost in the event you change your mind.
Responsive Team: Omni’s EPD team lead by ex-Looker founders ship impactful features regularly, they share the same weekly demos that they do internally with the general public, which is pretty neat. Tableau’s roadmap feels more about competing with Power BI and shipping specific niche features to win enterprise contracts. Ever since SFDC acquired Tableau, it’s seemed like the customer support and new product innovation has been nerfed, unless perhaps you’re a six figure + enterprise customer with a dedicated solution engineer.
Reasonable Licensing cost: Omni’s embedded licensing was flexible, reasonable, and affordable for a startup. Tableau’s embedded pricing is inflated to the point that it doesn’t make a whole lot of business sense in many scenarios.
Downside: Omni’s newer, so the chart library and community are still growing. The interface evolves fast, which can throw you for a loop if you miss the release notes. Still, in my humble opinion, the pros far outweigh the cons for embedded scenarios.
Note: Although Omni was suitable for our specific use case, factors including your data environment, team skill, pricing, and core features should impact the tool selection process and guide you when choosing the right tool for the job.
🍿 Key Learnings and Observations
A few weeks post-deployment, here are some of the things that might save you some time if you’re doing customer-specific ROI dashboards with Omni and clickhouse:
Customer specific customizations
A basic principle I’d recommend: prototype quickly, review often, assess scalability regularly.
The thing about customer specific customizations, is that the logic needs to live somewhere. Now, the location that that logic lives can always change, and it probably should as your solution matures. I typically operate with a rule of 5, wherein the first 5 times you’re building out a solution that requires customization, you host that logic as close to the customer as possible. As you learn more about the nuances of where the logic needs to be dynamic, how it can me optimized, and if/how it may be causing toil, you can fine tune the solution. In the case of Omni, I first iterated on customer specific customizations by running multiple models in parallel, I realized after the first customer that this would not be sustainable given the pace of future feature enhancements, and that the branched model would not inherit from the benefits from the main model, to which I then pivoted over to using a schema that had some branched views with customer specific logic, however that also became a pain to maintain once performance optimization measures were taken. What I ended up landing on, was normalizing the customer specific logic via a commented case statement in a materialized view to balance the requirements for agility to customer asks and performance. The customer specific semantics are then applied at the workbook layer by aliasing the stable fields to match the customization requirement. This approach is nice because you can then maintain one core model/topic that inherits any major logic changes, formatting changes etc. While you can host the customer verbiage directly in the workbooks that are customer facing. The main drawback to this solution, is that as of right now, Omni does not allow for multiple dashboards in a workbook right now. What this translates to, is that if you have multiple dashboards that require customer specific customization, you’ll need to do the semantic updates in each of those dashboards, rather than doing it in a single workbook file that pushes to all dashboards. I anticipate that this will likely evolve after the an additional 5-10 customer specific customizations given that there will likely be more learnings on commonalities of requested patterns that we can bake into the data pipeline.
In this solution, the way that we served up customer specific content was using Omni’s document API to call the document name. As an example, the customers that did not require customizations would have a workbook default URL like /fancy-roi-report and customers that had a custom version would have something like /fancy-roi-report-1234. We had it configured that each time the URL was called it would first check to see if there was a url with the user_attribute included in the specified format, and if there was then it’d serve up the custom content, if there wasn’t it’d be the default.
An Omni feature I’m excited about, and have been playing around with a bit that’s currently in Beta is the ability to embed customer specific fields in self serve environments via an entity attribute value (EAV) model. Russ does a great job showcasing a recipe for this in this blogpost.
Advanced filtering use cases
You can do a bunch of really powerful things with templated filters in Omni. I ended up using them to parameterize where clauses enabling users to do things like toggle between attribution models, configured a time frame selector to switch between day, week, and month, created a control for filter-only fields so that I could use metric specific date logic in a single view, and configured a dynamic dimension to change out the level of granularity that a window function was running at while swapping out the dimension slicing a view at the same time.
Omni lets you set control filters on dashboards, which have suggested values driven from a field of your choosing. I found this super useful, in that you can configure a filter once on a field you’ll use many times i.e. user_id, configure that filter to be controlled by a custom coded filter in your in-product experience, and then link that filter up to any additional user_id filters later on, even if they’re from a different data source. The interface enables you to select what worksheets you want to filter to drive, and which field within those worksheets. I have never seen this functionality work so well, or at all, in other analytics tools besides Omni.
In order to lookup the first action a user took on a contact linked to an opportunity within the context of the filter set, I used templated filters injected into the underlying SQL query to filter before the records before a row_count()=1 order by eventtime desc window function.
Given both the sensitivity and performance requirements of the data, I used user attributes to pre-filter by customer_id in both the initial CTEs querying source tables directly, as well as on the topic. This boosted performance significantly given that the tables were indexed on customer_id, and the queries were pulling a much smaller data set in. This will be a key requirement for most all embedded analytics scenarios that have customer specific views.
There’s a feature in omni that enables you to dynamically switch the data base table you’re querying that I really wanted to take advantage of, however it requires the identifier() function which clickhouse doesn’t have yet. If you’re using snowflake, then this should work for you. This feature would enable you to do outsource a fair amount of performance tuning to your DB if needed by pre-computing aggregates at various granularities etc.
Performance
Inter view/topic joins caused quite a performance hit. My understanding is that this feature is similar to Tableau’s Blending, so not surprising that this caused a perf hit. In my use case I had two disparate models with unique filter requirements that were related on an ID field that was not the lowest level of granularity. Based on how this works, both queries would need to run upon render. Even after materializing these views, this view still takes 5 or 6 seconds to load rather than the rest of the charts which load either instantaniously or <3 seconds.
I ended up using a combination of materialized views and sql with injected templated filter values in the final topic given that we needed to enable the user to select between multiple model options, as well as have some filters happen before attribution logic was applied. I’ll likely do a dedicated writeup on this portion of the solution given that it essentially mirrors Tableau’s LOD functionality, but templated filters also have some creature comforts that I, and I suspect other analysts will absolutely love. Without getting to deep into the weeds, Omni knocked the concept of dynamic parameters out of the park on the first try… like, it just… works. Tableau struggled with the concept of dynamic parameters for over a decade, which left subject matter experts scrambling to find hacky solutions workarounds like this one.
Data audits
Having a duplicate audit dashboard for each dashboard that has simple, raw SQL queries hitting the source tables with basic unit tests can go a long way in building confidence in the data. I like that you can just write the queries natively in Omni, and have them side by side with the query that Omni is running, and you can use the query debugger to compare the two. If you were to do this in tableau, in theory you could use custom SQL connectors for the basic queries, however you’d need to navigate to your data source each time you want to display it, so it might be easier to have a separate tab open with your queries to walk through.
Audit early, often, and in different environments. The earlier you’re able to catch a bug, the better. Particularly when your working in an embedded environment, things like wiring up filters and rendering can be a bit fragile when you’re getting everything dialed in.
When working with customer specific customizations, having a data subject matter expert involved on the customer side early on will save you a lot of headache down the road. If you’re working with someone that thinks they know enough to get by on sales data, yet does not involve somebody from RevOps for instance, you may end up with more of a POC that still requires additional cycles to get dialed in. Try to get the correct person for the first requirements cycle if possible.
Summary
The modern analytics landscape is evolving quickly, I’m happy to see longstanding data challenges are being tackled with innovative solutions. If you’re considering building an embedded analytics offering for your product, I’d love to connect. I’m happy to serve as a sounding board for your ideas or provide fractional or dedicated resourcing to help bring your vision to life.
One of the unique differentiators for Spicy Data’s services offering is that we can deliver full stack data projects without the need to hire or onboard a big team. We operate very lean, and pull in highly specialized, senior talent as needed.
Thanks for reading,
Josh