RPA Coding Best Practices
RPA code that works is not the same as RPA code that is good. Bad code runs today but breaks tomorrow, takes hours to debug, and nobody else can understand or maintain. Good code is readable, reliable, and reusable. Following best practices from your first bot saves enormous time over the lifetime of the automation programme.
Practice 1: Modular Design
Break every bot into small, focused workflows. Each workflow does one thing well. Large monolithic workflows — everything in one .xaml file — are impossible to debug, test, or reuse.
BAD: One giant Main.xaml with 200+ activities GOOD: Main.xaml ← Orchestrates everything Initialise.xaml ← Load config and open apps GetTransactionData.xaml← Retrieve next item from queue ProcessInvoice.xaml ← Core processing logic PostToSAP.xaml ← SAP interaction only HandleException.xaml ← All error handling CloseAll.xaml ← Graceful shutdown
Practice 2: Meaningful Naming
Every variable, argument, and workflow file should have a name that tells you exactly what it holds or does — without needing to read the code to find out.
| Type | Bad Name | Good Name |
|---|---|---|
| Variable | x, str1, flag | invoiceAmount, vendorName, isApproved |
| Argument | a, input1, result | in_InvoiceData, out_SAPDocNumber |
| Workflow | Workflow1.xaml, test.xaml | PostInvoiceToSAP.xaml, ValidateVendor.xaml |
| DataTable | dt1, table | dt_PendingInvoices, dt_AuditLog |
Practice 3: No Hardcoded Values
Every value that might ever change — URLs, email addresses, file paths, thresholds, system names — must be stored in an Orchestrator Asset or a config file, never hardcoded in the workflow.
BAD:
IF invoiceAmount > 5000 THEN send for approval
Send email to "ap.manager@company.com"
GOOD:
approvalThreshold = GetAsset("ApprovalThreshold")
apManagerEmail = GetAsset("APManagerEmail")
IF invoiceAmount > approvalThreshold THEN send for approval
Send email to apManagerEmail
When the threshold or email changes → update the Asset.
Zero code changes. Zero redeployment. Zero risk of bugs.
Practice 4: Consistent Error Handling
Every workflow that interacts with an external system must have error handling. Define a standard error handling pattern and apply it everywhere, consistently.
STANDARD PATTERN: ───────────────────────────────────────────────────── TRY Perform the risky operation CATCH (SelectorNotFoundException) Retry Scope (3 retries, 30s interval) If still failing → throw to outer handler CATCH (System.Exception) Log: workflow name + error message + stack trace Take screenshot Update queue item as Failed with error reason Do NOT re-throw (continue to next item) FINALLY Close any open applications
Practice 5: Intelligent Logging
Log enough to reconstruct what happened — but not so much that logs become noise. Log every transaction start, completion, and failure. Log the key data values (transaction ID, amount, vendor) but never sensitive values (passwords, full card numbers).
GOOD LOG MESSAGES: "Starting processing of invoice INV-2024-0451 | Vendor: Acme Ltd | Amount: $1,500" "Invoice INV-2024-0451 posted to SAP. Document: 1900034521. Duration: 47s." "Invoice INV-2024-0453 FAILED: Vendor 'Unknown Corp' not found in approved list." BAD LOG MESSAGES: "Processing started" ← Which item? What data? "Error occurred" ← What error? Where? "Done" ← Done what? With what result?
Practice 6: Use Comments and Annotations
Add comments to explain why the workflow does something non-obvious. Future maintainers — including yourself three months later — will thank you.
Add annotation to a complex selector: "Using ID instead of position-based selector because the SAP screen reorders elements when in edit mode." Add comment before a 5-second delay: "SAP requires a 5-second wait after posting before the document number appears. Do not reduce this value."
Practice 7: Validate Inputs
Never assume input data is clean and correct. Validate every value the bot reads from a file, screen, or queue before using it in a critical action.
BEFORE POSTING TO SAP:
─────────────────────────────────────────────────────
IF String.IsNullOrEmpty(invoiceNo) THEN
Throw New ArgumentException("Invoice number is empty")
IF invoiceAmount <= 0 THEN
Throw New ArgumentException("Invalid invoice amount: " + invoiceAmount.ToString())
IF invoiceDate > DateTime.Now THEN
Throw New ArgumentException("Invoice date is in the future: " + invoiceDate.ToString())
Practice 8: Avoid UI Delays — Use Smart Waits
Never add fixed delays (Thread.Sleep or Delay activity) to wait for screens to load. Fixed delays break when the system runs slower than usual and waste time when it runs faster.
BAD:
Click [Login Button]
Delay: 5 seconds (hoping the page loads)
Type Into [Search Field]
GOOD:
Click [Login Button]
Element Exists [Dashboard Heading]
Timeout: 30 seconds
ErrorIfNotFound: True
(Bot waits until the dashboard appears — no fixed delay)
Type Into [Search Field]
Practice 9: Use REFramework for Queue-Based Bots
For any bot that processes items from a queue, use UiPath's REFramework as the base template. It provides pre-built state management, error handling, retry logic, and logging patterns that would take weeks to build from scratch.
Practice 10: Keep Selectors Stable
Review generated selectors and replace unstable attributes with stable ones. Use ID over position. Use wildcards in titles. Test selectors with UI Explorer before finalising.
FRAGILE: <webctrl idx='3' parentid='div_row_47' /> STABLE: <webctrl id='invoice-amount-input' tag='INPUT' />
Code Review Checklist
- No hardcoded credentials or sensitive values
- All activities have meaningful display names
- Error handling present on all external interactions
- Logging covers start, success, failure for each transaction
- No fixed delays — smart waits used throughout
- Selectors use stable properties
- Workflows are modular and focused
- All configurable values come from Assets
- Input validation present before critical operations
Summary
RPA coding best practices make bots reliable, readable, and maintainable. Modular design breaks complexity into manageable pieces. Meaningful naming makes code self-documenting. Eliminating hardcoded values makes bots configuration-driven. Consistent error handling prevents silent failures. Intelligent logging enables rapid diagnosis. Input validation prevents bad data from causing bad actions. Smart waits replace fragile fixed delays. Stable selectors survive application updates. Applying these practices consistently from the first line of code produces bots that the entire team can understand, trust, and maintain for years.
