I didn't get much of an answer when I posted this question a while back.
My short take on it after personal experiences may be irrelevant because it is not based on much more than personal experience but here it is anyway...
Updating attributes will always cause events, grow event logs, check for subscriptions, etc. There are possible performance implications so unless I have a good reason for wanting to check this value outside of the device I do not use an attribute. Otherwise, I put data in an attribute. Examples, I have put the websocket state in an attribute as well as current values for things that I want to be able to check from RM.
Updating device data seems to be slightly more tedious than using state so I have decided to put permanent and semi-permanent (like firmware versions) in data.
All the rest like data needed for day to day operations of the driver or gimmicky things (like instructions) I put in the state. As far as I can tell this is the cheapest place to store data. Then there is also atomicState which is essentially an atomic state as the name suggests. If you need to work out thread-safe code this can be used because the values are persisted immediately instead of lazily when the driver instance is cleaned up. Don't mix state and atomicState I've been told. I don't know why but I picture some real "don't cross the streams" scenarios although highly unlikely.