DEV Community

Cover image for I Filled Out Three Forms in My Own App and Got Three Completely Different Validation Experiences.
Avery
Avery

Posted on

I Filled Out Three Forms in My Own App and Got Three Completely Different Validation Experiences.

I was doing a manual walkthrough of a few user flows before a release, clicking through screens as an actual user would rather than testing individual components in isolation. Somewhere between the second and third form I filled out, something started feeling off in a way I could not immediately name.

The signup form validated aggressively as I typed. The moment I entered a single character in the email field, it turned red and told me the email was invalid, which was technically true but felt hostile given that I had typed exactly one letter of what would eventually be a complete address. The settings form did the opposite. It stayed completely silent through everything I typed, then dumped four separate error messages on me at once when I hit save, forcing me to scroll back up and figure out which fields it was actually complaining about. The contact form did something in between, waiting until I moved out of each field before evaluating it, which felt notably more reasonable than either of the others.

Three forms in one application. Three fundamentally different validation experiences. From a user's perspective, this made the product feel inconsistent in a way that was difficult to articulate but easy to notice, a sense that different parts of the app had been built by different people with different ideas about how software should behave.

Every one of these three approaches is a documented, legitimate validation strategy. None of them represents a technical mistake. But encountering all three across a single user session is not a series of independent technical decisions. It is the absence of a decision entirely.

Why validation timing fragments more visibly than most inconsistencies

Most of the AI generated inconsistencies worth examining live entirely inside the codebase. Prop drilling handled three different ways, dependency arrays that are sometimes over-specified and sometimes under-specified, naming conventions that drift across sessions. These are real problems with real costs, but the costs are borne almost entirely by developers reading and maintaining the code. Users never encounter them directly.

Validation timing is different because it crosses directly into the user experience. The choice between validating on change, on blur, or on submit is not primarily a code structure decision. It is a decision about how the product communicates with the person using it, when it interrupts them, and how much patience it extends before flagging something as wrong.

This means validation inconsistency has two costs stacked on top of each other. There is the usual maintenance cost of having three different implementations of conceptually the same behavior. And there is a product cost that shows up in how the application feels to use, which is harder to measure but often more consequential for whether people actually enjoy using the thing you built.

The three patterns and what each one is actually good for

Validating on change, meaning evaluating the field on every keystroke, provides the fastest feedback loop. This works genuinely well for certain specific cases, particularly password strength indicators where the user benefits from watching the requirement get satisfied in real time, or character counters where live feedback is the entire point of the feature.

It works badly for almost everything else, particularly for fields with a format that is only valid once fully entered. Email addresses are the canonical example. Every partially typed email is technically invalid, which means validating on change produces an error state that persists for most of the time the user is typing, only resolving at the very end. The user has done nothing wrong, but the interface has been telling them they have for the entire duration of their input.

Validating on blur, meaning evaluating when the user leaves the field, is the pattern that tends to feel most natural for standard text inputs. The user gets to finish their thought before being evaluated, but they still receive feedback before submitting the entire form, which means they can correct issues incrementally rather than encountering all of them at once at the end.

Validating on submit, meaning waiting until the user attempts to submit the whole form, has legitimate uses primarily for validations that cannot be performed on a single field in isolation. Cross-field validations, where the validity of one field depends on the value of another, or validations requiring a server round trip that would be wasteful to trigger on every blur, are reasonable candidates for submit-time evaluation.

The problem is not that any of these three is wrong. It is that they solve different problems, and using them interchangeably based on which session generated which form means the choice has nothing to do with which problem the specific form actually presents.

Why the AI defaults differently across sessions

The pattern selected in any given session appears to correlate with fairly incidental characteristics of how the form was described in the prompt and what the surrounding code happened to look like.

A form described with detailed field requirements, particularly requirements involving format constraints, tends to produce validate-on-change behavior, since the format constraints are prominent in the prompt and immediate validation seems responsive to them. A form described more simply, focused on structure rather than validation rules, tends to produce validate-on-submit behavior, since submit-time validation is the minimum implementation that technically satisfies the requirement to validate at all. A form generated in a session where an existing form using blur validation was visible in context is more likely to also use blur validation, though this only holds when the existing example happens to be in the visible context window.

None of these tendencies are unreasonable as local heuristics. The AI is responding sensibly to the information available in each individual session. But the information available in each individual session varies for reasons entirely unrelated to what the correct validation strategy for that particular form actually is, which is why the outcome varies too.

The user experience cost that does not appear in code review

A code reviewer looking at a form component with validate-on-change behavior sees a functioning implementation of a legitimate pattern. There is nothing to flag. The validation works, the errors display correctly, the form submits when valid. Every technical criterion is satisfied.

What the code reviewer does not see, because it is not visible in a single component in isolation, is that the form two screens over validates completely differently, and that a user moving between those two screens experiences the product as inconsistent in a way that erodes trust in the overall quality of the application.

This is the specific reason validation timing benefits from being an explicit rule rather than a per-component judgment call. The decision is not visible at the level where code review operates. It is only visible at the level of the complete user experience, which no individual component review ever examines. Rules can operate at that higher level in a way that component-by-component review structurally cannot.

What a validation timing rule needs to specify

The rule has to make the timing decision based on the type of field and validation involved, rather than leaving it as an open judgment call for each new form.

Form validation timing rule:
1. Standard text inputs with format requirements, including email, phone, and URL fields, validate on blur. Do not validate these on change, since partial input is necessarily invalid and flagging it produces a hostile experience.
2. Fields where live feedback is the actual feature, specifically password strength indicators and character counters, validate on change. This is the narrow exception, not the default.
3. Cross-field validations, where one field's validity depends on another field's value, evaluate on submit, since evaluating them earlier produces confusing intermediate states that resolve themselves as the user continues.
4. Validations requiring a server round trip evaluate on blur at the earliest, never on change, to avoid generating a request per keystroke.
5. On submit, all fields are re-validated regardless of their individual timing strategy, and focus moves to the first field with an error so the user does not have to search for what went wrong.
6. Error message placement and styling is identical across every form in the application, regardless of which timing strategy applies to that particular field.
Enter fullscreen mode Exit fullscreen mode

The core of this rule is that timing is determined by the nature of the field, not by the session that happened to generate it. An email field validates on blur whether it appears in a signup form, a settings form, or a contact form, because the reasoning about why blur is correct for email fields does not change based on which form the field lives in.

Why rule five matters more than it initially appears

The fifth rule, about re-validating everything on submit and moving focus to the first error, addresses a failure mode that the other rules do not cover on their own.

Even with correct per-field timing, a user can reach the submit button with errors present, particularly if they never focused certain fields at all, meaning blur validation never triggered for those fields. Without explicit submit-time re-validation, these fields can pass through unvalidated, or produce errors that the user cannot easily locate on a long form.

Moving focus to the first error is a small detail that has an outsized effect on how forgiving the form feels, particularly on longer forms where the error might be several screens above where the submit button sits. This is exactly the kind of detail that gets skipped when validation is implemented fresh in each session, since it is not necessary for the form to technically function and requires deliberate additional implementation effort.

What changed after the rule went into effect

New forms consistently used blur validation for standard fields, which was the pattern that had previously appeared least often despite being the most appropriate for the majority of cases. Validate-on-change stopped appearing for email and similar format-constrained fields entirely, which eliminated the specific experience of being told an email was invalid after typing one character.

The submit-time re-validation and focus management specified in rule five turned out to require the most implementation change, since it had been implemented inconsistently or not at all in most existing forms. This was the part that produced the most noticeable improvement in how forms actually felt to use, which was somewhat unexpected given that the original problem I noticed was about timing rather than error recovery.

Existing forms with inconsistent timing did not update themselves, and the older forms remain inconsistent until they get touched for other reasons. But new forms consistently follow the pattern, which means the inconsistency is bounded rather than growing with each new form the project adds.

The prompt does not matter. The rules do.

Validation timing is a decision about how your product communicates with the people using it, not just a technical choice about when a function runs. Leaving it as a per-session judgment call means the user experience of your forms varies based on how each form's prompt happened to be phrased, which is not a defensible basis for a product decision that users directly experience.

Look for the other decisions in your project that live at the boundary between code and user experience, where the inconsistency is invisible in individual component review but clearly visible to someone using the actual application. Those are the decisions that most benefit from being an explicit rule, precisely because no existing review process is structured to catch them.


Want to find where your React project has user-facing decisions that were never standardized?

I built a free 24 point checklist that helps you identify exactly that. The structural gaps where inconsistency is invisible in code review but obvious to anyone actually using the application.

Get the React AI Clean Code Checklist — free

Avery Code React AI Engineering System

Top comments (0)