Why your CRM has eleven filter builders
Every large CRM ends up with a different filter builder per feature — and each one supports a slightly different set of operators. It is not incompetence. It is what happens when filtering is a feature instead of an engine.
By The Unify Loop team
Open a mature CRM and try this. Build a filter in a list view. Then build the same filter as a marketing segment. Then as a condition inside an automation. Then as a report filter. Four builders, four slightly different sets of operators, and at least one place where the thing you need is missing.
This is close to universal in the category, and it is not because the teams building these products are careless. It is the predictable outcome of treating filtering as a feature rather than as an engine — and the cost lands on you, every week, in a form that is hard to name in a review but obvious in daily use.
How it happens
A CRM ships with list views. Someone writes a filter builder for list views. Two years later the company adds marketing, and the marketing team needs audience segmentation. They could reuse the list-view filter, but that code is coupled to a table UI, it assumes a page size, and it lives in a module the marketing team does not own. The pragmatic decision — the one any sensible engineering manager makes under a deadline — is to write a segment builder.
Then automation ships, and needs conditions. Then reporting ships, and needs filters with grouping. Then row-level permissions ship, which are conceptually a filter applied invisibly to every query. Then AI search ships, and needs to translate a sentence into a query. Each of these is built by a different team, in a different quarter, against a different deadline.
Nothing is broken. Every builder works. But they diverge, and divergence in filtering semantics is unusually nasty because the differences are invisible until they bite:
- The list view treats an empty string and a null as the same thing. The segment builder does not.
- The list view can filter on a field of a related record. The report builder can only filter on the object in front of it.
- The automation condition evaluates dates in UTC. The view evaluates them in your timezone. Both are defensible; together they are a bug you will spend an afternoon on.
- One builder has “is one of”. Another only has “equals”, so you build three rules with an OR.
The result is that the answer to “can I filter on that?” is never yes or no. It is “which screen are you on?”
What it actually costs you
Three things, in rising order of importance.
You learn filtering several times
Every new person on your team learns four builders instead of one, and learns them by discovering the differences. This shows up as a slow, unattributable drag on onboarding that nobody logs a ticket about.
Logic gets re-entered instead of reused
You define “enterprise accounts with no activity in thirty days” in a view. Later you want a campaign to that audience, so you rebuild it in the segment builder. Then an automation, so you rebuild it again. You now have three definitions of one idea, maintained by hand. When the definition changes — enterprise becomes 200 seats rather than 100 — you have to remember all three. You will not.
Capability arrives unevenly, forever
This is the one that compounds. When the product adds a new operator, it lands in whichever builder the owning team maintains. Five years of that and the gap between your most and least capable filter surface is enormous, and every roadmap request becomes “which of the five would you like it in first?”
A saved view, a marketing segment, a workflow condition, a report filter, and a question typed at the AI should be the same object hitting the same compiler.
The alternative, and what it demands
The alternative is to make filtering an engine that everything else consumes. One representation of a filter — a structured, serialisable definition — and one compiler that turns it into a database query. Every surface becomes a different way of building or displaying that same object.
This is not free. It demands some genuinely uncomfortable discipline:
- The filter format has to be designed before the features that consume it, which means designing it while you are least certain about requirements.
- Every consumer has to accept the shared semantics, including the parts that are inconvenient for their screen.
- Nobody gets to add a quick special case. A special case in a shared engine is a special case in five products.
- You need conformance tests, because a subtle divergence in the compiler is now a subtle divergence everywhere at once.
The payoff is that capability arrives evenly. When we add related-record traversal, it appears in views, segments, automation conditions, reports, permissions, and AI search on the same day — not because five teams coordinated, but because there was only ever one implementation.
The part that surprised us
We expected the payoff to be engineering leverage. The bigger payoff turned out to be AI.
When you ask an AI copilot a question about your data, it has to produce something. If it produces prose, you cannot check it. If it produces raw SQL, it can invent a query that does not respect your permissions, or that is subtly wrong in a way you will not notice.
Because we already had exactly one filter format, the AI emits that. Three consequences fall out for free, and none of them were the reason we made the decision:
- It cannot express a filter the product cannot run — the format is the constraint.
- It inherits permissions automatically, because the compiler is the same one that enforces row-level access for the UI.
- You can read what it wrote, correct it, and save it as a view. The answer is auditable, and an auditable answer is one you can eventually trust to act on its own.
The architectural decision that looked like internal hygiene turned out to be the thing that made AI trustworthy rather than decorative. That is usually how it goes with foundations: the reason you are glad you built one is rarely the reason you built it.
How to check this yourself
You do not have to take an architecture claim on faith. In any CRM trial, spend ten minutes on this:
- Build a moderately awkward filter in a list view — something involving a related record and an empty value.
- Try to build the identical filter as a campaign audience.
- Try again as an automation condition, and once more as a report filter.
- Ask the AI a question that should produce that same filter, and see whether you can inspect what it ran.
If all five agree, the product has an engine. If they do not, you have found the eleven builders — and you now know which of the five will be missing the operator you need in month four.