<?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: Naveen Alavilli</title>
    <description>The latest articles on DEV Community by Naveen Alavilli (@naveen_alavilli).</description>
    <link>https://dev.to/naveen_alavilli</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4140078%2Fd351ad91-80c5-45b8-8946-0e59f869e352.png</url>
      <title>DEV Community: Naveen Alavilli</title>
      <link>https://dev.to/naveen_alavilli</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/naveen_alavilli"/>
    <language>en</language>
    <item>
      <title>The Validation Rule That Was Itself Invalid</title>
      <dc:creator>Naveen Alavilli</dc:creator>
      <pubDate>Wed, 23 Sep 2026 21:31:16 +0000</pubDate>
      <link>https://dev.to/naveen_alavilli/the-validation-rule-that-was-itself-invalid-12i7</link>
      <guid>https://dev.to/naveen_alavilli/the-validation-rule-that-was-itself-invalid-12i7</guid>
      <description>&lt;p&gt;I lost a save cycle last week to a bug that I now think is one of the most under-discussed failure modes in configurable systems: a validation rule that was itself incompletely configured.&lt;/p&gt;

&lt;p&gt;Not invalid data. An invalid &lt;em&gt;rule&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;If you have ever built or maintained a form builder, you know the shape. Forms are not code, they are data, arranged in a hierarchy that looks roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Form
└── Tab
    └── Section
        └── Question
            ├── type (text, date, number, radio, multi-select, ...)
            └── validations[]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A question carries a type and a list of validations. Text questions might accept &lt;code&gt;MaximumLength&lt;/code&gt;, &lt;code&gt;MinimumLength&lt;/code&gt;, &lt;code&gt;Email&lt;/code&gt;, &lt;code&gt;PhoneNumber&lt;/code&gt;, &lt;code&gt;Required&lt;/code&gt;. List questions bind to a shared option set so that "County" means the same 254 choices everywhere it appears. Authors compose forms in a UI, hit save, and the whole tree is persisted as a draft. Publishing is a separate, explicit step.&lt;/p&gt;

&lt;p&gt;This is a good architecture. It is also where the trap lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap
&lt;/h2&gt;

&lt;p&gt;Most validations are a single flag. &lt;code&gt;Required&lt;/code&gt; looks like one too. You add it to a question and move on.&lt;/p&gt;

&lt;p&gt;Except &lt;code&gt;Required&lt;/code&gt; was not a flag. Selecting it rendered a &lt;em&gt;second&lt;/em&gt; dropdown, with two choices, something like &lt;code&gt;Required&lt;/code&gt; and &lt;code&gt;Semirequired&lt;/code&gt; - a genuinely useful distinction when a field must be answered before publication but may be left blank during intake.&lt;/p&gt;

&lt;p&gt;That second dropdown had no default.&lt;/p&gt;

&lt;p&gt;So the rule sat there, attached to the question, looking complete in the list of validations, carrying no answer to the question "required &lt;em&gt;how&lt;/em&gt;?" I built out the rest of the form. Fifteen or so questions. Hit Save.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'Required' validation type is required
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inline, per question, for every question where I had added &lt;code&gt;Required&lt;/code&gt; and not set the sub-type. The save was rejected. The error was accurate. It was just forty minutes late.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this class of bug happens
&lt;/h2&gt;

&lt;p&gt;The rule had two states that the model could not distinguish:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Not configured yet&lt;/strong&gt; - the author is mid-edit, nothing is wrong&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configured to nothing&lt;/strong&gt; - the author has moved on, and this is now broken&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both serialize identically. A &lt;code&gt;Required&lt;/code&gt; validation with a null sub-type. The only thing separating "in progress" from "broken" is whether the author intends to come back to it, and intent is not a column.&lt;/p&gt;

&lt;p&gt;So the system deferred the judgment to the only moment it could be sure the author was finished: submit. That is a reasonable engineering decision and a poor authoring experience, and both things are true at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways out
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Make the invalid state unrepresentable.&lt;/strong&gt; If &lt;code&gt;Required&lt;/code&gt; always needs a strictness, then the rule is not &lt;code&gt;Required&lt;/code&gt; plus a modifier, it is two distinct rules: &lt;code&gt;Required&lt;/code&gt; and &lt;code&gt;Semirequired&lt;/code&gt;. One dropdown, no sub-dropdown, no null state. Most "field A is mandatory when field B is set" configuration problems dissolve the moment you stop modelling the dependency and start enumerating the valid combinations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Default to the safe option.&lt;/strong&gt; If enumeration is genuinely awkward, pick a default. Strict &lt;code&gt;Required&lt;/code&gt; is the conservative choice: it can only ever reject data that a laxer setting would have let through. An author who wanted &lt;code&gt;Semirequired&lt;/code&gt; will notice. An author who wanted &lt;code&gt;Required&lt;/code&gt; and got null will not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validate at the point of edit.&lt;/strong&gt; If you must keep the null state, do not save it up for submit. The moment focus leaves a half-configured rule, mark it. A red dot on the question in the tree costs nothing and turns a forty-minute feedback loop into a four-second one.&lt;/p&gt;

&lt;p&gt;Any one of these would have saved the cycle. The third is the cheapest to retrofit and the least satisfying, because it treats the symptom.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that generalises
&lt;/h2&gt;

&lt;p&gt;Configuration is code without a compiler.&lt;/p&gt;

&lt;p&gt;When behaviour lives in a database rather than a source file, everything the type system used to do for free becomes someone's explicit responsibility. A half-written &lt;code&gt;if&lt;/code&gt; statement will not compile. A half-written validation rule saves cleanly, sits in a table, and waits.&lt;/p&gt;

&lt;p&gt;The instinct that serves me best here is to ask, of any config schema: &lt;strong&gt;what is the set of rows this table can hold that no author would ever have meant?&lt;/strong&gt; If that set is not empty, the schema is doing less work than it should, and the difference is being paid for at save time by whoever is holding the keyboard.&lt;/p&gt;

&lt;p&gt;Worth an hour of design. Cheaper than forty minutes and a lost form.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Working notes from building form-driven systems. If you have fought the same fight in a different builder, I would like to hear how you modelled your way out.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>codequality</category>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
