I am a musician. Over the years I have played keyboards in many bands, traveling and performing mostly as a hobby. I’ll never forget what one of my mentors taught me. He said
“The rest is just as important as the note”
The rest is the part of the song where you don’t play. Young musicians often forget this and take great delight in trying to dazzle you with their dexterity. Put a group of young musicians together and you get a cacophony of noise. The purpose, the beauty, the soul of the music is lost in the means of its production.
When I enter a planning process in building software, I want to keep this principle in mind. The things I leave out are just as important as the things I put in.
Unlike music or art which can exist simply for their beauty, software is utilitarian. It is a tool and no tool ever existed without a problem that it seeks to solve. The first step in building a great tool is to be very clear about the problem.
“Well those drifter's days are past me now I've got so much more to think about Deadlines and commitments What to leave in, what to leave out” - Bob Seger – “Against the Wind”
“Well those drifter's days are past me now I've got so much more to think about Deadlines and commitments What to leave in, what to leave out”
- Bob Seger – “Against the Wind”
The art of building great software begins with the art of making great choices. We can’t do everything… we need to do the best thing that we can with the time we’ve and resources we’ve got.
Right now I’m still in the stage of defining it but I’m going to be as transparent as possible with you in this process with the hope that you can give me feedback. There is some risk in this. You might disagree with my conclusions. You might wonder about the priorities that we have as a team. You might get the wrong impression… blah, blah, blah…
We live in a time of increasing secrecy in the tech industry. Companies take delight in hiding the details and then in one dramatic moment pulling back the curtain to a round of applause. I suppose that for some things that is the best approach, but you and I are in a different place. We need each other and we need to be transparent which means we run the risk of misunderstanding and that’s ok.
I’ve been given the job of making Workflow and System.Threading.Task work well together. I began by researching the situation as it exists today. I might have come to conclusion that there is no problem or that even if there is a problem that it is so small it’s not worth addressing. Or I could have concluded that the problem is so massive that nothing can be done about it. I came to my initial conclusions by talking with some of you, my own experience and many hours prototyping and trying to work with tasks and workflows together.
There are two key problems and one opportunity.
Here are a few examples
As more and more developers become familiar with System.Threading.Task and the way it works with other classes in the .NET Framework they will expect that Workflow works the same way. If it did work the same way then someone who is familiar with Task will find Workflow more familiar.
Suppose you want to create an activity that access a database and you want to use SqlConnection.OpenAsync(cancellationToken). This new API is great because if the database server is down, you can cancel the Open operation on demand. Your first challenge is how to pass a CancellationToken from your workflow hosting code to the database activity. You could pass it as an in argument and require workflow authors to pass it to your activity but that doesn’t seem like the right approach.
So instead, you decide to pass it as an extension only to find that when you call workflowApp.Extensions.Add(cancellationToken) that you get an error stating that extensions must be reference types. No problem, you create a class that allows you to pass the token as an extension to the activity.
Now you’ve got it right? So you create a unit test to verify that if you try to connect to a server that does not exist and then cancel the token using CancellationTokenSource.Cancel that the activity immediately cancels the OpenAsync call and returns.
At first you think it works but then you notice that your workflow terminates with a TerminationException of AggregateException –> OperationCanceledException.
Is that ok? Should a cancel cause a workflow to terminate?
No, it should not. Workflow already has a model for cancellation of activities. Someone may use your database activity inside of a CancellationScope. If your activity faults when it is really just canceled then the cancellation handler will not be invoked.
This is just one of several problems I’ve identified with using tasks inside of activities.
Any time you change an existing API there are both risks and opportunities. We could try to make it better, simpler and more powerful and actually end up making it worse, more complicated or break existing things. We must be very careful about how we approach this opportunity.
When it comes to workflows and tasks here are my design principles.
Ok, ready for the risk disclaimer?
None of this might happen. Or what eventually happens might be radically different than what I am thinking right now. Blogs live forever so if you come back in 5 years and read this you might laugh… but that is a risk I’m willing to take.
Happy Coding!
Ron http://blogs.msdn.com/rjacobs Twitter: @ronljacobs