The very first task is to make the identification of UI elements unnecessarily hard or completely impossible.
Never assign IDs to UI elements. Using IDs test automation tools have is the easiest way to locate elements. Also, when you change the UI layout, IDs remain and still make finding elements possible, but without the IDs testers have a much higher chance of ending up with broken tests.
If there is no way to get rid of IDs at least make them dynamic. Make sure to generate a new value each time a view is presented to a user. This way, all tests relying on the fixed ID values are automatically broken immediately after recording.
It's a great idea to add a timer to the main title of an application window. E.g.
This technique may complicate not only the identification of UI elements but the process of recording the test as well. Imagine if objects inside a test automation tool were grouped by a window title. Every object will then form a separate group.
Refreshing of data, time required to open a dialog or load a web page must be unpredictable. This will force automation engineers to use a lot of synchronization statements in their tests.
But wait! There are other ways of making timing essentially random.
This is oh-so very easy. Do not think about data volumes during software implementation and check your code with tiny databases and in a single user environment. I guarantee under real load the program will start responding slowly.
Deploy server part of your solution to a VM hosted by a server with a lot of other VMs. There is a high probability that neighbor VMs will grab processor/memory/disk resources when you need them most.
Never bother with implementing accessibility interfaces like UI Automation and ARIA in your application, or else it may give testers an efficient way of interacting with the application in an automated way. You simply can not give them such power.
Change the UI layout every week. If you followed the advice above this will knock your testers out. They may even start fantasizing about manual testing because with manual approach they won't be seeing the results of their work lying in ruins.
Your application must never reveal any evidence of it's current state and what it is doing. Divulge nothing that can be used to understand what is happening or verify that the application is behaving correctly. No status bars, no breadcrumbs. There should be no way to find out that the application is busy.
Application should not support alternative ways of navigation or interaction, like keyboard shortcuts. Want to navigate menu - mouse and only mouse, want to move cell focus - mouse only, keyboard arrows should not have effect. You get the idea.
Do not write logs, any logs. They may provide information about problems and contain clues on how to fix things.
Use buttons with icons on them and no matter what do not assign text labels or hints to those buttons. Coupled with absence of IDs it will make identification almost impossible or very unreliable.
Effectively cut any attempts to apply workarounds, like keyboard shortcuts or log analysis.
And finally, if tester asks you to do anything that contradicts my advice above, just ignore them.