<?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: CertiSetu</title>
    <description>The latest articles on DEV Community by CertiSetu (@certisetu).</description>
    <link>https://dev.to/certisetu</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%2F4138691%2F9164bd65-1731-45c5-9039-532fd7ed88bd.png</url>
      <title>DEV Community: CertiSetu</title>
      <link>https://dev.to/certisetu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/certisetu"/>
    <language>en</language>
    <item>
      <title>Your validation rules can't tell you when they go stale</title>
      <dc:creator>CertiSetu</dc:creator>
      <pubDate>Tue, 29 Sep 2026 09:28:02 +0000</pubDate>
      <link>https://dev.to/certisetu/your-validation-rules-cant-tell-you-when-they-go-stale-aeb</link>
      <guid>https://dev.to/certisetu/your-validation-rules-cant-tell-you-when-they-go-stale-aeb</guid>
      <description>&lt;p&gt;Last week I wrote about &lt;a href="https://dev.to/certisetu/when-your-validation-feedback-loop-is-two-weeks-long-5g16"&gt;validating applications when the only real test takes two weeks&lt;/a&gt;. Short version: CertiSetu files domicile, income, caste and EWS certificate applications to Uttar Pradesh's e-District system. A rejection comes back 7 to 20 working days later and rarely says which field caused it, so every check we can run has to run before the file goes out. When something does get rejected, a reviewer works out why, and the reason becomes a rule that runs on every application after it.&lt;/p&gt;

&lt;p&gt;A comment on that post pointed at a hole in the loop. A rule that blocks an application stops that application from ever reaching the department, so the rule never gets tested against the department again. If the rule came from something temporary, like one tehsil's affidavit preference that month, it can go stale and keep blocking valid files with nothing ever telling you.&lt;/p&gt;

&lt;p&gt;That's right, and it's a bigger gap than it first looks.&lt;/p&gt;

&lt;h2&gt;
  
  
  A blocking rule never hears back
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn2ujz9xxsin4a5z03809.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn2ujz9xxsin4a5z03809.png" alt="A rule that passes a file eventually gets an answer from e-District. A rule that blocks a file never does." width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Look at what each outcome can teach a rule. When a rule lets a file through, the department eventually accepts or rejects it, and that answer is evidence. When a rule blocks a file, the file is never submitted, so there is no answer.&lt;/p&gt;

&lt;p&gt;The evidence is lopsided as a result. A rule that is too loose shows up in the rejection log. A rule that is too strict looks exactly like a rule doing its job. In our own records, every false positive reads as a mistake we caught.&lt;/p&gt;

&lt;p&gt;There was a good post here this week about how &lt;a href="https://dev.to/nyaomaru/your-type-guard-can-silently-drift-from-your-typescript-type-o57"&gt;a type guard can silently drift from the TypeScript type it's meant to describe&lt;/a&gt;. This is the same failure with a slower clock. The check still agrees with itself. It has stopped agreeing with reality, and nothing in the system is built to notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rules don't all age at the same rate
&lt;/h2&gt;

&lt;p&gt;The last post sorted checks into three groups, and they go stale very differently.&lt;/p&gt;

&lt;p&gt;A check that stands on its own ("this date parses", "this file opens") doesn't really go stale.&lt;/p&gt;

&lt;p&gt;A check across two documents mostly doesn't either. The department is not going to start accepting an Aadhaar and a ration card with two different spellings of the applicant's name. (If you're an applicant stuck on exactly that, there's a guide on &lt;a href="https://certisetu.com/guides/name-mismatch-aadhaar-ration-card" rel="noopener noreferrer"&gt;fixing a name mismatch between Aadhaar and ration card&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;The third group is the risky one: rules we learned from a rejection and can't verify ourselves. One tehsil wanted a particular affidavit format. One office started asking for a more recent utility bill. Each of these came from a single rejection, in one place, at one time. They're the rules most likely to be wrong six months later, and they're also the ones we're most tempted to make blocking, because the rejection that produced them was painful.&lt;/p&gt;

&lt;p&gt;So the first change is small: a rule has to say which kind it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every rule records where it came from
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;affidavit-format-tehsil&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;learned&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// "intrinsic" | "relational" | "learned"&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;rejectionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rej_...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;observedOn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-08-14&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;district&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tehsil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;reviewBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-11-14&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;warn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;// "warn" | "block"&lt;/span&gt;
  &lt;span class="nx"&gt;applies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;check&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;affidavit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This tehsil rejected this affidavit format recently.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three fields matter here.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;source&lt;/code&gt; ties the rule to the rejection that created it. Anyone reading the rule later can see why it exists and judge whether that reason still holds. This was the other half of the comment's suggestion, and it's cheap.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;scope&lt;/code&gt; keeps a rule learned in one tehsil from applying to the whole state. Without it, one office's habit turns into a statewide requirement the moment someone writes the check.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;reviewBy&lt;/code&gt; gives learned rules an expiry. When the date passes, the rule drops back to a warning until a reviewer looks at it again. Intrinsic and relational rules don't get one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start as a warning, earn the block
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3p6bdl4zk6jvg8n36z2l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3p6bdl4zk6jvg8n36z2l.png" alt="Lifecycle of a learned rule: it starts as a warning, can become a block, drops back to a warning when its review date passes, and returns to review when overrides get accepted." width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A learned rule starts out as a warning. The reviewer sees it, decides, and can file anyway. It becomes blocking only after a second, separate rejection points to the same cause.&lt;/p&gt;

&lt;p&gt;This has a cost. Warnings are easier to wave through, and a reviewer who overrides a correct warning sends someone's file into a two-week wait for nothing. But the two kinds of mistake aren't the same size. A wrong warning costs a reviewer a few seconds. A wrong block can cost an applicant a certificate they were entitled to, and we would never find out it happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  An override is the only test a rule gets
&lt;/h2&gt;

&lt;p&gt;The comment's second suggestion was to let a reviewer override a rule and file anyway now and then, since that's the only way a check can learn it was wrong. I had been treating overrides as noise to minimise. They're the opposite: the only experiments the system runs on its own rules.&lt;/p&gt;

&lt;p&gt;So each override gets logged against the rule it overrode, and the department's answer is attached to it when the file comes back. Over time a learned rule collects a small history:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;overridden and then accepted, which is evidence the rule is wrong, at least in that scope&lt;/li&gt;
&lt;li&gt;overridden and then rejected for the same reason, which is evidence it's right&lt;/li&gt;
&lt;li&gt;overridden and then rejected for something unrelated, which tells us nothing about this rule&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If overrides of a rule start coming back accepted in the same scope, the rule goes back to review. The numbers are far too small for statistics. The point is only that a rule now has some way to lose an argument.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same trap applies to document reading
&lt;/h2&gt;

&lt;p&gt;Most of the tooling attention right now is on AI that reads documents, so it's worth saying this applies there too. In the first post I argued that anything reading a phone photo of a certificate should treat "I could not tell" as a normal result. Staleness is the same problem one level up. A model that extracts fields confidently, checked only against rules that were themselves learned from its own output, has no route to finding out it's wrong. Whatever does the checking needs a path back to an outside answer, even a slow one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a rule is now
&lt;/h2&gt;

&lt;p&gt;A rule used to be a function. Now it's a function plus a claim: this was true, in this place, as of this date, because of this rejection. Claims go out of date, and the system should expect that instead of being surprised by it.&lt;/p&gt;

&lt;p&gt;If you work against a slow or silent source of truth (a regulator, a bank's batch process, a partner API that only emails you errors), how do you retire rules that might no longer be true? I'd like to hear what's worked.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;CertiSetu is an independent private platform, not a government portal. Certificates are issued and approved by the relevant government authorities, and anyone can apply directly on the UP e-District portal. For the applicant's side of this, see &lt;a href="https://certisetu.com/guides/why-applications-get-rejected" rel="noopener noreferrer"&gt;why applications get rejected&lt;/a&gt; and &lt;a href="https://certisetu.com/guides/reading-a-government-notification" rel="noopener noreferrer"&gt;how to read a government notification&lt;/a&gt;. The form design side is on &lt;a href="https://certisetu.hashnode.dev/designing-forms-for-people-who-will-only-fill-them-once" rel="noopener noreferrer"&gt;our Hashnode blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>architecture</category>
      <category>discuss</category>
    </item>
    <item>
      <title>When your validation feedback loop is two weeks long</title>
      <dc:creator>CertiSetu</dc:creator>
      <pubDate>Wed, 23 Sep 2026 05:35:03 +0000</pubDate>
      <link>https://dev.to/certisetu/when-your-validation-feedback-loop-is-two-weeks-long-5g16</link>
      <guid>https://dev.to/certisetu/when-your-validation-feedback-loop-is-two-weeks-long-5g16</guid>
      <description>&lt;p&gt;A card gets declined in under a second. A pincode service tells you the city does not match while the user is still looking at the field. You write a rule, send a bad value, read the error, fix the rule.&lt;/p&gt;

&lt;p&gt;We do not have any of that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://certisetu.com" rel="noopener noreferrer"&gt;CertiSetu&lt;/a&gt; files applications for four Uttar Pradesh government certificates: &lt;a href="https://certisetu.com/domicile-certificate" rel="noopener noreferrer"&gt;domicile&lt;/a&gt;, &lt;a href="https://certisetu.com/income-certificate" rel="noopener noreferrer"&gt;income&lt;/a&gt;, &lt;a href="https://certisetu.com/caste-certificate" rel="noopener noreferrer"&gt;caste&lt;/a&gt; and &lt;a href="https://certisetu.com/ews-certificate" rel="noopener noreferrer"&gt;EWS&lt;/a&gt;. A user verifies once through DigiLocker, uploads their documents, and the application goes to the state's e-District system. From a complete filing, the first three take 7 to 10 working days. EWS takes 15 to 20.&lt;/p&gt;

&lt;p&gt;If the department rejects the file, that is when we find out. And the rejection usually does not say which field caused it.&lt;/p&gt;

&lt;p&gt;So the only authoritative test we have takes two weeks and comes back with roughly one bit of information.&lt;/p&gt;

&lt;h2&gt;
  
  
  What that rules out
&lt;/h2&gt;

&lt;p&gt;The normal loop is to ship a validator, watch it fail in production, then tighten it. That works because production answers quickly and specifically.&lt;/p&gt;

&lt;p&gt;Here production answers slowly and vaguely, and every iteration costs a real person a fortnight and usually a deadline. You cannot learn by submitting.&lt;/p&gt;

&lt;p&gt;Anything we are able to decide ourselves has to be decided before the file goes out, because afterwards we are blind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three kinds of check
&lt;/h2&gt;

&lt;p&gt;Sorting the checks turned out to be more useful than writing any one of them.&lt;/p&gt;

&lt;p&gt;Some things are true or false on their own. A date parses. A file opens. An income figure is a number and not a string with a rupee sign in it. These are cheap, they belong in the browser, and they are almost never why something gets rejected.&lt;/p&gt;

&lt;p&gt;Some things are only true or false in relation to something else the user gave us. The name on the Aadhaar has to match the name on the ration card. The address on the electricity bill has to be the address typed into the form. An EWS application needs an income certificate from the right financial year, not just any income certificate. This is where the rejections actually come from.&lt;/p&gt;

&lt;p&gt;And some things only the department can decide, like whether a particular tehsil is accepting a given affidavit format this month. We cannot check those at all. We can only write down what happened.&lt;/p&gt;

&lt;p&gt;The middle group is the whole problem, and it does not fit the shape most validation libraries are built around.&lt;/p&gt;

&lt;h2&gt;
  
  
  Field validators are the wrong shape
&lt;/h2&gt;

&lt;p&gt;A field validator takes one value and returns a verdict:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validators&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;annualIncome&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;applicantName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The checks that matter need the whole application, and they need to name the two things that disagree, because a person is going to have to open both. Something closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name-match-aadhaar-ration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;applies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aadhaar&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rationCard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;check&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aadhaar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nf"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rationCard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aadhaar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rationCard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This name is spelled differently on these two documents.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That evidence list is the part worth keeping. "Validation failed" tells whoever has to fix it nothing. "These two files disagree, here they are" is a job someone can finish in a minute.&lt;/p&gt;

&lt;h2&gt;
  
  
  A slow oracle needs a fast stand-in
&lt;/h2&gt;

&lt;p&gt;Since we cannot ask the department anything, we put a person in front of it. Every application is reviewed by a verified agent against the department's current rules before it is filed.&lt;/p&gt;

&lt;p&gt;That is a bottleneck. It is also the only fast feedback in the system. A reviewer catches a mismatched name in a minute, where the department catches it in ten working days and does not say what it was.&lt;/p&gt;

&lt;p&gt;The reviewer is also where new rules come from. When something is rejected, the reason gets worked out once and then becomes a check that runs on every application after it. A rejection that does not turn into a rule costs two weeks and teaches nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The inputs are photographs
&lt;/h2&gt;

&lt;p&gt;This shaped the code more than I expected. People fill these forms on a phone, at night, with the document lying flat on a bed. The image is rotated. The flash has blown out one corner. A ward councillor's stamp is legible to a human and not to much else.&lt;/p&gt;

&lt;p&gt;Anything that reads these automatically has to treat "I could not tell" as an ordinary outcome rather than an error. We would rather send an unclear document to a person than let a confident parser read the wrong year off a smudged stamp and drop the file into a two-week hole.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this leaves us
&lt;/h2&gt;

&lt;p&gt;You cannot iterate against the system of record when the system of record answers in working days. So you iterate against your own record of what it rejected, and you spend the engineering effort on the step before submission instead of the step after it.&lt;/p&gt;

&lt;p&gt;None of this is a hard distributed systems problem. It is one slow, vague, unmockable dependency, and nearly every design decision here falls out of that single fact.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;CertiSetu is an independent private platform, not a government portal. Certificates are issued and approved by the relevant government authorities, and anyone can file directly on the UP &lt;a href="https://edistrict.up.gov.in" rel="noopener noreferrer"&gt;e-District portal&lt;/a&gt;. Our &lt;a href="https://certisetu.com/guides" rel="noopener noreferrer"&gt;guides&lt;/a&gt; go through the document requirements for each certificate, and there is more on the form design side of this at &lt;a href="https://certisetu.hashnode.dev" rel="noopener noreferrer"&gt;certisetu.hashnode.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ux</category>
      <category>india</category>
    </item>
  </channel>
</rss>
