<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ManojGompa</title>
    <description>The latest articles on DEV Community by ManojGompa (@manoj1512).</description>
    <link>https://dev.to/manoj1512</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1242584%2Fc1371c93-a1b4-412b-bc4c-6a3c94ade281.png</url>
      <title>DEV Community: ManojGompa</title>
      <link>https://dev.to/manoj1512</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/manoj1512"/>
    <language>en</language>
    <item>
      <title>Query Filters, UI Design and the CNF/DNF caveat nobody talks about</title>
      <dc:creator>ManojGompa</dc:creator>
      <pubDate>Sat, 16 May 2026 09:03:09 +0000</pubDate>
      <link>https://dev.to/manoj1512/query-filters-ui-design-and-the-cnfdnf-caveat-nobody-talks-about-16ef</link>
      <guid>https://dev.to/manoj1512/query-filters-ui-design-and-the-cnfdnf-caveat-nobody-talks-about-16ef</guid>
      <description>&lt;p&gt;I've worked on plenty of things — some more complex, some more well-known. But every now and then, something small stops you mid-implementation and turns out to be worth thinking about properly. No architecture concepts, no distributed systems  — just a quiet little observation that I thought was worth writing down.&lt;/p&gt;

&lt;p&gt;As part of a project I started working on recently, I had to integrate a &lt;strong&gt;custom boolean expression filter&lt;/strong&gt; into the product. Let users define conditions, combine them with AND/OR, nest them — you know the kind of thing.&lt;/p&gt;

&lt;p&gt;And my first instinct was obvious: a &lt;strong&gt;visual tree-like expression builder&lt;/strong&gt;. Interactive, widely used, makes the logic visible without making users write raw expressions. Done, right?&lt;/p&gt;

&lt;p&gt;But then I started wondering — is there a way to do better than the tree? Something flatter, more constrained, but still usable? Even though I'm not a UI designer or engineer, this felt like an interesting problem to sit with for a bit.&lt;/p&gt;

&lt;p&gt;So I thought about it. And the more I thought, the more I kept circling back to the same two goals:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;User friendly&lt;/strong&gt; — the UI shouldn't require users to understand grammar, parentheses, or boolean syntax at all. Just point and click.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible&lt;/strong&gt; — even if the UI is constrained to a specific form, users should still be able to express whatever nested logic they have in mind. The shape restricts the interface, not the expressiveness.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Those two goals seem simple. They're actually in constant tension. And that tension is exactly what makes filter UI design surprisingly hard to get right.&lt;/p&gt;




&lt;h2&gt;
  
  
  The two obvious extremes (keeping the tree aside for now)
&lt;/h2&gt;

&lt;p&gt;Let's start from scratch. You need users to express logical rules. Your first instinct gives you two options:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A — just let them type it:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"IN"&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"pro"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;referral&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="n"&gt;churned&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Powerful. Expressive.&lt;strong&gt;Shows every user who isn't a developer the exit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B — a flat AND/OR toggle:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Match [ ALL ▾ ] of the following:

  ┌─────────────────────────────────┐
  │  country     is    India        │
  │  plan        is    Pro          │
  │  signup_date after  Jan 2024    │
  └─────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean. Approachable. Works fine until someone needs &lt;code&gt;(A AND B) OR (C AND D)&lt;/code&gt; — at which point they're completely stuck and don't even know why.&lt;/p&gt;

&lt;p&gt;And that got me thinking — is there a way to keep the UI minimal and clean, but not silently take away expressiveness from the user? Like, can you have both?&lt;strong&gt;A UI that looks simple but doesn't secretly cap what logic you can express&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;That question is what pulled me deeper. And that's when I started thinking: what if you take a more &lt;em&gt;structured&lt;/em&gt; approach to the logic itself ? What if the UI enforced a specific logical form?&lt;/p&gt;

&lt;p&gt;That's where &lt;a href="https://en.wikipedia.org/wiki/Conjunctive_normal_form" rel="noopener noreferrer"&gt;CNF and DNF&lt;/a&gt; entered the picture — and honestly, they seemed like the perfect answer at first.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wait, CNF and DNF? — a quick refresher
&lt;/h2&gt;

&lt;p&gt;If those terms are unfamiliar, here's the short version:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Disjunctive_normal_form" rel="noopener noreferrer"&gt;DNF — Disjunctive Normal Form&lt;/a&gt;&lt;/strong&gt; = OR of ANDs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Group 1: (A AND B) ─┐
Group 2: (C AND D) ─┼─ OR'd together
Group 3: (E AND F) ─┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Conjunctive_normal_form" rel="noopener noreferrer"&gt;CNF — Conjunctive Normal Form&lt;/a&gt;&lt;/strong&gt; = AND of ORs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Clause 1: (A OR B) ─┐
Clause 2: (C OR D) ─┼─ AND'd together
Clause 3: (E OR F) ─┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both are normal forms for boolean logic. Both are well-studied, well-understood, and — crucially — both have a shape that maps &lt;em&gt;directly&lt;/em&gt; to a UI.&lt;/p&gt;

&lt;p&gt;DNF especially clicked for me, because it directly addressed both goals:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goal 1 — user friendly:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You never show the user a text box or ask them to write parentheses. The UI becomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add conditions inside a group → they get ANDed together&lt;/li&gt;
&lt;li&gt;Add another group → the groups get ORed together&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. &lt;em&gt;No syntax, no grammar, just buttons and dropdowns&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goal 2 — flexible:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Here's the thing that actually made DNF feel like the right answer — &lt;a href="https://en.wikipedia.org/wiki/Disjunctive_normal_form#Conversion_to_DNF" rel="noopener noreferrer"&gt;any boolean expression can be converted to DNF&lt;/a&gt;. In theory, that means the form doesn't cap what users can express, just how they express it. Unlike the flat AND/OR toggle, a user could say "all of these conditions together, OR this other set" — which covers a meaningfully wider slice of real-world logic.&lt;/p&gt;

&lt;p&gt;HubSpot does something similar :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─── Group 1 ─────────────────────┐
│  country = India                 │  ← ANDed together
│  plan = Pro                      │
└──────────────────────────────────┘
           OR
┌─── Group 2 ─────────────────────┐
│  referral = true                 │
│  signup_date &amp;gt; Jan 2024          │
└──────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can explain this to a non-technical people in 30 seconds.&lt;em&gt;It's flat. It's readable. User friendly. More expressive than a flat toggle&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Both goals, one clean structure. I was pretty convinced this was it.&lt;/p&gt;

&lt;p&gt;So when does it fall apart?&lt;/p&gt;




&lt;h2&gt;
  
  
  The explosion problem — this is the caveat
&lt;/h2&gt;

&lt;p&gt;Here's the part that actually surprised me when I dug into it.&lt;/p&gt;

&lt;p&gt;Both CNF and DNF have what's called an &lt;strong&gt;explosion problem&lt;/strong&gt; — and it goes in opposite directions for each.&lt;/p&gt;

&lt;p&gt;Take this expression: &lt;code&gt;A AND (B OR C OR D)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Totally reasonable thing to want. But DNF doesn't allow OR &lt;em&gt;inside&lt;/em&gt; an AND-group. To get it into valid DNF, you have to distribute A across the OR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  A AND (B OR C OR D)
             ↓
  (A AND B) OR (A AND C) OR (A AND D)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three output clauses from one input. Now go a step further:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  A AND B AND (C OR D OR E OR F)
             ↓
  (A AND B AND C)
  OR (A AND B AND D)
  OR (A AND B AND E)
  OR (A AND B AND F)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A is duplicated four times. B is duplicated four times. And this is &lt;em&gt;provably unavoidable&lt;/em&gt; — the exponential blowup when converting to normal forms is so well-documented that a whole separate technique, the &lt;a href="https://en.wikipedia.org/wiki/Tseytin_transformation" rel="noopener noreferrer"&gt;Tseytin transformation&lt;/a&gt;, was invented specifically to work around it. It's not a bug you can engineer around. It's a property of the form itself.&lt;/p&gt;

&lt;p&gt;CNF hits the same wall in reverse when you distribute OR over AND.&lt;/p&gt;

&lt;p&gt;The bottom line: &lt;strong&gt;any arbitrary boolean expression converted to CNF or DNF can blow up exponentially in clause count.&lt;/strong&gt; And when your UI &lt;em&gt;is&lt;/em&gt; the CNF/DNF structure, that blowup hits the user directly — as repetition, as confusion, as "why do I have to add this condition to every single group?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real users hit this all the time without knowing why.&lt;/strong&gt; There's a &lt;a href="https://community.hubspot.com/t5/HubSpot-Ideas/Custom-filter-rules-in-lists-and-views/idi-p/1018691" rel="noopener noreferrer"&gt;HubSpot Ideas thread&lt;/a&gt; where users have been asking for custom filter rules with proper nested logic — the ask has been open for years. &lt;br&gt;
&lt;em&gt;The problem isn't that these UIs lack expressive power — mathematically, DNF can represent any boolean logic. The problem is that the moment complexity crosses a threshold, the UI stops feeling like a tool and starts feeling like a constraint. Users know exactly what they want — they just can't map it to the interface without repeating the same painful things over and over.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  So where does that leave us?
&lt;/h2&gt;

&lt;p&gt;Honestly, the flat DNF/CNF UI isn't widely used in the wild — and I think this is why. The explosion problem is real, and once you've seen it, a strict two-level structure feels like a trap.&lt;/p&gt;

&lt;p&gt;What most UIs that handle complex logic actually reach for is some form of a &lt;strong&gt;tree abstraction&lt;/strong&gt; — technically an &lt;a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree" rel="noopener noreferrer"&gt;Abstract Syntax Tree (AST)&lt;/a&gt;. Instead of enforcing a fixed two-level shape, you let users nest groups inside groups:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AND
 ├── country = India
 ├── plan = Pro
 └── OR
      ├── referral = true
      └── AND
           ├── signup_date &amp;gt; Jan 2024
           └── trial_used = false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visual indentation replaces parentheses. You can nest as deep as you need. No duplication, no explosion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─── AND ──────────────────────────────────────┐
│  country = India                              │
│  plan = Pro                                   │
│  ┌─── OR ──────────────────────────────────┐ │
│  │  referral = true                         │ │
│  │  ┌─── AND ───────────────────────────┐  │ │
│  │  │  signup_date &amp;gt; Jan 2024            │  │ │
│  │  │  trial_used = false                │  │ │
│  │  └────────────────────────────────────┘  │ │
│  └──────────────────────────────────────────┘ │
└───────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the tree approach answers the question I started with: yes, you &lt;em&gt;can&lt;/em&gt; have a minimal-looking UI that doesn't secretly limit what the user can express. &lt;strong&gt;You just have to go one abstraction level deeper than DNF/CNF&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The spectrum — and why this is worth knowing
&lt;/h2&gt;

&lt;p&gt;All of this sits on a spectrum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Flat toggle → DNF/CNF editor → Tree/AST builder → Free-text expression
  (simple,          (structured,      (flexible,       (powerful,
 hits ceiling      hits explosion     scales well,      intimidating
   fast)            at scale)        but can be flashy)   to use)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Move right →&lt;strong&gt;more expressive power&lt;/strong&gt;, &lt;em&gt;more complexity to build&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Move left → &lt;strong&gt;easier to build and use&lt;/strong&gt;, &lt;em&gt;hits a ceiling faster&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This whole thing started from one implementation question and ended up pulling in boolean algebra, normal form theory, and how real products have handled it. That's the part I find genuinely interesting — not that the problem is hard, but that you wouldn't even know there's a formal angle to it unless you happened to dig sideways into it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're building something similar, worth knowing this spectrum exists before you pick a point on it. Anyway, went deeper than expected on this one — hope it was useful.&lt;/p&gt;




</description>
      <category>uidesign</category>
      <category>discretemathematics</category>
      <category>ui</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
