Your logic is slightly flawed. All async code can run in to stale data. The difference between Batchable, Queueable, and Future methods is how they're designed to behave. Batchable calls were designed to allow for heavy asynchronous processing. But, it was the only asynchronous code at one point, and it was too "heavy" in terms of resource usage, and had a lot of baggage associated with it. It required a long time to process and was often used only to handle callouts or things that were slightly longer than a normal transaction could handle.
So, next came future methods. Unlike Batchable methods, they were lighter on resources and perfect for doing that job that Batchable was doing before, but much more efficiently. Unfortunately, it was too efficient. You didn't know when the job was done (no Job ID), and it only had a limited amount of space to store data (the reason why it only accepted primitives and collections thereof).
In response to feedback, a third version of asynchronous code was designed: Queueable. It was lighter than Batchable, but it gave you a Job ID and let you store complex data patterns. The lesser resources it used compared to batchable also meant higher limits. Queueable was meant to be a hybrid between the limited future methods and the resource-hungry Batchable interface.
So, to get back to your question: you should always avoid passing in entire SObjects, especially if they may change between serialization and execution. If you need heavy processing, you probably want Batchable, while if you want normal-sized transactions with increased limits, you probably want Queueable. Of course, you can still use future methods if you don't need to monitor the results of the execution, and they'll continue to work for the foreseeable future.