The script had worked for weeks. Then, overnight, a dropdown that used to hold four options held sixteen, a rule that used to be allowed was forbidden, and a field started closing itself half a second after it opened. Nobody told us. The portal had simply changed.
Brittle UI automation is scripting against a user interface whose structure and rules can change without notice, so the script’s assumptions can silently expire between runs.
Table of Contents
The problem, in one overnight change
The automation had been steady for weeks: fill a form, choose from a few dropdowns, submit, confirm. Then a single redesign of the portal broke it in several places at once, and each break was its own small surprise.
- A role selector that had offered four choices now offered sixteen, and a value the script selected by position or by a now-stale name no longer resolved.
- A combination that used to be permitted — selecting more than one role — was now forbidden by a rule the form stated but the script did not know.
- A newly required field appeared that had not existed, so submissions that used to pass now failed validation.
- Dropdowns were re-rendering during an async load and closing themselves a fraction of a second after opening, so a click that used to land now missed.
No single change was dramatic. Together they meant the script was now operating a form that no longer matched its model of the form — and the portal never said a word.
Why it stayed silent
The redesign produced almost no honest errors, which is what let it run broken.
A UI script mostly interacts through the same surface a human uses, and that surface is forgiving in the worst way: a click that misses does not throw, it just does nothing; a field that is now required does not announce itself until submit; a selector that matches a differently-labeled control may match the wrong control rather than none. So the automation kept going through the motions, and the failures showed up as downstream weirdness — records not created, wrong values chosen, submissions rejected for reasons the script did not surface — rather than as a clean stack trace pointing at the cause.
This is the same trap as trusting a UI’s own success message: the visible layer reports what it tried, not what actually took effect, so only a check against the system of record tells you the truth. A redesign widens the gap between the two and hides in it.
A UI does not throw when your assumptions expire; it just quietly does the wrong thing.
Proving the portal changed, not the script
The first instinct was to suspect our own code — a regression, a dependency bump, an environment change. Ruling that out was quick and worth doing: the script had not changed between the last good run and the first bad one. Same code, same inputs, different result. When your code is identical across a good run and a bad one, the variable is not your code.
Reading the live page confirmed it directly. The role control genuinely had sixteen options where it had held four; the form now displayed a rule text that had not been there; a field carried a required marker it had lacked. These were not misread by the script — they were really there. The ground had moved, and the evidence was on the page itself, not in our logs.
When identical code fails on a run it used to pass, stop debugging the code and go read the dependency.
Surviving a moving target
You cannot stop someone else’s portal from changing. You can build the automation so that a change is caught fast, contained, and loud rather than slow, cascading, and silent.
- Verify against the portal’s own state, never the step. After selecting a value, confirm the value is set; after submitting, confirm the record exists in the portal, not that a toast appeared. A check against real state survives a restyle that a check against the UI’s optimism does not.
- Isolate each interaction. Drive one control at a time and confirm it took before moving on, so a single changed control fails in place instead of corrupting everything after it. The auto-closing dropdown is survivable if each pick is verified and retried; it is a disaster if one silent miss poisons the rest of the form.
- Assert your assumptions, loudly. If the role control should have the options you expect, check that and fail with a specific message when it does not. An automation that announces “the role list changed” on the next run turns a silent weeks-long breakage into a same-day fix.
- Re-read the page when a step misbehaves, before retrying blindly. A retry against a changed control just fails again; reading the current state tells you what changed, which is the difference between a fix and a flail — the same instinct behind treating a missing control as a configuration change, not a UI glitch.
None of this prevents the redesign. All of it shortens the distance between “the portal changed” and “we know exactly how.”
What resilience cannot buy
No amount of defensive automation makes an unowned portal a stable dependency; it only shortens your reaction time. A redesign can introduce a rule you cannot satisfy at all — a newly required field for data you do not have — and no verification loop conjures the missing data. Assertions catch the changes you thought to check; a change you did not anticipate still slips through until it causes a visible failure. And there is a genuine ceiling: past a certain rate of change, scripting a human UI is the wrong integration and an actual interface — an API, a data feed, a supported import — is the only durable answer, if one exists. Resilient UI automation is how you cope with a moving target you have no better way to hit; it is not a substitute for solid ground — and it assumes the automation has the page to itself, which is not true if a human is driving the same tab.
The short version
Automating a web portal you do not own means depending on an interface that can be redesigned without notice — new fields, changed rules, restyled controls — and it will not fail cleanly when that happens. A UI script keeps going through a forgiving surface: a missed click does nothing, a newly required field is silent until submit, a stale selector may hit the wrong control. So a redesign shows up as downstream weirdness, not a stack trace. Prove it is the portal and not your code by noting that identical code failed on a run it used to pass, then read the live page. Survive it by verifying against the portal’s real state rather than the step, isolating each interaction, asserting your assumptions loudly so a break surfaces on the next run, and re-reading the page before retrying. None of this stops the change; it shortens the time to knowing exactly what changed — and past a certain churn, a real interface beats scripting the UI at all.
Frequently Asked Questions
Why does my working automation suddenly break with no code change?
Most often because something it depends on changed underneath it — and when you automate a web portal you do not own, the portal’s interface is exactly that kind of unversioned dependency. A redesign can add fields, change permitted values, or restyle controls with no notice. Identical code failing on a run it used to pass is the signature: the variable is not your code, it is the ground it stands on.
Why doesn’t the automation throw a clear error when the portal changes?
Because it acts through the same forgiving surface a human uses. A click that misses does nothing rather than erroring; a newly required field stays silent until submission; a selector may match a differently-labeled control instead of matching none. So a redesign surfaces as downstream weirdness — wrong values, missing records, rejected submits — rather than a clean failure pointing at the cause.
How do I tell whether my code or the site changed?
Compare a run that used to pass with the one that failed. If the code and inputs are identical across the two, the change is external — the site. Then read the live page directly: count the options in a control, look for new required markers or rule text. Confirming the change is on the page, not in your logs, tells you to fix your model of the site rather than hunt a phantom regression.
How do I make UI automation resilient to redesigns?
Verify against the site’s own state instead of trusting a step succeeded, isolate each interaction so one changed control fails in place rather than cascading, assert the assumptions that matter so a break announces itself loudly on the next run, and re-read the page when a step misbehaves before retrying. These do not prevent a redesign; they shorten the gap between the change happening and you knowing precisely what it was.
Should I use an API instead of automating the interface?
If a supported interface exists — an API, a data feed, an import path — it is almost always the more durable integration, because it changes on a deliberate, usually versioned schedule rather than whenever the UI is restyled. Scripting the human interface is the fallback for when no such path exists. Past a certain rate of UI change, the effort of keeping a screen-scraper alive outweighs the cost of adopting or requesting a real interface.
How can I find out about portal changes before they break things?
There is rarely a changelog for a UI you do not own, so build the notification into your own automation: assertions that check the controls and rules you depend on, run regularly, that fail loudly when reality diverges from your model. That turns “we discovered it broke weeks ago” into “the assertion flagged it on today’s run,” which is the closest thing to advance warning an unowned interface offers.