Software for the Masses

Brian in Coding | 5 Comments July 10, 2003

I work on the .NET framework, writing the Windows Forms and component model APIs. One of the hardest parts of this job is trying to nail our target audience. Or audiences, as the case may be. We try to write an API for VB users, C# users and MC++ users. While there is some overlap, these are vastly different types of users. Some folks have never cracked a data structures book, while others eat assembly code for breakfast. Don't go assuming you know which one is which: I know plenty of "VB" developers who are right at home doing completely insane things in VB that many hardcore C++ developers wouldn't know how to tackle.

How do you write a single API for all types of users? Should you even try? I think the answer is definitely "yes". While some would consider Visual Basic .NET a huge increase in complexity over VB 6.0 (and it is...), the .NET framework has provided something unique for the first time in the history of a Microsoft delivered API: consistency. No longer do VB users have to wait for someone to wrap the latest API in a library or control they can use. The APIs come out of the box ready for all to use them.

But man, oh man, is there a lot of debate. People with PhDs in computer science and dozens of years of industry experience are trying to wrap their head around trying to make an API simple. After all, simple is relative; what's simple to one person may not be simple to someone else (hey, I invented the designer framework in .NET. Anyone understand it?) To compound that, often a nice simple API can be misused. One recent debate we've been having is on File.Exists. It's a handy method to tell if a file exists on disk or not. For example, let's open a file if it exists:

if (File.Exists(path)) {
    Stream s = new FileStream(path);

Simple, right? Simple yes, but incorrect code. Another process somewhere else on the computer could sneak in and delete the file between the Exists call and the opening of the file. So, you've got to add a try/catch:

if (File.Exists(path)) {
    try {
        Stream s = new FileStream(path);
    catch(IOException) {

But, if you've already added the try/catch, then you really don't need the File.Exists code any longer. The File.Exists method was added so everyday people could use the API. Most programmers won't ever think about these types of race conditions and their applications will work fine for years and years. But folks who write operating systems think about these sorts of things a lot. After all, you wouldn't want the operating system to crash if two people tried to access the same file at the same time.

So which way do you go? Offer an easy to use API that makes it easy to write incorrect code? Or write an API that is as hard to misuse as it is to use? We're tring really hard to strike a balance with the .NET Framework. One of the really exciting things about the framework is it is much easier to use than traditional Windows APIs. In most places, we have done a pretty good job of trying to steer people toward correct code without making the object model hard to use. If you make the obvious and easy path the correct path, people write better code without evening knowing it. That's the challenge we're facing now with each new version of the framework. You can bet that there will be much more debate in the hallways of Microsoft as we continue on our journey.

Comments (5) -

Frank Hileman, Friday, July 11, 2003 at 8:08 AM

Not sure you picked a good example. File.Exists may be useful for other things.

Brian, Friday, July 11, 2003 at 9:36 AM

The debate we were actually having was over this API, but I agree that there are much better examples.  Here is an example of an API that actually helps you write good code: in the early days of the collection APIs we had a ToArray method that would convert the collection to an array.  We scrubbed it, and instead went with the hard-to-use Array.Copy. Why?  Because a little analysis of the code base showed that even within our own code ToArray was rampant.  It's convenient, but it causes a copy of a collection to an array which is very expensive.  Array.Copy requires the developer to realize what he or she is doing, and is much more explicit.  Some may criticize this decision but the net result of it is that without them realizing it, their code is more efficient.

Frank Hileman, Saturday, August 23, 2003 at 10:25 AM

Sorry to restart this so late. I was thinking over your comments and something really bothered me. In both examples, File.Exists, and ToArray, it seems the solution (obscurity) only addresses the symptom and not the underlying problem. It is as if a doctor treated a serious infection with a pain-killer. For example, you noticed ToArray being used often, and performance impacts from that. The underlying problem is the fact that people prefered ToArray to something else. So the solution is to make the "something else," presumably using the collection directly, easier to use, thus preferable to users. The underlying problem was the inconvenience of not using ToArray, not the use of ToArray. By hiding ToArray you alleviate the symptoms but not the problem, and you frustrate developers who are trying to find ToArray functionality.

The same argument can be made about File.Exists. The problem is the lack of a File.OpenIfExists function: if that were present, well-documented, and used in example code, people would prefer that to the clumsier combination of two calls, and the specific File.Exists problem would never appear, without having to hide the logical File.Exists in an obscure location.

These two examples are not enough to bother me. But I have noticed other similar "hiding" techniques that do bother me tremendously, and probably frustrate the hell out of anyone who is simply trying to find a function or property in the logical location in the documentation. Sometimes, as well, the hiding causes performance impacts.

Consider that some methods were turned into explicit interface implementaions merely to hide them in the documentation. To me this is absurd. If there is a problem in the documentation, it should be fixed in the documentation -- have an "advanced" view, or group members together. For example, all the IConvertible explicit interface implementations in the basic value types. Because calling an interface function on a value type requires boxing, leaving these functions as non-explicit interface implementations would have been much better. To fix the documentation, performance was damaged permanently. But worse, no one can even tell, by looking at the "Int32 Members" page in the documentation, that these functions exist for Int32. Without a single page listing the methods, we tend to use tools like Reflector.

There is no conflict between a logical api and an easy-to-use api. They are reinforcing, not conflicting. The easy-to-use api can accomplish much with little code. But this design stinks if the user cannot then jump into advanced solutions in a logical way -- without having to do google searches constantly on all the forums.

By the way, I do understand the designer framework, but it took me a long time to grasp it all. The only big problem I found was the documentation. At first there was only reference documentation -- probably the worst possible way to learn such an API. Subsequent articles, especially ShapeDesigner, helped me tremendously. I think the main thing missing was (and still is to some degree) a sort of step by step set of articles on each topic. Starting with typeconverters, then uitypeeditors, then designers, then each are of the root designer features. The ShapeDesigner article is a big pill to swallow. And there are common root designer issues missing from that. A base class for the root designer might have helped.

Brian, Sunday, August 24, 2003 at 2:31 PM

I agree with your points above.  Code hiding should never be used to clear up documentation.  I think there is a grey line here, though.  Int32 is an excellent example of a case where we probably hid the interface to make the class itself "clean" to the detriment of the development community.  Luckily, we can fix that by adding the methods back onto the the classes.  But there are other cases where I think it does make sense.  For example, a read-only collection that wants to implement IList would do well not to expose Add and Remove methods, since they are not implemented for read only collections.
In many cases you will find that we take the timid route and underexpose APIs, so we can do a better job in the future without introducing a breaking change.  For example, we're already working on ways to make collection enumeration more efficient  so there will be little value in passing arrays.  We'll also looking at the file APIs.  As more people use the framework we are learning a ton about how to improve it.

Cleve Littlefield, Sunday, February 1, 2004 at 9:19 PM

I know this is pretty late, but I had to chime in, especially about the designers.  I agree the designers were hard to learn, but they are very well designed I thought.  The problem is like Frank said, there was no real documentation except on just what classes where laying around, not hey use this class for this and that class for that.  There was no GotDotNet quick start article on how get started, and really no information on any other site like code project or dotnet247 either.
Now if you wanted to do anything more advanced, say like design your own designer for your own app either separate from Visual Studio or together), that was near impossible.  There was no documentation at all there.  Obviously the information was out there, as from time to time you would see custom addin that implemented full designers and then finally someone wrote a full standalone designer and released the source, but why wasnt this available from MS?
It seems that someone at MS decided few people would use your very nice designer architecture and decided to not give it proper attention.  So basically my point is, is if you cant make it simple, or it isnt really for the masses, dont overlook it and not write the documentation and samples that help the gurus code the cool stuff that shows off your great platform...
Comments are closed