In a recent email, a customer asked for a delegation feature. He wanted to do something like (purely theoretical syntax):
class MyClass: ITest
MyHelperObject o: ITest;
so that he could delegate the implementation of the interface to a helper object, and have the compiler automatically generate the interface implementation methods.
My response was that we had talked about this several times in the past, but hadn't been able to come up with a syntax that worked well.
This is a not uncommon state for us - on several occasions in the Whidbey (excuse me - “Visual Studio 2005”) design process, we tried to come up with a syntax that allowed the user to declare a trivial property (backing store, trival getter and setter) with one bit of syntax, but weren't successfull.
I thought I'd talk about that process to give you some insight on how we approach things. I can't be exhaustive with all of our considerations, mostly because we don't really operate that way (there isn't a set checklist). If you have a question on a specific feature, I am happy to consider talking about other features and decisions - see the “suggest a topic” hard link on my main page. Just note that my success rate on actually covering such topics is not exemplary. Also note that I make no attempt to answer questions in the order asked.
The process is fairly organic, in that the steps I list don't happen sequentially, and they are often iterative. Despite the length of this post, I've simplified what goes on significantly.
We start with what I'm going to call the “Sniff Test“, which is basically the highest level triage to determine if we think such a feature is something we would ever consider for C#. Some features fail because they don't fit the language philosophy, some fail because there's no way we could ever express them in syntax.
Our next level of attack is looking at what user problem we're trying to solve. This typically involves us writing down (or up, on the whiteboard) the code that users are currently writing. Sometimes this is limited to the most common scenario, sometimes we'll cover several scenarios - it all depends on what we need to know. This often happens iteratively with the next step.
If we've gotten to this step, we're usually fairly convinced that there is a user need. We don't yet know if there is a language solution to that need. In this step, we'll start talking about what we could do in the language to make the developer's life easier.
This step typically works with Anders writing on the whiteboard while the rest of the design group gives suggestions and feedback. When we hit on a promising approach, we'll explore it for a bit to see where it leads us, contrast it to other approaches we could take.
In this step, we're asking questions such as:
- Does this new syntax cover the important scenarios?
- Does it really make the user's life easier? (new syntax is not a panacea - each new feature adds complexity)
- Is this syntax unambiguous? Is it possible for the parser to correctly deal with this new feature in the presence of all existing features? (this is a very limiting area)
- Would this be a breaking change for existing users? (Adding new keywords are examples of breaking changes, but it's also common for new language ambiguities and/or differences in behavior to show up because of new features)
- Does this feature fit the philosophy of the language?
- How will this impact tools? Can IntelliSense deal with this?
- Is this a language feature, or would it be better done through an API, or through the IDE?
- How have other languages approached this? Has their approach been successful?
- Does this require new features in the runtime?
- Does this require work in by other languages?
- What Microsoft teams would use this? Are they on board to use it?
In a single session, we will sometimes decide to do a feature and sometimes decide to not do a feature, but more typically we'll decide that we need to discuss it more. We go away and think about it, gather feedback from the people who read our language notes and those closest to the scenario, and then revisit it in a future meeting. A lot of the iteration is around exploring all the corner cases (some of which don't come up until implementation) and making sure the feature is well defined, still does what we want it to do, and still is in the best interests of the developer.
It's not uncommon for us to change our minds on the details of a feature, or even to decide upon reflection that we need to either reduce or eliminate a feature.
If we've gotten to this point, we're fairly sure that we want to have the feature, and typically have an implementation (though not always). We use feedback with the C# Customer Council (a small group of customers who we work closely with), and the C# MVPs (we've done a little with the MVPs in the past, we'll be doing more in the future) to see what their perspective is on the feature.
This is also the point where we work with the ECMA standards body to talk about our proposal, though it's often difficult to align the schedules of the standards body with product milestones.
There may also be internal Microsoft presentations at this point, depending on the feature.
Many times the feedback is positive, but sometimes it causes us to refocus the feature, and there have been language features that we cut due to feedback at this point.
Manufacturing and Shipping
If we've gotten this far, the feature is in the language, and often implemented. The remainder of the work is to polish the implementation, finish the testing (sometimes testing is concurent with implementation, sometimes it lags a bit - it depends on the type of feature and our resource constraints), do the spec, and discuss how we'll be presenting it to customers.