Skip to main content

Reliable Macro Patterns

The difference between a demo macro and a production macro is usually structure. A reliable macro does not just act. It checks state, performs one action, verifies the result, and only then continues.

Use Guard, Act, Verify

This is the safest default pattern for UI automation:

  1. Guard: confirm the expected state before interacting
  2. Act: perform one clear action
  3. Verify: confirm the UI changed as expected
Screen:wait("login-button.jpg", 5000)
Screen:click("login-button.jpg")
Screen:wait("home-title.jpg", 8000)

This pattern is slower than blind clicking, but it prevents most flaky failures.

Avoid Blind Sequences

A fragile macro looks like this:

Screen:click(900, 2100)
Screen:click(900, 2100)
Screen:click(900, 2100)

A reliable macro usually looks like this:

Screen:wait("continue.jpg", 5000)
Screen:click("continue.jpg")
Screen:waitVanish("continue.jpg", 5000)

Prefer State Transitions Over Fixed Delays

Use delays only when there is no reliable visual or text signal.

Better:

Screen:click("submit.jpg")
Screen:wait("success.jpg", 10000)

Less reliable:

Screen:click("submit.jpg")
Utils:sleep(5000)

If you must use a delay, keep it short and explain why.

Add Timeouts to Every Waiting Step

Every advanced macro should define failure boundaries. A missing timeout can stall the entire run forever.

Good:

Screen:wait("reward-button.jpg", 7000)

Better:

if Screen:exist("reward-button.jpg") then
Screen:click("reward-button.jpg")
else
Utils:alert("Reward button not found")
end

Design Fallback Paths

Real apps change state unexpectedly. Your macro should know what to do if the ideal path is unavailable.

Typical fallbacks:

  • close a popup if it blocks progress
  • retry the current action once
  • go back to a known screen
  • stop with a clear error message

Retry the Step, Not the Whole Macro

When a step is flaky, retry the smallest safe unit.

local success = false

for i = 1, 3 do
if Screen:exist("claim.jpg") then
Screen:click("claim.jpg")
success = true
break
end
Utils:sleep(500)
end

if not success then
Utils:alert("Unable to find claim button")
end

Keep UI State Observable

You should always know which screen the macro believes it is on.

A strong technique is to define one stable identifier for each major state:

  • a title
  • a button unique to that screen
  • a known text label
  • a small image anchor

Then use those identifiers to drive branching logic.

Separate Setup, Main Flow, and Recovery

Complex macros become easier to debug when split into phases:

  1. Setup
  2. Main task
  3. Recovery or cleanup

That makes failures easier to locate and easier to maintain over time.

Use Dialogs and Stored Values for Controlled Variation

If the macro changes based on user choice, do not duplicate the full macro. Let the user choose parameters up front, then run one shared flow.

Useful tools:

  • Dialog for run-time input
  • variables for short-lived state
  • Storage for persisted state between runs