Best practices in Workflow Design - Part 2
Naming convention in Workflows
Workflow activities should be named appropriately. As much as possible, they should reflect names form the business domain. For example, it wouldn’t appear good to have names like ifelseActivity1 or callExternalMethodActivity1. Instead, it is good to have names like ifProductsExists and MakePayment respectively. Shown below is a good example of the same:

Custom activity names should really represent their purpose. Good examples are ApproveRequestActivity. It is customary to suffix custom activity classes with “Activity”. It is also a good practice to provide apt descriptions to all the activities in the workflow. This would help better understand the nuances of the workflow logic.
Transactions in Workflows
In application design, avoid scenarios in which transactions need to span between business components and workflow logic. Either a database transaction should complete before the workflow is invoked, or should be started and completed within the execution of the workflow sequence (using a TransactionScope activity, for example). In either case, care must be taken to ensure that the transaction (and hence, locks on shared resources) should not be kept alive for a long period, as this adversely impacts performance. For long running transactions, compensation should be employed for undoing the effects of a prior transaction. WF has adequate measures (compensatable activities) to incorporate the same.
Sequential versus State machine workflow
It is definitely possible to represent a workflow scenario of a business application as a sequential or a state machine workflow. State machine workflows appear to be the right logical choice for long running business operations, which can typically be visualized as those transitioning from one state to another by means of some human or process input. A sequential workflow may be harder to represent the entire workflow process in these cases, thought it is not impossible.
State machine workflows also provide for better tracking of the process. Since each state in a state-machine workflow represents a logical step in a business process, the give better visibility into the current state of the process. Refer to this blog post for more info on the usefulness of state machine workflows.
Note that states within a state machine could have sequence of activities. Sequential workflows have their own place and have to be used appropriately.
Workflow Instance Identifiers - Correlation
Workflow scenarios, typically state machine ones, involve persistence. Here, the workflow waits for an event to occur and hence its state is persisted into the database. The workflow is brought to life by another part of the application which loads the hydrated instance and executes the workflow (perhaps transitions it to the next state). The unloading and loading is correlated by the WorflowInstanceId (a Guid value). It is the application’s responsibility to keep this value and then use it at a later point of time. It could be a wise thing to store this value in the database alongside with the other information and then fetch the same for correlation at a later point of time.
It is perhaps, not a good practice to wait for the runtime to start the workflow so as to get the WorkflowInstanceId. A better approach is to generate the Guid value and pass it the workflow runtime as a parameter. This helps in situations where starting a workflow is a small part of a transactional business operation, and you need to keep the WF runtime pieces out of transaction scope.
Error Handling in Workflows
It is important to understand that error handling in workflows is as important as those in the business components. Workflows that encounter an error, either through an exception thrown by a called service or that is raised internally, go in faulted state and complete prematurely. Since the execution nature of workflow is asynchronous, unhandled exceptions are not raised to the application and go unnoticed. This could adversely affect several parts of the application expecting a persisted workflow, especially in the case of a state machine workflow. Since the faulted workflow goes to the closed state, the application expecting the workflow to be in a specific state is left in an unrecoverable situation.
To avoid such situations, one should include appropriate fault handlers in the workflow. An example of the same is shown in the figure below. It would be the responsibility of the fault handler to take the workflow to a recoverable state. The fault handler could do things like logging the exception details, persist recovery information and so on, analogous to a catch block in C#.

State machine workflows are to be handled in a different way. State machine workflows do not have a workflow-wide fault handler collection (it does not make sense either). Here, one should provide a fault handler for every sub-activity within each state activity. It is a good practice to keep the semantics of a state machine even in the event of a fault. This can be done by having the fault handler move the workflow to a user-defined error state. At this state, the workflow could either wait for recovery to happen, might just log and exception and move to completed state, or whatever is the apt handling logic for the application. You could consider options for retrying for a fixed number of times as well.
Workflow Cancellation
Cancellation of workflows is handled similar to Faults. One should wire appropriate Cancellation handlers to every activity. Like a fault handler, the cancel handler should gracefully close a workflow by taking the appropriate action.