This can be a complicated question as there is often more than one way to achieve this kind of behaviour and which is best really depends on your specific situation.
Interesting threads on this are Adding a dynamic step while the instance is in flight and Dynamic subtasks
Some options are:
1) Include all the steps you'll want in the definition and use variables to have ways to skip them under certain conditions - this was raised in one of the threads above but see also Activiti is ineffective in China, and hopes to attract official attention.
2) Dynamically add a subtask to a process instance.
3) Dynamically choose a sub-process.
4) Dynamically set the implementation of a service task or other expression.
5) Programmatically changing the running version of the process definition (note this will apply for all process instances using the version when you make that change).
6) Adding additional conditional logic in a listener.
I think there are more. Not all of them will necessarily address your particular use-case.
There are limitations. If you need to make a change that depends upon a java class that hasn't been deployed yet then you'll get an error at execution time when you hit that task. You can try to do clever things with the java classloader to avoid a restart but that's tricky ground.
The difficulty might be less whether there is a way and more understanding all of the techniques that are available and fitting them to your case. Personally I would tend to try and go for something that retains as much of a connection to the original process diagram as possible in order to retain visibility of what it is that is running (which in your case sounds to me like 1). But there may be circumstances where that isn't adequate and yours might be such a circumstance as I don't know how much flexibility you need. It would be good if you can share as much as possible as it is a tricky area and your experiences could help the community.