Learning a New Tool

economics, psychology, technology, work 1 Comment

My old college buddy asks:

When you are trying out a new development tool, what do you look for to help you learn how to effectively use the tool? Is it help files, tutorials, white papers, samples, case studies, etc? Or do you learn best by participating in classes or through mentoring? Perhaps you only try to learn tools that are easily assimilated, and if so, what makes one tool easier to learn than another?

There’s a saying in the field of User Interface Design (computer and otherwise) that goes “there should only ever be one button: one that does exactly what the user wants.” Of course, this is hyperbole, but it does illustrate the theme of UI design: make the tool as easy to use / simple / natural as possible. A more usable product is the one that the user needs the least amount of thought to use and the least amount of initial training.

Accomplishing this is incredibly difficult, which is part of the reason why most user interfaces are absolutely horrible (the other is that most engineers don’t study usability, especially usability for mass audiences). Fortunately, this is getting better: Apple has made UI a sellable feature, and User Interface Design is now a bona fide field of research that gets attention from the builders (if you’re interested, you can start with Neilsen, Norman, and Tog, the current gurus of usability.

Now, to answer Graham’s question:

what do you look for to help you learn how to effectively use the tool?

Here’s my order of preference:

  1. The tool should follow some natural metaphor, if possible. Ideally, the tool should behave as if it is an extension of my body / mind. This way, there’s no learning curve; you already know how to use it. Unfortunately natural metaphors are hard to come by in the decidedly un-natural world of technology, so most of the time this isn’t available. Still, I think it should be said.
  2. If tool can’t follow a natural metaphor, then it should follow a familiar one. That is, it should try to duplicate one that already exists. This way there’s zero learning curve for users who already know the preexisting metaphor. There’s two big catches to this approach though:
    1. The old metaphor may not be terribly good to start with. Garbage in usually means garbage out.
    2. The old metaphor may not translate well to the new medium. QuickTime 4.0 is the poster child for this problem.
  3. If the tool can’t be familiar then it should be self-describing. The means of accessing the features should be apparent (in fact, blatant). Available features should be displayed (rather than hidden) at the ready. This makes the learning time efficient: you are able to learn while you actually use the tool. A good illustration of this principle is the use of text rather than graphical icons to represent features: text describes the feature far more explicitly and accurately than a (tiny) picture.
  4. If the tool can’t be (effectively) self-describing, then it should have description waiting in the wings for the initial learning period. Think of a tutorial, but one that teaches as the user uses the tool. Some modern games are great examples: every time that you encounter a new tool, feature, or technique they give you a brief explanation of how to use it, followed by some time to put it into practice. Play Half-Life or Portal with the commentary on to see the thought process behind this technique.
  5. If you can’t do an effective tutorial mode, the next best thing is to have built-in (local) context-sensitive help ready at the touch of a button. There are three important factors to application help: relevance, speed, and connection to other topics (ie: lots of hyperlinks). This will help a user get out of a jam, but it may not do much to get them started in the first place.
  6. If local help isn’t available, putting your help on the Internet (say, in the form of a FAQ) is almost as good as local help, although it’s not available if you’re disconnected (ie: on a plane). Internet help also lets you enhance help post-launch and get feedback/usage stats. If you get a good community behind the tool, they can potentially help with the help (with wikis & blog posts).
  7. Examples can be useful; lots of people learn better from example than they do from a spec. The major problem with examples though is that they are necessarily of narrow focused and contrived. They may not be answering the questions that are being asked, and they certainly won’t be able to answer every question.
  8. White papers and other wordy documentation are not nearly as useful as other forms of instruction; it’s harder to find the solution to a particular problem when it’s floating in a sea of flat text. Always remember that, as a rule, people don’t read.
  9. Screencasts are appropriate for dynamic situations, where capturing the actual motion is important. Otherwise, video just becomes a very hard-to-use interface to the information being communicated; think of a book where the pages are turned at a fixed rate. Static text and pictures are better for most applications.

I’ll leave mentoring off my list entirely. I’ve never been a fan of (nor had much experience with) mentoring, because:

  • I’ve always had a do-it-myself (and discover-it-myself) attitude.
  • I’m often learning at the (b)leading edge of things; mentors with prior experience aren’t always easy to find.
  • Likewise, people with more experience are often too busy to spend a lot of time mentoring. They’re adding more value by operating, especially if I’m able to learn effectively without their help (which, in turn, makes me more valuable too).
  • I’m more anti-social than most. For work purposes at least, personal interaction is a means to an end, not an end in itself.

These don’t apply to many (most?) other people though, so they’re not a criticism of mentoring itself. Many people appreciate mentoring and find it valuable.

Lastly:

Perhaps you only try to learn tools that are easily assimilated

I certainly prefer easily-assimilated tools. A small learning curve makes the tool more efficient, which is half of the value equation. The other half is effectiveness, and that’s where poor tools can find their niche. If there’s no other tool that can do the work of one with a crappy interface / steep learning curve, then there’s not much choice in the matter; I’ll have to bite the bullet and learn / use it. But I’ll always be looking for a way out.

.NET 3.5: Properties and Delegates: A New Solution to an Old Problem

programming 3 Comments

A while ago I ranted about how .NET properties didn’t play nice with delegates. The gist was that, if you had a property and a method which took a delegate of the appropriate type, you had to write your own new delegate method, even though the property’s getter and setter would ordinarily qualify. Here’s an example:

public class Foo {
  public String Bar { get; set; }
}

public delegate String GetString();
public void display(GetString getString){
  Console.WriteLine(getString());
}

public void performOnAValue(Action <String> action){
  action("A Value");
}

Foo foo = new Foo { Bar = "bar" };
// You can't do this, because .NET doesn't recognize
// Foo.Bar.get() and Foo.Bar.set() as valid methods
// performOnAValue(foo.Bar);    // foo.Bar.set() satisfies Action<String>
// display(Foo.Bar);            // foo.Bar.get() satisfies GetString

// Instead you have to do this in .NET 2.0:
performOnAValue(delegate(String value) {
  foo.Bar = value;
});
display(delegate() {
  return foo.Bar;
});

Unfortunately, .NET 3.5 doesn’t fix this. However, it does give us enough new features that we can fake it. Consider this:

public static class Getter {
  public static T get<T>(this T target) {
    return target;
  }
}

This extension method adds a get() method to every type. That means that we can do this:

display(foo.Bar.get<String>);

Even though Getter.get() doesn’t satisfy the GetString() signature (since it takes a <T>), .NET is smart enough to figure out that if you call it as an extension it will work.

The bad news is that you do have to specify the type of the object as the generic type parameter of get(). You could write a non-generic version that gets and returns an Object, but that leads to the potential for InvalidCastExceptions. The good news is that if you do use the wrong generic type parameter (let’s say foo.Bar.get<int>), the compiler will complain (albeit with a confusing error message).

More bad news: there’s no equivalent extension method for the setter. Without resorting to reflection, there’s no way (that I am aware of) to programmatically access an arbitrary property of an object from within a method. However, lambda expressions make the pain of writing a delegate a little easier to bear:

performOnAValue(value => foo.Bar = value);

You can also do the same for the getter, if you use an empty input parameter list and an inferred return keyword:

display( () => foo.Bar );

I’m not sure whether the lambda or the extension method getter is less ugly.

With these options, I’d say that the Stupid .NET Trick of properties and delegates is about 75% solved. The syntax is not as clean as it could be, but it’s a big step forward… possibly enough to make properties worth it again.

.NET 3.5: Lambda Expressions, Type Inference, and Anonymous Methods

programming No Comments

As of v2.0, .NET already had support for first-class function types through delegates. v3.5 adds in a new concise syntax for creating short delegates.

public void performOnAllMembers(Action<Foo> action) {
  foreach( Foo foo in datastructure ) {
    action(foo);
  }
}

// Old syntax
performOnAllMembers(delegate(Foo foo) {
  foo.bar();
});

// New Lambda Syntax
performOnAllMembers( foo => foo.bar() );

Besides shortening “delegate” to “=>”, most of the keyboard savings here comes from type inference; you no longer have to declare that “foo” is a “Foo” since performOnAllMembers() already declares that it needs an Action to perform on a Foo. Note that this is just an assumption in the source code; everything is still strongly typed and the compiler will complain if you pass anything that’s not compatible with “Foo”.

Too Much Type Hiding?

While the source code does look cleaner, I generally believe explicitness is a virtue, and type inference works against that. If you don’t happen to have the method signature/documentation/source code for performOnAllMembers() handy, then you might be left wondering exactly of what type foo is and what it’s capable of. That means that you have to interrupt your flow of reading the source to go and look up the performOnAllMembers() contract; this interruption can only hurt your ability to understand what’s going on. The relaxed-typing proponents claim that this isn’t a big deal; I haven’t had a lot of experience with non-strong typing so I’m cautiously withholding judgment.

Anonymous Types

.NET 3.5 takes type inference one step further with anonymous types. These are similar to the Java anonymous inner types, except with a better syntax:

var myReference = new { Foo = "foo", Bar = 1 };

myReference points a instance with a strong type, and behaves as such; the type name is just hidden. Combined with the new property initialization syntax, .NET comes very close to having tuples. However, there’s a big catch with anonymous types: they’re only valid as local variables. That means you can’t pass them as parameters, get them as return values, or use them as generic type parameters (which means they can’t be stored in generic collections except as Objects). Moreover it doesn’t appear that they can ever be given methods; they’re basically local, dynamically generated structs (and I mean C dumb structs, not C# wannabe-classes structs).

There’s also something to do with lambda expressions called “Expression Trees”. I haven’t investigated this in detail yet, but it looks promising. Scott Guthrie has more to say on it. I’ll probably post more once I read up on it.

.NET 3.5: Extension Methods

programming 1 Comment

Extension methods are static methods that, through the magic of the compiler and Visual Studio’s IntelliSense, can be made to appear as if they’re actually members of a particular type. The key is that the target type can be any type, including the standard .NET types. Thus, if you want to add features to a 3rd-party class, you don’t have to resort to Booch utilities.

public static class MyExtension {
  public static String prefixWith(this String target, String prefix ){
    return prefix + target;
  }
}

String result = "foo".prefixWith("bar");

This is a decent feature, although I have two concerns:

  1. There’s potential for developer confusion, especially with developers reading unfamiliar code. “prefixWith()” isn’t a standard known method of String so you’re going to be thrown for a bit of a loop when you see it. Ordinary static methods at least have a big class name attached to them so it’s obvious when you’re using nonstandard code.
  2. I’m yet not sure how extension methods avoid name collision, but it’s bound to become an issue. All you have to do to attach an extension method on potentially every type in the system is to import the namespace of a class that defines the extension method. That can get out of hand quickly.

Lastly, there’s one cool feature with extension methods: since they’re just syntactic sugar for a static method, they don’t require an actual instance in order to “use them on a reference”. So I could have:

public static class MyExtension {
  public static bool isNull(Object o ){
    return o == null;
  }
}

String foo = null;
return foo.isNull();

By the way, that sound you hear is the Ruby enthusiasts snickering.

.NET 3.5: Property Enhancements

programming 2 Comments

.NET 3.5 adds a few syntax enhancements regarding properties.

Automatic Properties

You no longer have to declare the private field, getter, or setter methods when creating a property; the compiler will handle it for you.

class Foo {
  public String Bar { get; set; }
}

When I first started learning .NET, I actually thought that this feature had already been built into the language, and was surprised when I found I had to write my own (repetitive) property code. Automatic properties finally alleviates that.

Note: You can only declare read-write automatic properties. If you want a read-only or a write-only property, you have to do the legwork yourself. Note that access modifiers can still be used, so you could have a public property with a private setter — but that’s not the same thing as read-only.

Property Initialization

There’s a new syntax for assigning values to properties during object creation:

Foo foo = new Foo { Bar = "bar", Baz = 1 };

If you have mutable objects that don’t have any constructor logic, then this enhancement helps reduce a lot of verbose dereferencing code with a nice constructor-like syntax. Generally I prefer my classes immutable and constructor-populated, but there’s lots of times where that’s not feasible.

This makes two worthwhile enhancements to properties. Unfortunately, by themselves they wouldn’t do me much good: I intentionally avoid using properties (in favor of explicit getter/setter methods) because properties are completely unusable with delegates. However, some of the other new .NET features help to compensate for this, so I’m still counting these features as a win.

.NET 3.5

programming 1 Comment

The latest version of the .NET Framework, v3.5, was released yesterday. As usual, there’s a bunch of new libraries and Visual Studio enhancements (particularly when it comes to web applications). Those are nice, but what really caught my attention was the core language enhancements, which are pretty significant.

Scott Guthrie is one of the head honchos in that particular neighborhood of Microsoft. His blog talks about the new language features in pretty good detail, so I’m going to keep the descriptions to a minimum and focus more on commentary.

This post got pretty long so I decided to split it up:

Stupid .NET Tricks #9

programming No Comments

The HTMLSelect.Name is always the same as its ID/UniqueID properties. The documentation states:

Use the Name property to determine the unique identifier name for an HtmlSelect control. In this implementation, the get accessor returns the value of the Control.UniqueID property. However, the set accessor does not assign a value to this property.

The set accessor does not assign a value to this property because the Name property must have the same value as the Control.UniqueID property for the HtmlSelect control to work properly.

Thus, you can’t output an HTML <select> element that has different values for the “name” property and “id” attributes… unless you re-implement the code to do so yourself.

This isn’t the case for most other controls in the System.Web.UI.HtmlControls namespace. There’s no stated reason why the restriction is so necessary either. You have to really wonder WTF is going on over at Microsoft for a team to code in a seemingly arbitrary restriction and document it but not fix the problem in the first place.

Stupid .NET Tricks #8

programming 2 Comments

ASP.NET, in its grand paternal wisdom, replaces the “id” attribute on any server-side-enabled control with a dynamically generated (and thus guaranteed to be unique) value. So, when you write this:

<div id="foo"></div>

…what you actually get is something like this:

<div id="ctl00$page_content_replace$ctl00$foo"></div>

…thus dashing any hope you had of writing proper DOM-aware content (such as JavaScript and CSS).

Update: Oh yeah, and it changes the “name” attribute of input fields too, thus ensuring that your form parameters and query strings are utterly nasty.

Update 2: JonEHolland writes (below):

Use the ClientID attribute to specify ID once the control is rendered.

IE:

<div ClientId=”someId” id=”ServerSideId” runat=”server”></div>

Renders:

<div id=”someId” ></div>

I tried setting ClientId (and ClientID and clientid) and it didn’t help for several reasons:

  1. ASP.NET did not actually copy my server-side ClientID attribute to the client-side ID attribute as JonEHolland stated. The id attribute was the same generated string.
  2. It did, however, pass the same ClientID attribute and value on to the client (basically it left the unrecognized attribute alone, as it should). That could be somewhat useful, especially if you’re generating arbitrary XML. However, it doesn’t solve the core problem of the munged IDs; CSS/JavaScript won’t recognize ClientID without a lot of extra hacking.
  3. ClientID isn’t valid in the XHTML 1.0 Strict DTD, so Visual Studio complains about it. If you use any uppercase letters (such as ClientID) it actually generates an error; if you stick to lowercase (clientid) it lets you off with a warning.
  4. On the C# side: the ClientID property of Control is read-only and thus not changeable to anything. (ID is changeable but the change is ignored/overwritten by the time the control is rendered — at least as far as I’ve probed. Perhaps there’s a point in the control lifecycle where you can actually set it? Even if there is it appears to be too dicey to be worthwhile.)
  5. The ClientID property does provide me with the ID that ASP.NET has chosen for the element. If I wanted to generate CSS or JavaScript that used the element’s ID, I could use this property to discover the ID that would be used. But that’s horribly ugly and restrictive.

Sorry, JonEHolland; it doesn’t look like this works. If I’ve missed something let me know, but it appears that you’re incorrect.

Update 3: JonEHolland points to this blog post by Rick Strahl which describes how to bypass the ID munging by overriding the UniqueID and ClientID properties. That’s better than nothing, and just fine if you’re developing your own controls, but it still doesn’t solve the problem for the standard ASP.NET controls (in System.Web.UI.WebControls and System.Web.UI.HtmlControls); you’d have to create a whole bunch of new classes.

Mining Clients for Requirements

business, software 1 Comment

Ted says: Clients don’t always say what they mean. This is 100% true. My experience is that the majority of all people in business can’t fully and clearly articulate their thoughts, either verbally or in writing. Compounding this are situations where the client isn’t 100% honest and forthcoming; these run the range from simple cautiousness to outright deceit. Thus, you typically can’t take a client’s statement at face value.

Furthermore, even if a client can say what they mean, they may not mean what they actually want. And, even if they mean what they want, they may not actually want what they need. It’s the need that should be fulfilled, but there’s a lot of obstacles in the way.

To make matters worse, these obstacles are often multiplied by the numerous layers of “clients” involved. Here’s an example based off a real-world situation (the one I was in until recently):

  1. The implementor gets requirements from his immediate boss.
  2. The boss gets the requirements from the business/marketing/client relations department.
  3. The client relations department gets their requirements from a 3rd-party development company (who is subcontracting part of the work to the implementor’s company)
  4. The contracting development company gets their requirements from the actual client.
  5. The client gets their requirements from the users.

It’s easy to see how confusion and misinformation can affect the requirements when there’s this many stages between the producer and the consumer of a piece of software. That’s why it’s important to do a thorough review of a particular request to verify that the sights haven’t drifted too far off the target.

I like mining as metaphor. There’s a nugget of gold buried in a mountain of communication. You need to dig away all of the useless rock in order to find it. As you dig, you’d better be setting up struts of solid reasoning to keep it from collapsing on you… and you’d better be digging in the right direction, or else you might miss the prize entirely.

Refining Problems and Goals

business, software 2 Comments

Bruce responds to my prior post on requirements:

…I think you also have misinterpreted the user’s statement of the requirement.

Ted’s discarded first sentence IS a problem statement, but only for the second sentence (password confirmation field).

I don’t think the last sentence is a goal for the two requirements, in fact it has got nothing to do with problem statement. Interpreting it as the goal causes your question 5 to yield a negative answer.

The last sentence is (I think) a statement of a second problem, which leads to the second (but already stated) requirement. I would interpret the paragraph (briefly) as…

I didn’t really put any interpretation into the validity of the first and fourth sentences, nor did I attempt to answer any of the questions I asked. I just used them to illustrate the questions & types of statements themselves.

However, Bruce’s disagreement shows exactly the kind of process I was looking for when assigning qualifiers and questions to the sentences in the first place. He brings up valid points which prompt further action:

  1. acceptance (if everyone on the team is convinced he’s right)
  2. counterargument (if someone on the team thinks he’s wrong and can explain why), or
  3. clarification (go back to the requirement writers and ask for more information to confirm or deny the assumptions we’ve made

The last two actions restart the process until full agreement is achieved. If you have enough people with good reasoning skills and tenacity to see disagreements through until they’re resolved, then the results of your analysis will probably be very close to the hypothetical “most correct answer possible.”

Along the way, all of your requirements, problem statements, and goals stand a good chance of complete replacement. That’s a good thing — if you’re doing the wrong thing, it doesn’t matter how well you do it, you’ll still come out with a poor result.

« Previous Entries Next Entries »