Atom feed

27 July, 2006 / Usability: Helping Novice Users

All systems have common problem: new user should understand how to work with the system. This is a matter of usability. Usual objects like cup and hummer do provide some valuable hints. Let’s take a knife. It has handle and blade. Handle designed specially for human hand and even if you never seen a knife, you’ll likely take it for handle. This is a good design. But knife is a very simple system, while all enterprise software systems are complex. Very, very complex indeed.

You can’t touch them and find physical hints. You get feedback via monitor only, so all possible hints should be visual. Basically, software should provide feedback on all user actions:

  • What can be dragged?
  • What can be clicked?
  • What can be hovered?
  • Did I hover something interesting?
  • What happened after I’d clicked this large red button with strange title “Delete & Erase”?
  • What can I do on this screen?
  • What can I do with this software?

Two latter questions are the most general and important for us. In most cases system should support two types of users: Novice and Expert. Novice knows a little and system should guide him. Expert knows a lot and system should help him to perform frequent tasks fast, with fewer clicks and less reloads. We are talking about novice users, so let’s forget about expert for awhile.

It’s funny, but in vast majority cases software developers design systems for expert users first. Software released and then developers try to put something that can simplify Novice life. Yes, they put the following things for sure:

  • Huge manual somewhere on installation disk
  • Help (the same manual, but slightly shortened)

Less usual, but still popular:

  • Quick Start (the same manual, but shortened significantly)
  • Tutorials (at least something usable, flash would be great!)

Looks good, isn’t it? There are many help materials, so with luck even dumbest human being should understand how to use the system! No, no, no, that’s not true. What’s the problem? Here is it:

· 0.00% will read Huge Manual

· 1% will read Help (but it will not help :)

· 2% will scan Quick Start

· 5% will see one or two tutorials

So only about 10% of novice users will know something about system. Other 90% will start right from login page. Let’s try to look at real application from novice user’s side.

“I am Mike. I want to review this good-looking project management system. I have it installed and logged in. I want to create and plan first project to see system in action. Here what I see on first page after login:

Hmm… What we have there? Empty ToDo list. It seems something can be assigned on me. Dashboard is active, so I am in Dashboard. In top menu we have Projects and Users. Well, let’s add new project first. Click on Projects link.

Quite clear, click add link and create new project.

What’s next? Most likely I should go inside created project and investigate possibilities. OK, click on project name.

Hmm, new dashboard… Project dashboard. I don’t like it. Why another dashboard? OK, I see one new tab Planning. Also the system has Team and Time Tracking. What Time Tracking means? Ah, nevermind, it is not important for now. Maybe I should click Planning or try Team first?”

As you see, Mike slows down very quickly. It seems he will be able to create project plan, but his first impression is not great. He will have a feeling that system is not easy to use at the first look and maybe he’ll throw it away.

What about solution? In general it sounds pretty easy: system should guide user. It is easier to say than do. How system can guide user at all? How system can predict user actions? It can’t, but you as system developer can. Here are novice user’s life-savers:

  • Wizards. Well-known solution for tasks with narrow focus and several steps. Wizard guide you through all the steps and any novice can accomplish the task.
  • Advices about next possible steps. For tasks with broad focus wizard will not work perfectly. It is better to show next possible actions for user.
  • Context help. Less important, but may be considered for complex actions.

Let’s try again.

“I am logged in.”

“Wow! Nice picture! Here are all steps I need to do to reach the goal. I even can click steps right on the picture! Project added, now add two users.”

“Current step filled green! Oh, so neat feedback! I like it!”

This is not pure wizard, but a mix of context help and “next step adviser”. Novice instantly knows what can be done after all. Novice sees all steps to reach the goal. Novice can navigate to any step from anywhere at any time (if step is available in current system state). First steps became No-brainer even for novice.

Don’t make users think, just make them happy.

26 July, 2006 / Drag and Drop: User Interface

We are finalizing Drag & Drop functionality for TP 2.0. It is Atlas-based and we will share implementation details later.

Here are some basic guides for Drag & Drop functionality UI:

  1. It should be clear that something can be dragged
  2. When you drag something, it should be clear where the item can be dropped
  3. If dragged item hovers droppable area, it is better to highlight the area. Highlighting ensures that this place is really droppable

If you follow these 3 rules, your D&D UI will be at least not bad, since application feedback is clear and guides user.

In TP 2.0 we have Iterations and User Stories. Drag & Drop fits perfectly for iteration planning process, when project manager can assign several user stories on iteration, see iterations plan and make changes.

On screenshot below we have three D&D areas: Backlog (left area) and two iterations (right areas). It is possible to take any user story from any area and drop to any other area. For example, you can take user story from backlog and assign it on Iteration #1.3 (right-top area). Then you may think that this is not the best place for user story, take it again and drop on Iteration #1.4 (right-bottom area). Or in the end you can take this annoying user story and throw it to Backlog. These three areas give user real flexibility and freedom.

21 July, 2006 / IE6, SELECT and Z-index Problem Resolution

Yesterday we have found very tricky but workable solution for awkward IE bug. IE6 SELECT element doesn't support z-index, so if you have layer that covers drop down, the drop down will still be visible! Our first solution was to hide all drop downs on page using javascript when user clicks something and layer appear.

    function showOrHideAllDropDowns(newState) {
        
        var elements = document.documentElement.getElementsByTagName('select');
     
        for (var i=0; i<elements.length; i++) {
            elements[i].style.visibility = newState;
        }
    } 

For example, when user clicks Add link, new layer appear and all drop downs disappear. This is somewhat confusing.

New solution is pure CSS and uses IFRAME, filter:mask() for IE rendering, z-index:-1 to hide IFRAME and other VERY clever tricks. It is required to use the following HTML code


<div class='toolTip selectFree' id='{0}'>
<div class='content'>
Something useful here
</div>
<!--[if lte IE 6.5]><iframe></iframe><![endif]--></div>

And CSS code

.selectFree {
  display: none; 
  font: 10px Verdana; 
  z-index: 2000; 
  background : #C1DAF0;  
  width: 200px
}

.selectFree IFRAME
{
 display:none;/*sorry for IE5*/ 
 display/**/:block;/*sorry for IE5*/
 position:absolute;/*must have*/
 top:0;/*must have*/
 left:0;/*must have*/
 z-index:-1;/*must have*/
 filter:mask();/*must have*/
 width:2000px;/*must have for any big value*/
 height:3000px/*must have for any big value*/;
}

To be honest, I've never seen more tricky solution in CSS world (and I've wrote many articles and two books on CSS!). But it works like a charm.

19 July, 2006 / Why We Developing TP 2.0 from Scratch

Björn Waide posted great comment and said that we'd better refactor TP 1.x than developing TP 2.0 from scratch.

Let me justify our decision. TP 2.0 going to be easier to use, easier to evolve and easier to support. We've really estimated cost of all work that required to refactor TP 1.x to appropriate level, but it appeared that cost of new software almost the same as cost of refactoring.

Reasons:

  1. NEO O/R framework has several very bad restrictions that make overall architecture ineffective and hard to support. So NEO should be replaced by another ORMF (NHibernate in our case). But since so many places in application use NEO directly, such replacement will take really much time
  2. UI of TP 1.x does not fit new ideas and structure. It should be completely changed.
  3. Controller layer in ASP.NET has high cohesion with UI because of code behind (Page Controller pattern). So if UI changed greatly, controller will be changed as well.

So we have to throw out business layer and throw out UI. In fact that is almost all we have in application :) As a result, there is almost no difference between refactoring and re-writing.

First release of TP 2.0 (code name "Minima") will appear next week or so. We've almost done with new architecture and partially with new UI, so from this perspective, I don't think our decision was wrong.

Navigation is one half of usability. Well, maybe slightly less, but it is very important for sure. Do you know simple test for navigation? Visit this page and try to answer just 3 questions:

  1. Where I am now?
  2. Where can I go?
  3. How did I get there?

Did you answer them? Yeah, google doesn’t make great UI all the time, their web site is not perfect in some areas :) I bet only second questions can be partially answered. If you can answer on all these 3 questions being on page, navigation is at least good.

Hierarchy

Each web application has hierarchy. You have areas, sections and pages. For example, diagram below shows small part of TP 2.0 hierarchy. Areas are in gray, sections in blue and pages in green.

Usually, there are several areas, dozens of sections and many pages. You always have two tradeoffs: make hierarchy deep and narrow or plain and wide. I don’t recommend hierarchy deeper than 3-4 levels, otherwise it will be hard to get back to the sun from the bottom. And I don’t recommend hierarchy wider than 10-12 items, since too many options make user frustrated.

The best way to make great navigation is reflect user expectations about domain. For example, if user thinks that Bug is something related to Quality Assurance, it is better to create Quality Assurance section and put Bugs page into it. If you put Bugs page into Issues Management section user will hardly find it quickly.

Where I am now?

So where I am on the page below? System gives some hints:

  • Current project is shown in drop down, so we are in Project area
  • Planning tab is active: has blue color and all its items are visible. So we are in Planning section
  • User Stories item highlighted in tab, so we are in User Stories page
  • Breadcrumb shows current page in hierarchy.

Four hints that shows my current coordinates in TP 2.0 hierarchy. Each level has its hint, while breadcrumb reflects all levels. In fact one may think that breadcrumb will be enough, but that is not true. Breadcrumb is not highly visible on the page, it is a secondary element. Many people don’t pay attention to breadcrumb and silently ignore it.

Several UI elements works together to unload user’s memory:

  • Menu with tabs. Current section highlighted
  • Breadcrumb with current page enlarged
  • Current project drop down
  • Sections color coding. Each section in TP has own color. With time user starts to ‘feel’ current section.

Where can I go?

This important question is harder to answer than previous one. If you provide too many links, it will be confusing for user. If you provide tool little links, basic navigation will take more clicks and pages reloads. We can categorize all navigation paths:

  • Vertical navigation (up and down in hierarchy)
  • Horizontal navigation (in scope of single area or section)
  • Jump navigation

I have several rules that almost always use in navigation development:

  • Make areas menu visible on all pages. If you will not do that, user will have to click Home link to see areas menu. For example, let’s imagine that TP does not have top menu. I am inside project and suddenly find out that I should change something in Admin. Without top menu I have to click Main link on breadcrumb and then Admin link. But why should I reach Admin via Main? This is not intuitive. So top menu should be visible on all pages.
  • Make important actions available with a single click.
  • Make all items in current section always visible. For example, if you in Planning section, links on all pages in Planning area should be visible. Otherwise user may not discover invisible links.

Screenshot below shows how these patterns work in TP 2.0.

Quick links drop down. Maybe we will add another drop down with other actions but Add in future.

How did I get there?

Maybe this question is less important, but solution is hardest from my point of view and at the moment TP 2.0 does not have a good one. All we have is breadcrumb, but it does not show actual user path, but only page’s place in hierarchy.

Among several solutions we are reviewing are:

  • Remember last 10 pages user visited and show them in a list somewhere. This will give complete path and user will have clear answer, but will take valuable place. Well, we can take these 10 last pages and put them into hidden layer that will be visible on “Show Last Visited Pages” link. But we doubt that users will actually use this link
  • Put Back link on all pages. Yes, you are right, all web browsers have Back action, but I personally know several people who don’t use them!

If you have other solutions of navigation problems, post them!

15 July, 2006 / Rules Framework Architecture. Unobtrusive Way.

Yesterday we have finished Rules Framework implementation. Functional requirements are quite simple: It should be possible to execute set of rules on events in entity life-cycle (Add, Update, Delete, Change State). For example:

  • When someone adds user story into TP, it may be assigned on less burden developer
  • When someone changes state of bug (from Open to Fixed), assigned tester should be notified about that

Let's invent the wheel (I mean solution for Rule Framework).

We have entities hierarchy (some entities shown on the left of the picture below). Entity class is a base class for all entities. It contains common operations like Save/Delete/Retrieve, assertions and changes detection. From Rules Framework point of view we need to execute several rules when entity saved into database, so all we need is invoke rules from Save() method. OK so far, now we need to think about events domain model.

Some knowledge that we have about rules:

  • Event and Rule nouns. We often used them in discussion, so most likely they are entities.
  • Each entity may have own set of rules
  • All entities have Add, Update and Delete events and several entities have Change State event.
  • For each state we may apply different set of rules, this complicates things
  • We have different rules that will be executed uniformly. This leads to interface with Execute method. Let's call it IRuleAction. Each rule with business logic will be an implementation of IRuleAction.

So let’s coin some terms. Event may be applied for different entity, for different state and each event has one of four types (Add, Update, Delete, Change State). Rule is something that relates to event and may be executed. Naturally, we came up with two classes: TpEvent and Rule (Tp prefix required to avoid conflicts with C# event keyword). TpEvent has a collection of rules (look at the right corner of diagram). TpEvent and Rule are persisted classes.

Rule class in quite general and does not have any specific behavior. But how we will have different rules in this case? In other words, how we will bind IRuleAction implementation to Rule class? Well, Rule has RuleClass property, which gives rule some uniqueness. Nothing special, RuleClass stores name of the custom rule class that contains specific behavior. As you rightly guessed, we will use reflection for rules execution.

That’s (almost) it! Now we understand that we need something that will initiate rules execution. We will call this class RuleManager. It will be instantiated in appropriate place (for example, Save() method) and invoke all events for current ActionType and entity.

For example, we adding new user story:

  1. Instantiate RuleManager in Save method
  2. Pass this new user story and Add action type into RuleManager
  3. Rule manager will load TpEvent from database with all Rules, instantiate IRuleAction using Rule.RuleClass property and reflection magic and Execute this instantiated IRuleAction implementation.

1. RuleManager usage:

ActionTypeEnum actionType = entity.IsNew
? ActionTypeEnum.Add
: ActionTypeEnum.Update;

RuleManager ruleManager = new RuleManager(entity);
ruleManager.RaiseEvent(actionType);

2. Load event from database and execute each rule

TpEvent tpEvent = TpEvent.FindEvent(entity.GetType(), actionType);
foreach (Rule rule in tpEvent.Rules)
Execute(rule);

3. Inside Execute method. Create instance and really execute the rule

Type ruleActionType = Type.GetType(rule.RuleClass);

IRuleAction ruleAction = (IRuleAction) Activator.CreateInstance(ruleActionType);

ruleAction.Execute(entity, rule);

Very nice class diagram:

So what are the advantages of such implementation? I can’t find them… Just kidding :) First, we almost leave domain model untouched. Few lines of code have been added into Entity.Save() and Entity.Delete() methods. So we have low coupling. Second, we can add new rule very easily: all we need is implement IRuleAction interface with single Execute(Entity entity, Rule rule) method. Third, rules are written on C# which is definitely good.

Another interesting part is UI for Entity->Event->Rules setting, but this is a different story.

10 July, 2006 / NHibernate and Generics

Today we've struggled with NHibernate + Generics + Collections. The typical problem in NHibernate collections is that when you add relation using property, collection is not updated. For example, we have two classes Project and Release. Project has a collection of Releases. So, the following test will fail:

Project project = new Project();
Release release = new Release();
release.Project = project;
project.Save(); // this is our wrapper for session.Save...

Assert.AreEquals(1, project.Releases.Count)

Also you can't just add release to project via collection, the code below will not save anything:

Project project = new Project();
Release release = new Release();
project.Releases.Add(release);
project.Save();

To have Releases collection in place, you should re-read project from database, which is not very good in general case. We are not alone with this problem and quite cool solution has been implemented by Ayende, it is NHibernate Generics.

We've take the solution and tried to incorporate it in our framework. That was not very easy and we've learn some gotchas. First of all, our application already has about 20 classes and changes should be made in each class. However, that was not a large problem, since:

  1. We use code generation
  2. We have about 150 unit tests

This was major refactoring and it took only 1 full day. I don't know how much time such refactoring will take without code generation and tests, maybe a week... But most possible after 2-3 days team will revert code back :)

The most difficult thing was templates for code generator. We generate base classes for entities, and generator logic is was not obvious. For example, base constructors should have delegates in one cases and should not have them on another. Also we have custom method SaveByMerge that we use to save entities by ObjectDataSource that were edited in FormView. This method broke several tests. The logic of the method is quite natural, but it did not work as expected. Let's say you want to update project entity. You open edit form, type new project name and click Save button. System creates new project entity (it plays role of data transfer object in fact) and pass this new project from UI to business layer as well as actual project that should be updated. SaveByMerge checks all changed fields, sets new values for actual project and save it. The problem was in collections update in this method. When collection copied from DTO project to real project, all elements of collection in DTO removed and NHibernate throws an exception about re-saving deleted entities. The only solution we found is comment one line of code in Ayende library to not invoke item removal in this case.

The first question is what we are developing. We are creating web-based project management system for agile teams. Many words, but idea is quite simple. It is integrated tool for project planning, progress tracking, bug tracking and so on.

The first version was released in 2004 and now it is time for redesign. We have many new ideas based on previous customers' experience, we have better tools like .NET 2.0, so we decided to release v2.0 with completely new architecture. Yes, we are re-writing TP from scratch. This looks like a Things You Should Never Do from Joel Spolsky, but we've did some estimates and it appeared that full rewrite will take almost the same time as such major refactorings. So Joel NOT always right :)

Since we creating new software, there are no restrictions and legacy code to support. So we may use any tools we like. Here is a short list:

Technologies and Frameworks
  • ASP.NET 2.0
  • Atlas
  • NHibernate
  • Microsoft ReportViewer
Tools
  • Subversion
  • TargetProcess 1.7 (of course :)
  • NUnit
Practices
  • Test Driven Development
  • Iterative Development

Nothing special for modern world in fact.

09 July, 2006 / First Post and Announcement

So what is this? We are developing new version of our product TargetProcess and use a bunch of new and not-so-new-but-still-interesting technologies and practices like ASP.NET 2.0, NHibernate, Atlas, Test Driven Development, Iterative Development. We think that our real experience in real large application should not die in code and ready product, we want to share our problems, solutions, mistakes and success. We want to share useful controls, code samples, opinions and so on. This is the place where for experience sharing.

We hope that updates will be every day (except weekends, sorry :), but please excuse us if you will not find new post for a day or two. The main our goal is ready product, so delays may happen.

 

We are developing new version of TargetProcess and blogging about our progress.

TP 2.0 online demo
TP 2.0 quick tour