Sunday, July 18, 2010

Rainier Summit via Kautz Ice Chute

Late last December, my pastor, Charlie Swartz, pulled me aside and asked if I’d be interested in climbing Rainier with him.  I’d tried twice before, succeeding once, but I didn’t feel comfortable enough with my skills to lead a team myself.  Consequently, I asked my cousin Brian (who has 10 Rainier summits and an Everest summit on his resume) if he’d be interested in leading the team.  He was, and we started planning.

The only route that I’d done before was the Schurman/Emmonds approach on the other side of the mountain.  This time, however, my cousin had talked us into trying something a little more challenging: the Kautz Ice Chute, a couple miles climber’s left of the standard Disappointment Cleaver route.  Gauthier’s book describes it as a Grade II/III, basically because of the difficulty of the ice chute, which has a one 50 degree pitch, and a second 60 degree pitch.  That seemed challenging for a bunch of newbies, but folks liked the idea, so we went with it.

Over the next six months, probably two dozen people “joined” our team and then backed out, but when we finally left the Paradise parking lot on Wednesday morning, we had 11 folks hoofing it up towards Pan Point.

IMGP3729[1]

Unfortunately, the two most interesting members of our party had to back out first.  Lhakpa Sherpa has five Everest summits under his belt, but his wife Maya wasn’t feeling good at elevation, so they peeled off after crossing the Nisqually Glacier, and headed back to Seattle, which left nine of us heading up out of the Nisqually and up onto Wapowety Cleaver.

IMGP3756[1]

IMGP3771[1]

Once we were on Wapowety Cleaver, we crossed over the Wilson Glacier then began working our way towards the base of the Turtle.

IMGP3780[1]

What with one delay or another, along with the logistics of keeping a large group moving, we ended up getting to camp later than we had hoped, about 7:00 pm.  We setup camp at about 9500 feet, right around the base of the Turtle, ate dinner, watched the sunset, and then headed to bed.

IMGP3815[1]

IMGP3820[1]

IMGP3827[1]

Our original plan had been to take Thursday as a rest day, and then summit and descend on Friday.  However, most folks were feeling fairly good, so we decided to try for a 3:00 am start on Thursday morning.  The result was that I got four hours sleep, and pretty much everyone else got less – several folks didn’t get any, and two members of our team were feeling too exhausted to try for the summit.

IMGP3833[1]

The seven remaining members of our team made it to the top of the Turtle just as the sun was coming up.  We roped up, adjusted our crampons, and headed onto the Kautz ice chute.

IMGP3839[2]

IMGP3841[1]

IMGP3849[1]

Luckily for us, the route was in excellent shape.  Usually, by mid-July, the Kautz is more ice than snow, but we still had a great snowpack, so even the two steep pitches didn’t give us much trouble going up.  We eventually made it up off the Kautz, and at about 13,000 feet crossed over to the Nisqually.

Unfortunately, this is about when one of our party began to suffer some of the symptoms of AMS.  Stephanie Spence was an experienced mountaineer, and had climbed at the 14,000 foot level in the past, but this time she began throwing up at around 13,000 feet.  She insisted on continuing, but by 13,500 she was done.  Graciously, Eric Dalzell, another Everest veteran, volunteered to give up his summit and descend with her. 

IMGP3853[1]

The last 1000 feet were brutal.  It was a step, and then two breaths, and then another step, and another two breaths, the entire way.  Anne Timblin declared that this was significantly harder than the half-ironman she’d done the year, and that she should have turned back.  But she kept going, and so did the rest of us.

Finally, a little after noon, we crossed over the summit rocks and stood on the summit crater. 

IMGP3859[1]

IMGP3867[1]

IMGP3877[1]

The weather was still perfectly clear, though the winds were beginning to pick up.  Several of us hunkered down in the rocks near the steam caves and took long naps.

IMGP3862[1]

After an hour and a half or so on the summit, we began descending, which was a long, slow process.

IMGP3886[1]

The trickiest part came while descending the top pitch of the Kautz Ice Chute.  The snow was pretty soft, which made for good down climbing, except for a 10 yard stretch of ice.  My cousin Brian had four of us on belay as we were descending the chute, so we were fairly well protected, but we were still trying to be careful.  I got to the icy stretch, and between general fatigue, nervousness, and inexperience, I couldn’t keep my crampons in the ice.  Unfortunately, although I had slowed down, the climber above me hadn’t, so there was a significant amount of slack in the rope when my crampons finally popped out and I went for a tumble down the slope.  I yelled “Falling!!”, and luckily the folks above me had enough time to go into self arrest.  I ended up sliding some 20-30 feet, and gave the rope a good yank when I finally hit the end, but they held on, which was good, as I didn’t have any desire to test the strength of the pickets up at our belay station.

IMGP3890[1]

After we’d reached another belay station towards the bottom of the pitch, we unroped, and then I belayed my cousin down.  The belay was mostly just for appearances, as we hadn’t placed any protection on the way down (Eric was carrying our extra pickets when he left to accompany Stephanie down the mountain): if Brian had fallen, he would have swept past us and then to the end of the rope before we could have stopped him.  Luckily, he was able to bypass the icy patch, and made it safely down to our belay station. 

The only other interesting part of our descent came a couple hundred feet further down the chute, towards the bottom of the lower pitch.  I was kickstepping my way down the chute, facing into the slope, when I felt the snow give way under me.  I went into self-arrest, but not before I found my legs and chest dangling inside a large crevasse.  I elbowed my way out – style points didn’t seem important at that point – and then our team carefully navigated around the crevasse, and we continued our descent to camp without any further incidents (other than a nice glissade down the Turtle).

IMGP3902[1]

The next day, we made ourselves breakfast, then headed out. 

IMGP3933[1]

The snow was soft enough that we were able to glissade maybe 75% of our way down to the Nisqually.

IMGP3956[1]

IMGP3966[1]

And damn, it felt good to get back to the parking lot.

IMGP3998[1]

I’ve posted the full assortment of the pictures I took here.  Anne’s pictures are on Facebook here.

Some lessons I learned:

  1. If we ever do a newbie trip again, we’ll plan for four full days on the mountain.  If we’d had a rest day between our ascent to high camp and our summit day, probably all nine remaining members of our party would have been able to summit, instead of just five of us.
  2. Folks without ice climbing experience have no business being out on the Kautz Ice Chute.  We got lucky in that the ratio of snow to ice on the Kautz was roughly 20:1, but I get the impression that’s not at all typical for this time of year.  If it had been a normal year, we would have struggled a great deal more.  Before I do that route again, I need to get some real experience on hard ice.
  3. Training with a heavy pack at altitude is the best conditioning, and I hadn’t done enough of it.  I’ve been training regularly and hard for six months, and I’ve made trips up to both Muir and Schurman this season, but I could easily have used another two 10,000+ climbs before trying for the summit.
  4. Large groups travel slowly.  A team of 12 people is just too large, and moves, well, glacially.  Next time, I won’t go with a group larger than six people.
  5. I need a smaller camera than my DSLR to take on these trips.  Not only was my Pentax K10D heavy (I didn’t need the extra two pounds), but it’s awkward, with the result that I had to leave it in my pack most of the time.  It doesn’t matter that an SLR takes better photographs if I can’t get the picture because I can’t get at the camera.
  6. Once again, I was carrying too much: my pack weighed nearly 80 pounds out of the parking lot.  Options for dropping weight include switching over to a jet boil stove, taking less food (I had ~10 pounds left over), and carrying less water (see #7).
  7. I need to figure out how to drink less water.  During our two climbing days, I drank probably twice as much water as anyone else – nearly six liters on each of our climbing days.  I sweat a lot, but still, this seems excessive.  My suspicion is that I was probably suffering from hyponatremia, and that some Gatorade mix, or even just some Nuun tablets, would have made a significant difference in the amount of water I felt I needed.
  8. The Asolo AFS Evoluzione climbing boots that I rented from REI were worthless.  They have no flex in them whatsoever, and while they might be good for technical ice climbing, they’re vastly inappropriate for general-purpose mountaineering.  Specifically, they’re nowhere near as good as the Koflachs that REI used to rent.  It was like climbing in plastic ski boots: I ended the climb with large painful blisters and bruises on the front of both shins, and numerous places elsewhere on my feet.  If the Asolo’s are still what REI has available the next time I head up a peak, I’ll either have to bite the bullet and buy my own boots, or rent elsewhere.

Monday, June 14, 2010

Major Problem and Major Progress

The Major Problem

Brendan has been very excited about pictures lately, especially when they're hanging on the wall.  Last week, when Galena’s parents, John and Sue, were up visiting, John built an oversized picture frame for us that we could place at toddler eye-level. 

This morning, during a temporary lull in the insanity, Galena and I decided to mount that picture frame.  It was big enough that I figured we should attach it to the studs in the wall, so I used my handy-dandy stud finder to locate the first stud, and with the help of four toddler hands, managed to pound in the first nail.  While I was looking for the next stud, Galena asked, "What's that noise?"  With three kids under three, there's a lot of strange noises in our house, and I was preoccupied with keeping Brendan's hands off the stud-finder, so I didn't pay much attention to her question.  But she repeated it several times, and then suddenly yelled, "Ken, we've got a major problem.  There's water coming out of the wall!"

And there was.  From right behind the frame, about where I'd put the first nail, there was a small but steady stream of water dripping down onto the floor.  I realized almost immediately what had happened: I must have pounded the nail through a pipe in the wall.  Not knowing what else to do, and wanting to get a better look at the source of the leak, I pulled the frame away from the wall, pulling the nail out along with it.  That was when the real fun started, as a spray of water emerged from the wall powerful enough to drench the other side of the kitchen.

I don't remember everything that got said in the excitement that followed, but suffice to say that as I was running around trying to find the water main, the stream turned too hot for Galena to block with her bare hands.  She temporarily abandoned her attempt to contain the spray, and scooped Brendan up to get him out of the way of the increasingly hot water.  (Brendan exhibited extreme consternation at being manhandled in this way: he'd been enjoying himself tremendously.)  Caedmon, meanwhile, had run into the family room and was crouched out of the way of the spray, yelling excitedly, "Major problem! Major problem! MAJOR PROBLEM!"  Galena grabbed my Goretex jacket from the back of a chair, and with that in front of her, waded back into the scalding stream of water.  As this was happening, I was busy running in and out of the house in my bare feet, looking for pliers, and checking on results, as I first managed to turn off the gas to the entire house, and then the gas to the hot water heater, neither of which was as helpful in this situation as you might think.  Finally I found the right valve, and I heard the gushing from inside the house subside.

The plumber will supposedly be here sometime this afternoon.  I'm guessing if we're lucky, this will only cost us $1000.  In the meantime, the floor is covered with towels soaking up the water, and Caedmon has asked for the "major problem" to be re-explained to him at least 50 times.  "Why major problem, Mommy?"  It's a question I'd like to know the answer to myself.

The Major Progress

This is almost anticlimactic, but Brendan took his first consecutive steps today.  After we'd managed to get all the water soaked up, we fed the kids lunch.  Since the water is still turned off, I wiped Brendan down afterwards with a washcloth, and set him down on the floor.  Without even thinking, he took five steps in a row over to a chair and grabbed onto it.  Galena said, "Brendan, that's major progress!"  He looked up at us with a huge smile on his face: he knew he'd just accomplished something worthwhile.  If only the same could be said for my morning . . . :-(

Sunday, November 8, 2009

Bedtime Routine

Back at the beginning of September, Caedmon finally figured out how to climb out of his crib, and how to turn the light on in his room.  We tried very hard for several nights to get him to stay in bed on his own, but we eventually gave up.  Ever since, every night, our routine has been like this:

  • Give him a bath and brush his teeth.
  • Read three stories together.
  • Give him half an hour to play quietly in his room.
  • Tuck him in, pray together, and turn off the light.
  • Wait outside the room (at most 30 seconds) before he's up and turning on the light or opening the door.
  • Remove the lightbulb, and lock his door.
  • Wait for the screaming to die down.

For the last week or so, we've been talking about trying something different – partly because we're tired of the fight, but also because we don't like having to resort to a physical restraint, like a lock.  We'd rather that he be able to control himself, well, by himself.

So this afternoon, we explained to Caedmon that we'd be doing something different tonight.  We had four new bedtime rules: (1) Stay in bed; (2) close your eyes; (3) stay quiet; (4) put your head on your pillow.  We gave him hand motions for each of the rules, and rehearsed them with him repeatedly throughout the afternoon and evening.  We also explained that if he got up out of bed, we would immediately put him back to bed, without looking at him, and without saying anything.

So Galena drew the short straw tonight.  Everything went well, up to the point where she turned off the light, left the room and closed the door. 

At about the half hour mark, I came up to see how things were going.  She was standing outside the door with her teeth clenched.  In-between missions, she said, “Thirty-three.”  The door opened again, she disappeared inside, then re-emerged.  “Thirty-four.” The door opened again, and she disappeared once more. “Thirty-five,” she said when she came out.

I came back about half an hour later.  She was still standing outside the door, teeth still clenched, but she had removed her sweater and her arms were bare.  “One hundred thirty.”  “One hundred thirty-one.”  “One hundred thirty-two.”

Somewhere around 150, I could hear Caedmon's giggles switch to crying.

I popped my head into the hallway a bit later.  “One hundred sixty-two,” she said, but there was triumph in her eyes.  From within his room, I could hear Caedmon screaming, “No, Mom! No! Go away! Daddy! Daaaaddy!"  The door opened again, and in she went.

Caedmon is now asleep.  Galena had to put him back in bed 169 times before he finally stayed.

It's my night tomorrow.  Pray for me.

Thursday, October 1, 2009

Really Missing Serialization Callbacks

I just ran into another feature whose absence from Silverlight is sorely missed.

I’m using the WCF generated proxy classes as the basis for binding to some UI objects.  If you’re using a full MVVM pattern, the way that you’d normally do this is wrap the proxy-generated classes with a ViewModel, so that instead of binding to, say, the User class generated by “Add Service Reference”, you’d bind to a UserViewModel class that acts as a facade for the User class.  So far, I haven’t been willing to go that route.  The only point to having a ViewModel is if you’re making extensive use of databinding, and databinding happens to be my least favorite Silverlight technology, for reasons that I’ve explained elsewhere.  And if I’m only doing databinding occasionally, it seems like a lot of more-or-less pointless work to recreate a facade for my complete object model on the client, and then keep it synchronized with the classes that the Entity Framework has already helpfully generated for me.  So I’ve been making do with partial classes to add any additional properties or methods that seem appropriate.

But as I said, I ran into a problem today.  One of my classes, SharedFile, has a bindable property called StatusText whose value depends on a complicated graph of other object properties; and to get them all working, I’ve had to string together an unpleasant chain of INotifyPropertyChanged notifications, sorta like so (this is just one of numerous chained handlers):

   1: void uploadCommand_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
   2: {
   3:     if (e.PropertyName == "State")
   4:     {
   5:         UpdateStatusText();
   6:     }
   7: }

OK, so far so good – but I have to wire up these event handlers somewhere.  Since the definition of SharedFile in reference.cs didn’t define a default constructor, I thought it would be simple to create one in my partial class, and that would be the end of it:

   1: public SharedFile()
   2: {
   3:     this.PropertyChanged += SharedFile_PropertyChanged;
   4: }

But although the objects get created, that constructor never gets called.  WTF?  Well, it turns out that when Silverlight deserializes the XML from my WCF service, it uses FormatterServices.GetUninitializedObject to create the object, which skips calling the constructor.  I guess that makes a certain sort of sense – the WCF service is handing you an object that, in theory, is already constructed, so you shouldn’t need to call the constructor again.  I get that.  But then where do I put this code?

OK, I know, I can put that code in a method that I tag with the [OnDeserialized] attribute.  That’s the normal way of doing it, right?  Oh – except Silverlight doesn’t support serialization callbacks.

Huh?

I get that some features need to be left out of Silverlight.  But this seems like a really odd one.  Serializing and deserializing objects is what you do in Silverlight.  You can write real-world WPF or WinForm or ASP.NET applications all day long and never once have to deal with object serialization and deserialization.  But you can’t use Silverlight for five minutes without needing to touch an object that’s just been deserialized from some web service.  So why choose that particular set of features to cut?  It sure seems like a bizarre design choice.

At any rate, my choices were either to implement a full-blown ViewModel layer, which I really don’t want to do, or write a hack of some sort to initialize the event handlers manually.  Uggh.

What I’ve done for now is to throw an Initialize() method on the containing object (User), which in turn initializes any object that gets added to its SharedFiles ObservableCollection:

   1: // This is necessary because Silverlight doesn't call a constructor when deserializing classes,
   2: // and also doesn't support on the [OnDeserialized] attribute.  Damn annoying.
   3: public void Initialize()
   4: {
   5:     InitializeSharedFileList(this.SharedFiles);
   6:     SharedFiles.CollectionChanged += SharedFiles_CollectionChanged;
   7: }
   8:  
   9: private void SharedFiles_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
  10: {
  11:     if (e.Action == NotifyCollectionChangedAction.Add)
  12:     {
  13:         InitializeSharedFileList(e.NewItems);
  14:     }
  15: }
  16:  
  17: private void InitializeSharedFileList(IList sharedFileList)
  18: {
  19:     foreach (SharedFile sharedFile in sharedFileList)
  20:     {
  21:         sharedFile.Initialize();
  22:     }
  23: }

And then I call User.Initialize() when I first retrieve it from the web service:

   1: private User user;
   2: public User User
   3: {
   4:     get
   5:     {
   6:         return user;
   7:     }
   8:     set
   9:     {
  10:         if (!object.ReferenceEquals(user, value))
  11:         {
  12:             user = value;
  13:             if (user != null)
  14:             {
  15:                 user.Initialize();
  16:                 UserId = user.UserId;
  17:             }
  18:             else
  19:             {
  20:                 UserId = string.Empty;
  21:             }
  22:         }
  23:     }
  24: }

Like I said, uggh.  But it works.  I just wish that MS had thought through their deserialization scenarios a little better.  I don’t like being forced into creating another abstraction layer if I don’t have to.

Friday, September 25, 2009

Silverlight duplex client limitation

I ran into an interesting (and largely irrelevant) limitation today on Silverlight's implementation of duplex web services.

I'd been using Jeff Wilcox's handy Silverlight Unit Test Framework to test the data access piece of my current Silverlight project. However, I was running into a nasty problem that was driving me nuts. Part way through every test run, my unit tests would start failing. I could usually get through something like 10 or so tests before every new WCF call would return "Not Found" (which is not really the most helpful error message Microsoft ever came up with). It didn't have anything to do with the individual tests themselves, because the error would show up after 10 tests, no matter which 10 tests they were.  I'd been working through this error for some time before I realized that the "10" number was undoubtedly significant, since that seems to be the default number of connections that WCF allows, unless you go in and bump it higher. (Now of course, IMO, that's a pretty dumb default: the obvious purpose for leaving it that low is to prevent DOS attacks -- but the net result is that instead of needing some 10,000 connections to DOS your server, you only need 10. Sigh.)

This made me think that I might be leaking a connection somewhere. In theory, I was closing all my connections in one test before moving on to another, but we all know how well that works :-).  Since I was opening and closing all my connections through the same static class, I wrote up some quick instrumentation, and saw that yeah, one connection was staying open after every test.  Some additional poking around, and I found a method that was opening a new connection and failing to close it.  Easily fixed.  Instead of this final line in my method:

EnqueueTestComplete();

I just made it do this:

EnqueueCallback(() => DataConnectionManager.TryClientClose(client,
            error => EnqueueTestComplete()));

And TryClientClose() looks something like this:
        public static void TryClientClose(RoomServiceClient client, OperationCallback callback)
        {
            if (client != null && client.State == CommunicationState.Opened)
            {
                client.CloseCompleted += (sender, e) =>
                {
                    ClientsClosed++;
                    ClientsOpen--;
                    if (e.Error != null)
                    {
                        client.Abort();
                    }
                    if (callback != null)
                    {
                        callback(e.Error);
                    }
                };
                client.CloseAsync();
            }
            else
            {
                if (callback != null)
                {
                    callback(null);
                }
            }
        }

Close enough. But why was I running into this error in the first place?  Truth be told, I don't completely know.  But my best guess is that the Silverlight client (or maybe the browser that's hosting it) has a limitation on how many duplex callback sessions it can support.  And so far as I'm aware, there's no way to increase this number.  At least, I've poked around in all the relevant blogs, and looked through the appropriate docs, and can't find anything obvious.  But the net result is that you don't want to have more than 10 duplex clients open at the same time on any given Silverlight application.

Saturday, September 19, 2009

And Three More Silverlight Complaints

Unhelpful error messages rank high on my list of complaints about Silverlight and WPF. Take this particular error message: AG_E_PARSER_BAD_TYPE. I get it pretty regularly, but this particular instance started showing up about 15 minutes ago. The location that it points to (presumably in App.xaml? – it doesn't actually say) has nothing to do with the error in question.

The error is presumably triggered by some XAML I screwed up somewhere in one of the 50+ different XAML files in my solution. I have no idea which file (I just did a big search-and-replace as part of a codebase-wide refactor). So first of all: AG_E_PARSER_BAD_TYPE? Since when does that qualify as an error message? We're not doing sockets programming here, folks. The CLR allows for an incredibly rich set of exceptions. So why the incomprehensible error message? But more to the point, why am I being pointed to the wrong location? One of the things that makes the Silverlight tools for Visual Studio feel so frustrating is that they know which file is problematic, and they know precisely what the problem is, and they refuse to tell me. Yes, I know my reaction makes Silverlight a little more anthropomorphic than necessary. But it sure as heck feels like the Microsoft tools are out to get me sometimes. (For what it's worth, after an hour of troubleshooting, I opened the solution in Blend, and for once, Blend gave me what I needed to know, and immediately highlighted the problematic file.)

My second complaint is bogus error messages, i.e., error messages when there's no error. Again, to take one example of many, in one particular form in my project, Visual Studio lists 27 different errors, all having to do with certain local controls not being found.

The assembly in question (SlideLinc.Client.dll) and the associated namespace (SlideLinc.Client) are registered correctly in the form, and indeed, the form loads and executes correctly at runtime. For once, Blend recognizes this, and doesn't raise any errors. In other words, there's no problem. But I've tried six ways from Sunday to make those errors go away in Visual Studio, and nothing has been successful. In this particular case, it's generally not that problematic – except when it comes time to track down a real error, and you have to fight your way through dozens of bogus error messages to find the right one. (It's at least a tad ironic that these bogus error messages at least show up at the right time, i.e., when the app is compiling, and are able to point me to exactly the place where they – incorrectly – think the error is occurring. In contrast, the quite real AG_E_PARSER_BAD_TYPE error that I ran into above showed up way too late, at runtime, and wouldn't even tell me which file it was occurring in. It's this kind of stuff that makes you pull your hair out – and which has cost me at least a month of troubleshooting on my current project.)

My third complaint is more specific, and has to do with the Add Service Reference feature in Visual Studio. Specifically, I run into two reoccurring, distinct, but possibly related bugs when I try to update my service references. Neither of these occurred with the March 2009 SL3 CTP, and both started showing up immediately after I upgraded to SL3 RTW.

  1. Periodically (about a third of the time), when I pull open the "Configure Service Reference" dialog box, the "ObservableCollection" option for collection types isn't available; instead, there's something listed called "(Custom)", which is apparently interpreted as "Array[]". So when it generates your proxy references, it generates any list of values as an array, rather than an ObservableCollection. This breaks all your existing code, which of course was written to expect an ObservableCollection in those instances. What usually fixes it for me is simply restarting Visual Studio. Nothing else seems to work.
  2. The second problem is that periodically the Reference.cs file fails to generate – which of course also breaks all your existing code. I ran into a tip from someone, somewhere, who recommended deselecting the option "Reuse types in specified referenced assemblies", and then select only "System.Core". When I do this, and then I update my service reference, it seems to fix this pretty reliably. Sometimes it happens anyway, at which point in time I simply change which referenced assemblies it should try to reuse, and then immediately everything works again.

On this last point, I should note that lots of other folks besides me have been running into these issues. Indeed, I don't know anybody who regularly works with Silverlight and WCF who hasn't run into them. It's also been quite well-documented on the forums. I've tried to report these bugs on the MS Connect website, but I can't figure out how: I don't seem to have that option.

I should note that the Silverlight runtime itself seems to be pretty darned stable, all things considered. I've beat the thing to death with stress tests, and haven't run across any bugs worth mentioning that weren't my own damned fault. All the real problems seem to show up in the various tools you use to build Silverlight applications.

It seems to me that there are going to be some massive improvements to the .NET platform coming with the impending .NET 4.0 / Visual Studio 2010 release. I keep hoping that the reason these bugs haven't been fixed out in the wild is that MS is working so hard to improve the quality of that upcoming release.

I can hope, can't I?

Friday, September 18, 2009

Mixed Feelings about Silverlight

I spent all day yesterday at the Microsoft Silverlight FireStarter event. (You can see the decks here, and the recordings will be up shortly.) It was an appropriate place for me to be hanging out, since Alanta, my current startup, is working to build a web conferencing system on Silverlight. I came away from the day with a variety of mixed feelings.

First, I realized that not only do I have a lot to learn, but what I don't know is hurting me. If I knew the tools and frameworks better, I could make a lot faster progress on our product, and the resulting code would be better architected and more maintainable. The demo that Adam Kinney did on Blend made me realize just how crappy my own Blend skills are, and how I could be a ton more productive if I knew Blend better. Similarly, the stuff that Marco Matos did with the Silverlight Toolkit, and Karl Shifflet's demo of his XAML PowerToys.

I also felt grateful to Microsoft (in general) and to the people who put on the event (very specifically). They've pretty much dedicated their lives to creating tools and frameworks and platforms for the rest of us to use, to make our lives easier or better in some way. And when, like me, you spend more than half your waking hours working with those particular tools and frameworks and platforms, that's not something you can just take for granted. It's something to be thankful for.

At the same time, I think I spent more time at the event feeling frustrated than anything else. I've said before that you know a product is succeeding when people start complaining about it; and by that standard, Silverlight is a very successful product. Here are my top three complaints about the current state of Silverlight and its tools:

  1. Silverlight databinding is a mess. This is partly due to the fact that it is missing many of the features of its more powerful cousin, WPF databinding. But databinding in both Silverlight and WPF suffer from the same underlying problem: it's incredibly difficult to get right, very easy to get wrong, and MS provides provides no troubleshooting help at all. As I've said elsewhere (here and here), I think the biggest underlying problem with the current databinding implementation is that it's untyped, i.e., you never specify the type of the DataContext your XAML is expecting. As a result, there's no Intellisense to help you when you define your bindings, and no design-time or compile-time checking to make sure you got them right. I could probably live with this limitation if Microsoft provided better tools and frameworks for runtime troubleshooting. But at runtime, any binding errors get swallowed entirely and completely. A very few of them will show up in the Visual Studio debug window, but you have absolutely no insight into the vast majority of things that might go wrong. Even if MS thought that it was too big of a switch to move to a strongly-typed DataContext, they should at the very least, for the sake of all that is holy, have their controls raise an event when something goes wrong, so that you have some opportunity to see and respond to what might be happening. I can't express strongly enough how absolutely insane it is that we're about to see version 4 of Silverlight, and Microsoft still hasn't fixed this basic, fundamental problem. I spend enormous amounts of time troubleshooting databindings, and it's the primary reason why we've only partially adopted an MVVM architecture for Alanta. I spend enough of my time troubleshooting WCF, which is complicated and annoying, but at least it provides real errors. There's not enough time in the day to guess (and guess and guess and guess again) what might be going wrong with my databindings.

    I should note that Tim Heuer, frequently the face of Silverlight to the world, was able to sympathize with these problems yesterday. He pointed me Glimpse, a recent tool from Karl Shifflett, that provides some insight into what's happening under the hood with databinding errors. I haven't had the chance to try it out yet, but it seems like a step in the right direction. Still, "not sucking quite so horribly" doesn't equal "sufficient", least of all "good".

    Microsoft and the rest of the community is making a huge push towards MVVM as the standard development model for Silverlight and WPF. That makes sense: but its adoption by average, everyday developers will remain limited until MS is able to fix their databinding experience.

  2. Blend is a mess. Yes, it's incredibly powerful. And yes, V3 is much, much better than V2. And it goes without saying that it's better than Visual Studio for UI design. But as the premiere MS tool for designing great user experiences, it shows an astonishing lack of appreciation of what makes for a great user experience. To take just a couple examples:
    1. In the picture below, try to guess which of the two tabs ("FileRowControl" or "FileManagerControl") is selected. Is it the one on the left whose bold, dark color seems to flow uninterrupted into the workspace? Or is it the one on the right whose light gray color, everywhere else in the Windows UI, indicates "disabled"? Astonishingly, it's the tab on the right that's selected. I've been working with Blend for months now, and every time I have to look at the open file tabs, I have to stop and think which one is actually selected. That's inexcusable for a company with as many design resources as Microsoft.
       
    2. Here's a fairly standard property panel in Blend:
       
      Now, somewhere in this panel is a way to set these properties in an advanced fashion, i.e., through databinding or other similar techniques. Care to hazard a guess as to how you do that? Might one, perhaps, right-click and see some options for doing so? Nope, no right-click menu is available. Give up? Well, I'm astonished that you missed the tiny little two-pixel squares to the right of every property. It turns out that if you navigate your mouse over one, and after two or three tries manage to click on it successfully, it pops up this context menu:
       
      This is such a non-standard user experience that there isn't even a name for it. (Adam Kinney has suggested calling it a Property Peg.) But there's a darned good reason there isn't a name for it. It's because it's a bad idea! It's a horrible UX design. How more completely unintuitive and non-discoverable is it possible to get? (That's apart from the fact that because the, umm, peg is so small, even once you figure out what it's there for, it's still hard to use.)

    3. In every other Windows application, scrolling your mouse wheel moves the selected document up or down. In Blend, scrolling your mouse wheel sets the zoom level on your current workspace. That wouldn't be so bad if it made a better guess about where you were aiming. For instance, here's what my screen looks like after I open up a specific user control I've been working on lately:
       
      And here's my screen after four "zoom in" mouse-wheel movements:

       
      Rather than zooming in on anything that I might be interested in (say, the selected object), Blend has helpfully chosen to move my control entirely off the screen. And what's worse, it seems bloody well determined to be random about the direction it moves it. Sometimes it moves it off my screen up and to the right, sometimes down and to the left, and occasionally, very occasionally, it decides to actually zoom in on the thing I'm interested in. Taken all together, though, it's as if Blend is determined to take my mouse wheel, one of the very best usability inventions ever, and make it entirely useless.

      I could go on for quite a while about Blend's failings: these are only a couple of random examples. But they should be sufficient to demonstrate that Blend desperately needs some UX TLC.

  3. Silverlight is still missing some very basic features. The one that's usually brought up is the lack of printing support, which is laughable, if you think about it very long at all. Given the project I'm currently working on (web conferencing), the feature that I miss the most is obviously web camera and microphone support. Flash has supported web cameras and microphones for years, so it was very disappointing to me that this feature got cut from Silverlight 3. From the knowing and slightly embarrassed looks that I see on Microsoft faces whenever the subject gets brought up, I'm pretty sure that it'll make it into Silverlight 4. But still. Of the six months that I've spent working on Alanta, at least a month out of that time has gone to troubleshooting the clever but ultimately painful Flash hack that we're currently using.

I feel the need to repeat what I said earlier: you know a product is being successful when people start complaining about it. They complain – I complain – because I can see the potential for the product, and get frustrated because it's not there yet. I'm also a developer, and know how hard it is to get these things right. The folks at MS are only human. But I also hope that they're listening.