On a retail project last year, we spent an afternoon chasing down Opportunity records with negative amounts that kept slipping past a validation rule we'd already written and tested. The culprit turned out to be a nightly sync job inserting records through the API with an admin credential that had a specific bypass configuration in place. That afternoon taught us more about where validation rules actually enforce — and where they silently don't — than any documentation had. The rule was correct. Our mental model of how it fired was not.
That gap between "we wrote the rule" and "the data is actually clean" is exactly what this post is about.
The logic is inverted — and that trips everyone up at least once
The single thing that confuses every new admin we've onboarded is that a validation rule formula should evaluate to true when you want to block the save. Not when you want to allow it. When the formula returns true, Salesforce blocks the operation and shows the error message. When it returns false, the save proceeds without complaint.
So a rule written as Amount < 0 blocks any save where Amount is negative. If Amount is positive or zero, the formula returns false and nothing happens. We've learned to read these formulas out loud as "block this save if..." — it reframes the logic in a way that matches how Salesforce actually evaluates it. Writing rules as "allow this save when..." leads to inverted conditions, and that leads to either blocking everything or blocking nothing, both of which are bad in different ways.
API enforcement: the behavior that surprises integration teams
Here's what we tell every integration team before they go live: validation rules enforce through the API. A record inserted via Data Loader, a REST call from an integration platform, a Flow, or a third-party ETL tool will fail with the same error as a UI save if the formula evaluates to true. This is not optional and it's not configurable at the rule level.
This is fundamentally different from Page Layout required fields, which only enforce in the browser. We've seen this confusion cause real problems — a team at a logistics company we worked with had several fields marked required on the page layout, assumed that meant the data was always populated, and then spent weeks cleaning up empty fields that had been coming in through an API integration for months. The integration didn't touch the UI, so the page layout requirement was completely invisible to it.
Our rule of thumb: if a data quality requirement needs to hold regardless of how the record arrives — through the UI, through an API, through a Flow, through anything — the enforcement has to be a Validation Rule or the field's universally required setting. Page Layout required is a UI convenience, not a data integrity guarantee.
ISCHANGED() and ISNEW(): the functions we use on almost every project
Once you move past simple "field must not be blank" rules, these two functions become the foundation of everything. ISCHANGED(field) returns true if the field value changed during the current edit. It returns false on new record creation because there's no prior value to compare against. ISNEW() returns true only when the record is being created for the first time.
The pattern we reach for most often is preventing a field from being reduced once it's been set. On a financial services project, we needed to ensure that once an Opportunity's contracted amount was entered, nobody could quietly lower it without going through an approval. The rule looked like this:
AND(
NOT(ISNEW()),
ISCHANGED(Amount),
Amount < PRIORVALUE(Amount)
)
This blocks any save where the record already exists, the Amount field was touched, and the new value is lower than what it was before. PRIORVALUE(field) is what makes this work — it gives you the field's value before the current edit began, so you can compare before and after in the same formula. Without NOT(ISNEW()), the rule would fire on every new record where Amount is blank, which is almost never what you want.
We find ISNEW() most useful for mandatory fields that only apply at creation — onboarding data that must be captured when a record is first created but doesn't need to be re-entered on every subsequent edit.
Cross-field validation: where rules earn their keep
Single-field checks are useful, but the real power is combining multiple fields in a single rule. We built a rule for a telecoms client that blocked reps from leaving Opportunities in an open stage once the close date had passed:
AND(
CloseDate < TODAY(),
StageName <> "Closed Won",
StageName <> "Closed Lost"
)
This kind of rule is impossible to express with page layout required fields, which only look at individual fields in isolation. The logic here is inherently relational — the Close Date only matters if the Stage is still open, and the Stage only matters in the context of the date. That relationship between fields is exactly what validation rule formulas are built to express, and we use multi-field rules on almost every project we deploy.
Cross-object validation: traversing up the relationship tree
A validation rule on a child record can reference fields on its parent using dot notation, and we use this regularly. For a healthcare client, we needed to ensure that any Opportunity linked to a healthcare-industry account had a compliance ID populated before it could be saved:
AND(
Account.Industry = "Healthcare",
ISBLANK(Healthcare_Compliance_ID__c)
)
The rule lives on the Opportunity but reaches up to the Account to check the Industry field. This is clean, works through the API, and requires no code. The constraint to know going in is that cross-object formulas only traverse upward — child to parent, and one level at a time. A rule on an Opportunity can reference its Account, and a rule on a Contact can reference its Account, but you can't write a rule on an Account that references its child Opportunities. And there's no path to an unrelated object unless a lookup relationship exists between them.
We always verify the relationship structure before promising a cross-object rule will work, because the error you get when the traversal is invalid isn't always obvious.
Where validation rules quietly do nothing
This is the part we wish was more prominently documented, because it's where real data quality gaps hide.
Record deletion is the most common blind spot. Validation rules do not evaluate when a record is deleted. If you need to prevent deletion of an Account that has active Opportunities, a validation rule will not help you. That logic requires a before-delete Flow or an Apex trigger. We've seen teams build elaborate validation rules to "protect" records, not realizing the rules are completely silent during delete operations.
Bypass via System Administrator credentials is the one that caused our negative-amount incident. In certain configurations — particularly integrations running under admin credentials with "Modify All Data" enabled — validation rules can be bypassed. The details depend on the tool and how it's configured, but the lesson we took away is: if a data requirement is truly non-negotiable, enforcement at the database level (universally required field) is more reliable than a validation rule alone. Rules can be worked around, intentionally or accidentally.
The losing record in a merge is a subtler gap. When two records are merged, the validation rule fires on the winning record if its fields change, but does not fire on the losing record that gets deleted in the merge. Usually this doesn't matter, but if your rule is protecting a field on the losing record, it won't protect it during merge.
Error message placement: a small thing that matters to users
On every rule we write, we decide whether the error message belongs at the top of the page or next to a specific field. For rules that fail because of a single field, placing the error next to that field is almost always clearer — the user sees exactly where to look. For multi-field rules where the problem is the combination of values rather than any single field in isolation, the top of page placement makes more sense because anchoring the error to one field implies that field alone is the problem when it isn't.
It's a small decision but one that affects how quickly users understand why their save failed. We've started treating error message copy as part of the rule design, not an afterthought — a rule that blocks correctly but explains itself poorly generates support tickets just as surely as a rule that doesn't fire at all.
How we approach this on every project
Our standard practice is to audit the enforcement path for every validation rule we deploy: does it need to hold through the API, and if so, have we tested it through an API insert, not just through the UI? Does it involve deletion scenarios, and if so, have we implemented a complementary trigger or Flow? Are there admin-credential integrations that could bypass it?
The rule is rarely the hard part. Understanding the full perimeter of where it does and doesn't fire — that's what separates a validation rule that actually protects data quality from one that just looks like it does.








