Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

UI/UX Design  Module 4 – Information architecture

Card sorting and tree testing

Run open, closed and hybrid card sorts, read a similarity matrix and a dendrogram cut, then tree-test the structure that comes out of it.

  • Intermediate
  • 25 minutes
  • Examples run with Node.js 24.21.0, Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free lesson, Pro answers: 25 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Distinguish open, closed and hybrid card sorts by what each one can tell you about a structure
  • Choose a card-sort sample size from the correlation evidence, and say what the extra people buy
  • Analyse a sort with a similarity matrix and average-linkage clustering, and cut the tree at a height
  • Interpret a tree test's success, directness and first-click numbers task by task
  • Plan the one change a weak task asks for, and the test that would show whether it worked

Before you start

What you’ll learn · Before you start

What you will learn

  • Distinguish open, closed and hybrid card sorts by what each one can tell you about a structure
  • Choose a card-sort sample size from the correlation evidence, and say what the extra people buy
  • Analyse a sort with a similarity matrix and average-linkage clustering, and cut the tree at a height
  • Interpret a tree test's success, directness and first-click numbers task by task
  • Plan the one change a weak task asks for, and the test that would show whether it worked

Before you start

On this page

Twenty people sorted twelve cards from MealBox’s account area into piles of their own making, and the pair Pause plan and Cancel plan came out at 0.75: fifteen of the twenty kept them together. Five people did not, and the five are the interesting part, because a menu has to work for them too.

Two methods carry most of this work. A card sort asks people to group content and say what they would call each group, which is how a structure gets proposed; tree testing asks people to find things in a structure that has been written down, which is how it gets checked. This lesson runs both on invented data you can re-run: a similarity matrix and an average-linkage tree for the MealBox sort, then success, directness and first clicks for an eight-task tree test of the Ward Desk menu, ending with the one change each weak task asks for.

Three kinds of card sort, and what each one can tell you

MealBox has twelve things a customer can do to their account, and today they sit in one list of twelve links. A card sort: A study in which participants group content items written on cards, and often name each group, so that a structure can be proposed from how people think the content belongs together. puts each of those twelve on a card and asks people to group them. Three variants ask three different questions.

In an open card sort: A card sort in which participants are given the cards only: they make as many groups as they like and name each group in their own words. participants get the cards and nothing else: they make as many piles as they like and name each pile themselves. In a closed card sort: A card sort in which the categories are given to participants already named, so the study reports how well those categories absorb the cards rather than what groups people would make. the categories come from you, already named, and participants place each card into one of them. A A card sort in which participants are given your named categories and may also create categories of their own for cards that fit nowhere. gives your categories and allows new ones, so a card that fits nowhere has somewhere to go.

The difference matters more than it looks, because only the open sort can tell you something you did not already think of. Nielsen Norman Group draws the line sharply: card sorting is a generative method, used to find possible groupings and the vocabulary that goes with them, and it is a poor way to evaluate a hierarchy you have already built. A closed sort reports how well your categories absorb the cards; it cannot report the category you forgot, because no participant can invent it.

Answer three questions about your own study in the table below and watch options drop out:

Interactive · answer and compare Open, closed or hybrid for the sort in front of you? Answer the three questions about your study; the table then says which kind of sort still fits.
Do you already have a structure, named end to end, that you want to put to the test?
Do you need the words people use for these groups, for labels or for search?
Who will analyse it, and how much time do they have?

Answer the questions above: the table then marks what fits your case and says why the rest does not.

Three kinds of card sort, against what each one tells you, what the analysis costs and how much it leads.
Kind of sort What it tells you Cost to analyse Risk of leading people
Open sort Participants get: cards only, and name the piles Good the groups people make and the words they use for them, with nothing suggested Poor the names people wrote on their piles have to be coded by hand before a count means anything Good no category is shown, so nothing in the material points at an answer
Closed sort Participants get: cards and your categories Poor only whether cards land where you expected; a missing category never appears Good counts per category come straight out, so two structures compare in minutes Poor every card has to go somewhere, so a wrong category still collects cards
Hybrid sort Participants get: your categories, plus new ones Mixed tests your categories and catches the gaps, but most people take what is offered Mixed fixed categories count themselves; the added ones need coding like an open sort Mixed the offered categories anchor people, though a new pile is one way out
How the answers decide
  • “Do you already have a structure, named end to end, that you want to put to the test?” No, we start from the content: rules out Closed sort. A closed sort can only report how well cards fit the categories you gave it. Without a structure there is nothing to give, and the categories you invent on the way become the finding.
  • “Do you already have a structure, named end to end, that you want to put to the test?” Yes, it is named: adds a caveat to Open sort. An open sort still works and may contradict the structure you have, which is useful; it just costs more to analyse than putting the structure itself in front of people.
  • “Do you need the words people use for these groups, for labels or for search?” Yes, we need their words: rules out Closed sort. Only an open or hybrid sort collects pile names in people's own words, which is where label candidates and search synonyms come from.
  • “Who will analyse it, and how much time do they have?” One person, a few hours: rules out Open sort. Coding the pile names twenty people wrote is the slow part of an open sort; with hours rather than days the coding is what gets skipped, and then the similarity numbers stand on nothing.
  • “Who will analyse it, and how much time do they have?” One person, a few hours and “Do you need the words people use for these groups, for labels or for search?” Yes, we need their words: adds a caveat to Hybrid sort. A hybrid sort halves the coding, because only the piles people added need it, but those few piles are then the whole answer to the label question.
  • “Do you already have a structure, named end to end, that you want to put to the test?” Yes, it is named and “Do you need the words people use for these groups, for labels or for search?” No, labels are settled: adds a caveat to Hybrid sort. With labels settled, the new piles a hybrid sort allows are mostly a signal that a category is missing, so read them as gaps rather than as naming evidence.

The table’s reason for ruling out an open sort when only hours are available is worth taking seriously: the slow part of an open sort is coding the pile names people wrote, and a coded-badly open sort is worse than a clean closed one.

Using a sort to grade a menu you already have

Wrong: “we ran a card sort on the new menu and 80 % of cards landed where we put them, so the menu is good.” That was a closed sort, and the number says that people can place cards into the categories you named. Right: propose the structure from a sort, then test whether people can find things in it, which is what the second half of this lesson does.

Five participants are enough to test a design, not to build one

The usual advice for a usability test is to watch five people, because the fifth rarely shows you something new. Card sorting does not behave that way, and there is a measurement behind the claim. Tom Tullis and Larry Wood ran a sort with 168 participants at Fidelity Investments and then simulated smaller studies by drawing random subsets of that set, so the result of a 20-person study could be compared with the result of the full 168. Nielsen Norman Group reports the correlations:

Participants Correlation What it buys
5 0.75 ideas, not placements
15 0.90 the usual stopping point
20 about 0.93 contested cards firm up
30 0.95 for a structure used daily
60 0.98 almost nothing more

Tullis and Wood recommended 20 to 30 participants; Nielsen’s own reading of the same numbers is 15 for most projects and 30 where a lot rides on the structure. Both readings beat five by a wide margin, and the reason is the kind of question being asked. A usability test evaluates something that exists, so one person struggling is already evidence of a problem. A sort generates something that does not exist yet, out of mental models and vocabulary that differ from person to person, and a stable picture of that variation needs a crowd.

Treat these figures as one careful study rather than a law: it was run at one company on one information space, and the analysis looked only at the numbers, not at what participants said while sorting. Nielsen Norman Group names both limits itself. The practical consequence is the useful part — spend the budget you save by stopping at 15 on a round of tree testing rather than on participants 16 to 30, because the second method finds a different class of problem.

Checkpoint

Check: Open, closed and hybrid sorts · How many participants a sort needs

  1. Question 1 of 2 Ward Desk has no structure yet and needs label candidates in residents' own words. One researcher has three days for the analysis. Which sort fits?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: An open sort, with the pile names coded afterwards

    No structure plus a need for people's own words rules out the closed sort, and the three days cover the one real cost of an open sort, which is coding the pile names people wrote. A hybrid sort would be the compromise if only hours were available.

  2. Question 2 of 2 In the study Nielsen Norman Group reports, how many participants were needed for a sort's similarity scores to correlate 0.90 with the result from the full set?

    Type a number.

    A hint

    It is three times the number usually recommended for a usability test.

    Show the answer: Question 2 of 2

    Answer: 15 participants

    Five participants reached 0.75, fifteen reached 0.90 and thirty reached 0.95. Fifteen is where Nielsen's reading stops for most projects, because the next fifteen people buy 0.05.

The similarity matrix is the sort, and the cut is your guess

Everything quantitative in a card sort comes from one number per pair of cards: the share of participants who put those two cards in the same pile. All twenty together is 1.00, half of them is 0.50. Collect that number for every pair and you have a similarity matrix: A table holding one number per pair of cards in a sort: the share of participants who put those two cards in the same group, from 0 for none to 1 for all of them., which is the sort in a form you can compute with.

The program below builds the matrix for the twelve MealBox cards from twenty synthetic sorts, then groups the cards. Press Run to see the first five rows, five named pairs and three readings of the tree; press Edit and move a card between piles in UNSETTLED to watch the groups move:

A similarity matrix and average-linkage groups, standard library only Python · card-sort/card_sort.py
"""An open card sort of the MealBox account area, analysed with the standard library.

Twenty synthetic participants sort twelve cards. Each one starts from a latent
grouping and then moves the cards this lesson calls unsettled; some keep one
pile for two of them. Every pile, card and number below is invented.
"""

import random

CARDS = [
    "Pause plan", "Cancel plan", "Skip one day",
    "Change address", "Change delivery window",
    "Update payment card", "Download invoices", "Apply a coupon",
    "Change meal preferences", "Rate a dish",
    "Report a missing box", "Contact support",
]
LATENT = ["plan", "plan", "plan", "delivery", "delivery", "money",
          "money", "money", "food", "food", "help", "help"]
UNSETTLED = {"Cancel plan": (0.25, ["help"]), "Skip one day": (0.40, ["delivery"]),
             "Apply a coupon": (0.45, ["plan", "food"]),
             "Download invoices": (0.40, ["help"]), "Rate a dish": (0.35, ["help"])}


def sorts(seed, people=20):
    """One pile name per card, for each participant."""
    out = []
    rng = random.Random(seed)
    for _ in range(people):
        piles = dict(zip(CARDS, LATENT))
        for card, (chance, elsewhere) in UNSETTLED.items():
            if rng.random() < chance:
                piles[card] = rng.choice(elsewhere)
        if rng.random() < 0.30:  # this participant keeps one pile for both
            piles = {c: ("plan" if g == "delivery" else g) for c, g in piles.items()}
        out.append(piles)
    return out


def similarity(piles):
    """The share of participants who put each pair of cards in one pile."""
    return [[sum(p[a] == p[b] for p in piles) / len(piles) for b in CARDS] for a in CARDS]


def average_linkage(sim):
    """Join the two closest groups until one is left; distance = 1 - similarity."""
    groups, merges = [[i] for i in range(len(CARDS))], []
    while len(groups) > 1:
        height, x, y = min(((sum(1 - sim[i][j] for i in a for j in b) / (len(a) * len(b)), p, q)
                            for p, a in enumerate(groups) for q, b in enumerate(groups) if p < q),
                           key=lambda t: (round(t[0], 9), t[1], t[2]))
        joined = sorted(groups[x] + groups[y])
        groups = [g for k, g in enumerate(groups) if k not in (x, y)] + [joined]
        merges.append((height, joined))
    return merges


def cut(merges, height):
    """The groups left when every join up to this height is kept."""
    groups = [[i] for i in range(len(CARDS))]
    for at, joined in merges:
        if at <= height + 1e-9:
            groups = [g for g in groups if not set(g) & set(joined)] + [joined]
    return sorted(groups)


def report(seed=194):
    sim = similarity(sorts(seed))
    print("Cards: " + ", ".join(f"C{i + 1} {c}" for i, c in enumerate(CARDS)))
    print("\nSimilarity matrix, first five rows (share of 20 participants):")
    print("      " + " ".join(f" C{i + 1:<2}" for i in range(len(CARDS))))
    for i in range(5):
        print(f"C{i + 1:<4} " + " ".join(f"{sim[i][j]:.2f}" for j in range(len(CARDS))))
    print()
    for a, b in [("Pause plan", "Cancel plan"), ("Change address", "Change delivery window"),
                 ("Apply a coupon", "Update payment card"), ("Apply a coupon", "Change meal preferences"),
                 ("Download invoices", "Contact support")]:
        print(f"{a} + {b}: {sim[CARDS.index(a)][CARDS.index(b)]:.2f}")
    merges = average_linkage(sim)
    print("\nJoin heights: " + ", ".join(f"{h:.2f}" for h, _ in merges))
    for height in (0.00, 0.50, 0.90):
        groups = cut(merges, height)
        print(f"\nCut at distance {height:.2f} ({1 - height:.2f} similarity): {len(groups)} groups")
        for g in groups:
            print("  " + " | ".join(CARDS[i] for i in g))
    return len(merges)


_ = report()

Output

Cards: C1 Pause plan, C2 Cancel plan, C3 Skip one day, C4 Change address, C5 Change delivery window, C6 Update payment card, C7 Download invoices, C8 Apply a coupon, C9 Change meal preferences, C10 Rate a dish, C11 Report a missing box, C12 Contact support

Similarity matrix, first five rows (share of 20 participants):
       C1   C2   C3   C4   C5   C6   C7   C8   C9   C10  C11  C12
C1    1.00 0.75 0.50 0.20 0.20 0.00 0.00 0.20 0.00 0.00 0.00 0.00
C2    0.75 1.00 0.40 0.15 0.15 0.00 0.05 0.20 0.00 0.05 0.25 0.25
C3    0.50 0.40 1.00 0.70 0.70 0.00 0.00 0.15 0.00 0.00 0.00 0.00
C4    0.20 0.15 0.70 1.00 1.00 0.00 0.00 0.05 0.00 0.00 0.00 0.00
C5    0.20 0.15 0.70 1.00 1.00 0.00 0.00 0.05 0.00 0.00 0.00 0.00

Pause plan + Cancel plan: 0.75
Change address + Change delivery window: 1.00
Apply a coupon + Update payment card: 0.60
Apply a coupon + Change meal preferences: 0.20
Download invoices + Contact support: 0.40

Join heights: 0.00, 0.00, 0.25, 0.30, 0.30, 0.40, 0.50, 0.73, 0.85, 0.89, 0.96

Cut at distance 0.00 (1.00 similarity): 10 groups
  Pause plan
  Cancel plan
  Skip one day
  Change address | Change delivery window
  Update payment card
  Download invoices
  Apply a coupon
  Change meal preferences
  Rate a dish
  Report a missing box | Contact support

Cut at distance 0.50 (0.50 similarity): 5 groups
  Pause plan | Cancel plan
  Skip one day | Change address | Change delivery window
  Update payment card | Download invoices | Apply a coupon
  Change meal preferences | Rate a dish
  Report a missing box | Contact support

Cut at distance 0.90 (0.10 similarity): 2 groups
  Pause plan | Cancel plan | Skip one day | Change address | Change delivery window
  Update payment card | Download invoices | Apply a coupon | Change meal preferences | Rate a dish | Report a missing box | Contact support

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 card_sort.py

Four pairs from that output say most of what the sort found. Change address with Change delivery window is 1.00, so nobody disagreed. Pause plan with Cancel plan is 0.75. Apply a coupon sits at 0.60 with Update payment card and 0.20 with Change meal preferences, so it is a money card for most people but not for all. Download invoices with Contact support is 0.40, which is the sort telling you that invoices are a help topic for two people in five.

hierarchical clustering: Building a tree of groups by repeatedly joining the two closest groups; average linkage measures closeness as the mean distance over every pair of their members. turns those numbers into a tree. Average linkage starts with each card alone, repeatedly joins the two groups whose average distance is smallest, and records the height at which each join happened; distance here is simply 1 minus similarity. Standard-library Python does all of that for twelve cards, with no package to install, and the heights it recorded are the output’s Join heights line. The tree it draws is a dendrogram: The tree hierarchical clustering produces, with each join drawn at the distance at which it happened, so cutting it at a chosen height gives a set of groups., and a dendrogram on its own proposes nothing: you have to cut it at a height, and every height gives a different answer.

Start from the twelve leaves, step down the three cuts the program reports and watch the group count fall:

Interactive · stage by stage Three cuts through the same tree of twelve cards Walk down the heights and watch the group count fall from twelve to two, then pick the cut worth testing.
Three cuts through the same tree of twelve cards, step 5 of 5. Showing Leaves: 12 cards, Cut at distance 0.00 (10 groups left), Cut at distance 0.50 (5 groups left), Cut at distance 0.90 (2 groups left). In focus: Cut at distance 0.50.Leaves: 12 cardsno joins made yetCut at distance 0.0010 groups leftCut at distance 0.505 groups leftCut at distance 0.902 groups left

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. The leaves. The tree starts as twelve separate cards, one leaf each, and nothing is grouped yet. Reading the tree at this height gives twelve groups, which is the account area MealBox already has: twelve links in a row.
  2. Cut where nobody disagreed. Average linkage joins the two closest groups first. Two joins happen at distance 0.00, where similarity is 1.00: all twenty participants put Change address with Change delivery window, and all twenty put Report a missing box with Contact support. Cutting here leaves 10 groups.
  3. Cut at half the room. Keep every join up to distance 0.50, that is every pair half the room or more agreed on, and 5 groups are left: Pause and Cancel plan; Skip one day with the two delivery cards; payment card, invoices and coupon; preferences with Rate a dish; missing box with Contact support.
  4. Cut so low it says nothing. At distance 0.90 any faint overlap counts as a join, and the twelve cards collapse into 2 groups: everything about a box in one, everything about money, food and help in the other. A menu of two labels hides the twelve things people came to do, so this cut is useless even though it is the tidiest.
  5. The cut is a hypothesis. The 5-group cut is the one worth writing into a menu, and it is still only a guess: the program never saw a label, a task or a screen. Name the five groups, then tree-test them. Skip one day landing with the delivery cards rather than with Pause is exactly the kind of claim a tree test can check.

The five-group cut is the one worth writing into a menu, and notice what it claims: Skip one day belongs with the two delivery cards rather than with Pause plan and Cancel plan. In the matrix, Skip one day sits at 0.70 with Change address and 0.50 with Pause plan, so that claim rests on four participants. The program has never seen a label, a task or a screen.

Key idea

A cut through a dendrogram is a hypothesis about a structure, not a structure. It inherits every habit of the twenty people who sorted, and it has to survive a test with new people and real tasks before it becomes a menu.

Checkpoint

Check: The similarity matrix and the cut

  1. Question 1 of 2 In the lesson's run, Download invoices and Contact support have a similarity of 0.40. What does that mean about the twenty participants?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Eight of the twenty put both cards in one pile

    Each pair gets one number: the share of participants who put the two cards together. Eight of twenty is 0.40, which is the sort saying invoices are a help topic for a sizeable minority.

  2. Question 2 of 2 The same tree gives ten groups at distance 0.00, five at 0.50 and two at 0.90. A stakeholder asks which cut is correct. What do you tell them?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: None is correct yet; the five-group cut is the structure worth tree-testing first

    Every height gives a different structure, so the tree cannot say which is right. Cut where the groups are recognisable, name them, then tree-test; the five-group cut also carries a claim worth checking, that Skip one day belongs with the delivery cards.

A tree test checks findability before anything is designed

A tree test: A study that evaluates a category structure by asking participants to find where given things would live in it, shown as bare expanding lists with no visual design or page content. is the cheapest way to check that hypothesis. Participants see the categories as a bare set of expanding lists, with no visual design, no page content and no search box, and they are given tasks: find where you would look for this. They click down, can climb back out, and finish by choosing one item. Tree testing therefore runs off a spreadsheet. Nothing has to be designed or written first, so a tree can be tested the day after a card sort, and a rejected tree costs an afternoon rather than a sprint.

Four numbers come out of it per task. task success: In a tree test, the share of participants who finished a task on an item that answers it; reported per task rather than as one figure for the study. is the share of participants who ended on an item that answers the task. directness: In a tree test, the share of participants who reached their answer without climbing back out of a branch they had opened, read as a sign of how confident the path felt. is the share who never climbed back out of a branch they had opened, which Nielsen Norman Group describes as a proxy for how confident people were in the path they took. The first click: The branch or link a participant opens first in a task, counted across participants to show where a structure sends people before anything else happens. is the branch each participant opened first, counted across participants, and it tells you where the structure sent them before anything else happened. Time on task is the fourth, and it needs a tool’s clock rather than a click log.

The program below scores an eight-task tree test of the Ward Desk menu from a seeded log of forty participants, and prints the two weakest tasks with their first-click split. Press Run, then press Edit and move Birth certificate’s weight towards Pay and apply in TASKS to see a task recover:

Success, directness and first clicks for eight tasks Python · tree-test/tree_test.py
"""A tree test of the Ward Desk menu: eight tasks, forty synthetic participants.

The log is drawn from a seeded model of where people click first and how often
they climb back out of a branch. Success is the share who ended on the right
leaf; directness is the share who never left a branch they had opened. Every
category, task and number below is invented.
"""

import random
from collections import Counter

TREE = {
    "Report a problem": ["Street light out", "Pothole", "Rubbish not collected", "Water leak"],
    "Pay and apply": ["Property tax", "Water bill", "Trade licence", "Birth certificate"],
    "Your ward": ["Ward officer", "Ward meetings", "Ward boundaries"],
    "About the council": ["Departments", "Budget", "Tenders"],
    "Help": ["Contact us", "Track a complaint", "Office hours"],
}
TOP_OF = {leaf: top for top, leaves in TREE.items() for leaf in leaves}
# task: the leaf that answers it, and how often each branch is opened first
TASKS = {
    "street light out": ("Street light out", {"Report a problem": 36, "Help": 3, "Your ward": 1}),
    "pay the property tax": ("Property tax", {"Pay and apply": 33, "About the council": 4, "Help": 3}),
    "chase a complaint": ("Track a complaint", {"Report a problem": 25, "Help": 9, "Your ward": 6}),
    "get a birth certificate": ("Birth certificate", {"About the council": 17, "Help": 12,
                                                      "Pay and apply": 8, "Your ward": 3}),
    "name the ward officer": ("Ward officer", {"Your ward": 31, "About the council": 6, "Help": 3}),
    "rubbish not collected": ("Rubbish not collected", {"Report a problem": 34, "Help": 4, "Your ward": 2}),
    "last year's spending": ("Budget", {"About the council": 28, "Your ward": 7, "Help": 5}),
    "office opening time": ("Office hours", {"Help": 30, "About the council": 7, "Your ward": 3}),
}


def branch(rng, weights):
    tops = list(weights)
    return rng.choices(tops, weights=[weights[t] for t in tops])[0]


def session(rng, leaf, weights):
    """One participant: the branches they opened, and the leaf they settled on."""
    path = [branch(rng, weights)]
    while path[-1] != TOP_OF[leaf] and len(path) < 3 and rng.random() < 0.45:
        path.append(TOP_OF[leaf] if rng.random() < 0.55 else branch(rng, weights))
    inside = TREE[path[-1]]
    chosen = leaf if path[-1] == TOP_OF[leaf] and rng.random() < 0.92 else rng.choice(inside)
    return path, chosen


def log(seed=31, people=40):
    rng = random.Random(seed)
    rows = []
    for task, (leaf, weights) in TASKS.items():
        for _ in range(people):
            path, chosen = session(rng, leaf, weights)
            rows.append({"task": task, "path": path, "chose": chosen, "right": leaf})
    return rows


def score(rows):
    out = {}
    for task in TASKS:
        mine = [r for r in rows if r["task"] == task]
        out[task] = {
            "success": sum(r["chose"] == r["right"] for r in mine) / len(mine),
            "directness": sum(len(r["path"]) == 1 for r in mine) / len(mine),
            "first": Counter(r["path"][0] for r in mine),
        }
    return out


def report():
    rows = log()
    scores = score(rows)
    print(f"{len(rows)} task attempts, {len(rows) // len(TASKS)} participants, {len(TASKS)} tasks")
    print(f"\n{'Task':<24}{'Success':>9}{'Direct':>8}  First branch most often")
    for task, s in scores.items():
        top, hits = s["first"].most_common(1)[0]
        print(f"{task:<24}{s['success']:>8.0%}{s['directness']:>8.0%}  {top} ({hits} of 40)")
    weakest = sorted(scores, key=lambda t: (scores[t]["success"], scores[t]["directness"]))[:2]
    print("\nThe two weakest tasks:")
    for task in weakest:
        s = scores[task]
        print(f"\n  {task}: answer {TASKS[task][0]!r} under {TOP_OF[TASKS[task][0]]!r}")
        print(f"  success {s['success']:.0%}, directness {s['directness']:.0%}; first branch:")
        for top, hits in s["first"].most_common():
            print(f"    {top:<20}{hits:>3} of 40 ({hits / 40:.0%})")
    return weakest


_ = report()

Output

320 task attempts, 40 participants, 8 tasks

Task                      Success  Direct  First branch most often
street light out             88%    100%  Report a problem (36 of 40)
pay the property tax         95%     80%  Pay and apply (32 of 40)
chase a complaint            48%     70%  Report a problem (23 of 40)
get a birth certificate      40%     68%  About the council (17 of 40)
name the ward officer        85%     95%  Your ward (33 of 40)
rubbish not collected        90%    100%  Report a problem (38 of 40)
last year's spending         75%     80%  About the council (25 of 40)
office opening time          80%     88%  Help (30 of 40)

The two weakest tasks:

  get a birth certificate: answer 'Birth certificate' under 'Pay and apply'
  success 40%, directness 68%; first branch:
    About the council    17 of 40 (42%)
    Help                 14 of 40 (35%)
    Pay and apply         8 of 40 (20%)
    Your ward             1 of 40 (2%)

  chase a complaint: answer 'Track a complaint' under 'Help'
  success 48%, directness 70%; first branch:
    Report a problem     23 of 40 (57%)
    Help                 12 of 40 (30%)
    Your ward             5 of 40 (12%)

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 tree_test.py

Six of the eight tasks are healthy: reporting a street light succeeded for 88 % of participants with every one of them going straight there, and paying the property tax reached 95 %. Two are not, and the output names them.

Writing the label into the task

Wrong: “Find information about starting a business” on a tree whose category is called Starting a business. Participants then match words rather than find things, and the task passes whatever the structure does. Right: describe the situation and let them choose. Nielsen Norman Group’s own version of that task sets up someone who is considering a lawn-care service and asks them to see whether the site has anything to help them begin.

One more limit is worth knowing before you read any tool’s report: most tree-testing software accepts only a bottom-level item as a correct answer, never a category. Real sites often give a category its own landing page, and a task answered by that page cannot be marked right, so a sensible tree can score badly for a reason that has nothing to do with people.

India-specific

Ward Desk publishes in Hindi and English, and a tree test runs per language: the same eight tasks against a Hindi tree, with participants who use that version. A label that carries its meaning in one language often loses it in translation, so success and first clicks are compared language by language rather than pooled, and a rename is checked in both.

Checkpoint

Check: What a tree test measures

  1. Question 1 of 2 Which of these can a tree test report, given only a tree and a set of tasks?

    Choose every answer that is right.

    Show the answer: Question 1 of 2

    Answer:

    • The branch each participant opened first
    • Whether participants climbed back out of a branch
    • The share of participants who ended on an item that answers the task

    Success, directness, the first click and time on task all come from the click log. Anything that depends on layout, content or styling needs a prototype instead, which is exactly the cost a tree test avoids.

  2. Question 2 of 2 A task scores 40 % success and 68 % directness. Which description matches those two numbers?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Most participants went somewhere without hesitating, and it was the wrong branch

    Read the two together. Directness well above success is a structure that misleads rather than confuses, which is a label or a placement problem, and the first clicks say which branch did the misleading.

Two weak tasks, and the change each one asks for

Success alone tells you that a task failed. Success read against directness tells you what kind of failure it was, and both weak Ward Desk tasks come out the same way on that reading:

Task Success, directness Reading
get a birth certificate 40 %, 68 % most went straight somewhere, and it was wrong
chase a complaint 48 %, 70 % the same, with one branch taking half the room

Directness well above success is the clearer of the two diagnoses: most people did not hunt, so the structure is not so much confusing them as misleading them. The first clicks say where. For the birth certificate, 17 of 40 opened About the council and 14 opened Help, while only 8 opened Pay and apply, where the item actually sits. For chasing a complaint, 23 of 40 opened Report a problem, which is where you report a new one, and 12 opened Help, where the tracker lives.

The first clicks are where the two tasks part company, and they point at two different fixes. A card most people look for somewhere else is a placement problem: a certificate is something you apply for, so either the item moves to where people look, or the branch that attracts them gets the item too, which is what a tree test is for — the report tells you whether anyone would use the second home before you build it. A branch that people read as covering two jobs is a labelling problem, and the words come from people rather than from the organisation. The GOV.UK Service Manual makes this concrete for service names: use the words users use, describe a task rather than a technology, and prefer a verb. Renaming Help to Track a complaint or anything else is itself a change to tree-test, and the Service Manual names tree testing as a way to check whether a new name lets people navigate to the service at all.

Two rules keep this from becoming guesswork. Change one thing per round, because a rename and a move in the same test leave you unable to say which one moved the number. And keep the tasks identical between rounds, so the before-and-after numbers compare.

Key idea

Read success with directness before you redesign anything: low success with high directness is a label or a placement sending people confidently to the wrong branch, while low success with low directness means no branch gave them a reason to try it.

Structure is also an accessibility matter: the W3C’s Web Accessibility Initiative asks for navigation that is named, styled and positioned consistently across pages, for more than one route to content such as a search or a sitemap, and for orientation cues such as breadcrumbs. A tree test checks the first of those directly, because it strips everything else away. The reason any of it works at all is older than the method: Peter Pirolli and Stuart Card’s information-foraging account describes people judging the cues a link offers before they spend a click on it, which is exactly the judgement a first click records.

Checkpoint

Check: Reading two weak tasks for the fix

  1. Question 1 of 2 For the birth-certificate task, 17 of 40 opened About the council, 14 opened Help and 8 opened Pay and apply, where the item sits. What is the first change to try?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Move the item to where most people looked, or list it in both places and see whether the second home is used

    With first clicks spread over two wrong branches and only a fifth reaching the right one, the item is in the wrong place for most people. Move it, or give it a second home and let the next round show whether that home gets used.

  2. Question 2 of 2 You want to move one item and rename one branch after a tree test. How do you run the next round?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: One change per round, with the same tasks each time

    A second round is a comparison, so hold everything still except the one thing being tested. If both changes must ship together, test them in two rounds and keep the task set fixed throughout.

Practise: read a sort and a tree test for MealBox

The exercise hands you a second pair of invented datasets, both from MealBox’s account area: a similarity table for twelve cards from twenty-four participants, stored one way round as a tool exports it, and a log of six tasks run by twenty people against the account menu. Work out five pair similarities, the two tasks with the lowest success and the directness of each:

Exercise · Medium · Python

Read a card sort and a tree test for MealBox

MealBox ran two studies on its account area. Twenty-four people sorted twelve cards into piles of their own making; twenty more ran six tasks against the account menu that came out of it. Both datasets are in the starter file, and both are invented for this exercise.

Fill in three answers at the bottom of analyse.py. Work them out by hand from the data or write the code that does it — either counts.

  1. PAIR_SIMILARITY: for each of the five pairs in PAIRS, the share of the 24 participants who put those two cards in one pile, rounded to two decimals. SIMILARITY stores each pair once, in the order the cards appear in CARDS, so some of the five are in the table the other way round.
  2. WORST_TASKS: the two tasks of TREE_TEST where the smallest share of participants ended on the leaf in answer. Give their task names in a list, in any order.
  3. DIRECTNESS: for each of those two tasks, the share of participants who never left the branch they opened first. A session string lists the branches opened and then the leaf chosen, so "Deliveries>Billing>Refunds" has two branches in it and "Billing>Refunds" has one.

The sample tests accept any number within 0.01 of the data, check that the task names are two of the six, and check one more thing: on this log the two weakest tasks are weak for different reasons, so their directness is not the same.

Starter code · analyse.py

"""Read a MealBox card sort and tree test, then fill in the three answers at the end.

Twenty-four synthetic participants sorted twelve cards of MealBox's account
area; twenty more ran six tasks against the proposed account menu. All data is
invented for this exercise.

SIMILARITY holds each pair once, in the order the cards appear in CARDS, the
way a tool exports it: look a pair up in both orders. A session string is the
branches the participant opened, then the leaf they settled on, so
"Deliveries>Billing>Refunds" means they opened Deliveries, climbed back out,
opened Billing and chose Refunds.
"""

CARDS = [
    "Gift a box", "Refer a friend", "Change password", "Delete account",
    "Export my data", "Notification settings", "Add a second address",
    "Switch to weekly", "See past boxes", "Reorder a past box",
    "Claim a refund", "Report a late box",
]

SIMILARITY = {
    'Gift a box': {
        'Refer a friend': 1.00, 'Change password': 0.00, 'Delete account': 0.00, 'Export my data': 0.00,
        'Notification settings': 0.00, 'Add a second address': 0.00, 'Switch to weekly': 0.00,
        'See past boxes': 0.00, 'Reorder a past box': 0.00, 'Claim a refund': 0.00, 'Report a late box': 0.00
    },
    'Refer a friend': {
        'Change password': 0.00, 'Delete account': 0.00, 'Export my data': 0.00,
        'Notification settings': 0.00, 'Add a second address': 0.00, 'Switch to weekly': 0.00,
        'See past boxes': 0.00, 'Reorder a past box': 0.00, 'Claim a refund': 0.00, 'Report a late box': 0.00
    },
    'Change password': {
        'Delete account': 0.75, 'Export my data': 0.62, 'Notification settings': 1.00,
        'Add a second address': 0.33, 'Switch to weekly': 0.00, 'See past boxes': 0.00,
        'Reorder a past box': 0.00, 'Claim a refund': 0.00, 'Report a late box': 0.00
    },
    'Delete account': {
        'Export my data': 0.54, 'Notification settings': 0.75, 'Add a second address': 0.25,
        'Switch to weekly': 0.25, 'See past boxes': 0.00, 'Reorder a past box': 0.17, 'Claim a refund': 0.00,
        'Report a late box': 0.00
    },
    'Export my data': {
        'Notification settings': 0.62, 'Add a second address': 0.21, 'Switch to weekly': 0.00,
        'See past boxes': 0.38, 'Reorder a past box': 0.17, 'Claim a refund': 0.00, 'Report a late box': 0.00
    },
    'Notification settings': {
        'Add a second address': 0.33, 'Switch to weekly': 0.00, 'See past boxes': 0.00,
        'Reorder a past box': 0.00, 'Claim a refund': 0.00, 'Report a late box': 0.00
    },
    'Add a second address': {
        'Switch to weekly': 0.00, 'See past boxes': 0.00, 'Reorder a past box': 0.00, 'Claim a refund': 0.33,
        'Report a late box': 0.67
    },
    'Switch to weekly': {
        'See past boxes': 0.00, 'Reorder a past box': 0.67, 'Claim a refund': 0.00, 'Report a late box': 0.00
    },
    'See past boxes': {
        'Reorder a past box': 0.33, 'Claim a refund': 0.00, 'Report a late box': 0.00
    },
    'Reorder a past box': {
        'Claim a refund': 0.00, 'Report a late box': 0.00
    },
    'Claim a refund': {
        'Report a late box': 0.54
    },
    'Report a late box': {},
}

TREE_TEST = [
    {
        "task": 'switch to a weekly box',
        "answer": 'Switch to weekly',
        "sessions": [
            'Plan>Switch to weekly',
            'Plan>Cancel',
            'Plan>Switch to weekly',
            'Plan>Switch to weekly',
            'Plan>Switch to weekly',
            'Plan>Switch to weekly',
            'Account>Plan>Switch to weekly',
            'Plan>Switch to weekly',
            'Plan>Switch to weekly',
            'Plan>Switch to weekly',
            'Plan>Switch to weekly',
            'Plan>Switch to weekly',
            'Deliveries>Deliveries>Plan>Switch to weekly',
            'Plan>Switch to weekly',
            'Plan>Switch to weekly',
            'Deliveries>Plan>Switch to weekly',
            'Plan>Pause',
            'Plan>Switch to weekly',
            'Plan>Switch to weekly',
            'Plan>Switch to weekly',
        ],
    },
    {
        "task": 'get money back for a box that never came',
        "answer": 'Refunds',
        "sessions": [
            'Deliveries>Delivery window',
            'Billing>Refunds',
            'Deliveries>Billing>Refunds',
            'Deliveries>Billing>Refunds',
            'Deliveries>Delivery window',
            'Deliveries>Deliveries>Delivery window',
            'Deliveries>Billing>Refunds',
            'Deliveries>Delivery window',
            'Account>Deliveries>Account>Delete account',
            'Deliveries>Addresses',
            'Deliveries>Billing>Refunds',
            'Deliveries>Delivery window',
            'Deliveries>Deliveries>Billing>Refunds',
            'Billing>Refunds',
            'Deliveries>Deliveries>Addresses',
            'Deliveries>Addresses',
            'Billing>Refunds',
            'Deliveries>Billing>Refunds',
            'Deliveries>Late or missing box',
            'Account>Export data',
        ],
    },
    {
        "task": 'stop the emails',
        "answer": 'Notifications',
        "sessions": [
            'Deliveries>Delivery window',
            'Account>Notifications',
            'Account>Notifications',
            'Account>Notifications',
            'Account>Notifications',
            'Account>Notifications',
            'Account>Notifications',
            'Plan>Plan>Cancel',
            'Account>Notifications',
            'Account>Notifications',
            'Plan>Account>Delete account',
            'Account>Notifications',
            'Account>Notifications',
            'Account>Notifications',
            'Account>Notifications',
            'Account>Notifications',
            'Account>Notifications',
            'Account>Notifications',
            'Account>Notifications',
            'Plan>Pause',
        ],
    },
    {
        "task": "download last month's invoice",
        "answer": 'Invoices',
        "sessions": [
            'Plan>Cancel',
            'Plan>Billing>Invoices',
            'Billing>Invoices',
            'Billing>Invoices',
            'Billing>Refunds',
            'Billing>Invoices',
            'Billing>Invoices',
            'Billing>Invoices',
            'Billing>Invoices',
            'Billing>Invoices',
            'Billing>Invoices',
            'Billing>Invoices',
            'Billing>Invoices',
            'Billing>Invoices',
            'Billing>Invoices',
            'Billing>Invoices',
            'Billing>Invoices',
            'Billing>Invoices',
            'Billing>Invoices',
            'Billing>Invoices',
        ],
    },
    {
        "task": 'give a box to a friend as a present',
        "answer": 'Gift a box',
        "sessions": [
            'Plan>Rewards>Gift a box',
            'Plan>Plan>Pause',
            'Account>Rewards>Gift a box',
            'Plan>Cancel',
            'Plan>Deliveries>Addresses',
            'Deliveries>Plan>Deliveries>Addresses',
            'Plan>Cancel',
            'Plan>Rewards>Gift a box',
            'Account>Rewards>Gift a box',
            'Plan>Pause',
            'Plan>Plan>Switch to weekly',
            'Plan>Plan>Rewards>Gift a box',
            'Deliveries>Rewards>Gift a box',
            'Plan>Plan>Pause',
            'Deliveries>Late or missing box',
            'Rewards>Gift a box',
            'Plan>Deliveries>Addresses',
            'Plan>Deliveries>Rewards>Gift a box',
            'Rewards>Gift a box',
            'Deliveries>Plan>Cancel',
        ],
    },
    {
        "task": 'change the day the box arrives',
        "answer": 'Delivery window',
        "sessions": [
            'Account>Export data',
            'Deliveries>Delivery window',
            'Deliveries>Delivery window',
            'Deliveries>Delivery window',
            'Deliveries>Delivery window',
            'Deliveries>Delivery window',
            'Deliveries>Delivery window',
            'Deliveries>Delivery window',
            'Deliveries>Delivery window',
            'Account>Export data',
            'Deliveries>Delivery window',
            'Deliveries>Delivery window',
            'Plan>Cancel',
            'Plan>Deliveries>Delivery window',
            'Deliveries>Delivery window',
            'Deliveries>Delivery window',
            'Deliveries>Delivery window',
            'Deliveries>Delivery window',
            'Account>Export data',
            'Deliveries>Delivery window',
        ],
    },
]

PAIRS = [
    ("Export my data", "Delete account"),
    ("Add a second address", "Report a late box"),
    ("Switch to weekly", "Reorder a past box"),
    ("Export my data", "See past boxes"),
    ("Notification settings", "Add a second address"),
]

# ---- your answers ----------------------------------------------------------

# 1. For each pair in PAIRS, the share of the 24 participants who put the two
#    cards in one pile, rounded to two decimals: {("card a", "card b"): 0.42}.
PAIR_SIMILARITY = {}

# 2. The two tasks of TREE_TEST with the lowest success, as a list of their
#    task names in any order.
WORST_TASKS = []

# 3. For each of those two tasks, the share of participants who never left the
#    branch they opened first: {"task name": 0.42}.
DIRECTNESS = {}
The sample tests · test_analyse.py
from analyse import CARDS, DIRECTNESS, PAIR_SIMILARITY, PAIRS, SIMILARITY, TREE_TEST, WORST_TASKS

TOLERANCE = 0.01


def stored(a, b):
    """The similarity of a pair, whichever way round it was asked for."""
    if b in SIMILARITY.get(a, {}):
        return SIMILARITY[a][b]
    return SIMILARITY[b][a]


def success(task):
    hits = sum(s.split(">")[-1] == task["answer"] for s in task["sessions"])
    return hits / len(task["sessions"])


def directness(task):
    straight = sum(len(s.split(">")) == 2 for s in task["sessions"])
    return straight / len(task["sessions"])


def test_every_pair_is_answered_with_its_similarity():
    """the five pairs of PAIRS carry the share of participants who grouped them"""
    assert set(PAIR_SIMILARITY) == set(PAIRS), "answer every pair of PAIRS, and only those pairs"
    for pair in PAIRS:
        for card in pair:
            assert card in CARDS, f"{card!r} is not one of the twelve cards"
        got, want = PAIR_SIMILARITY[pair], stored(*pair)
        assert isinstance(got, float), f"{pair}: give a share such as 0.42, not {got!r}"
        assert abs(got - want) <= TOLERANCE, f"{pair}: {got} is not within 0.01 of the table"


def test_the_two_weakest_tasks_are_named():
    """WORST_TASKS holds the two task names with the lowest success"""
    names = [t["task"] for t in TREE_TEST]
    assert len(WORST_TASKS) == 2, "name exactly two tasks"
    for name in WORST_TASKS:
        assert name in names, f"{name!r} is not one of the six tasks"
    ranked = sorted(TREE_TEST, key=success)
    assert set(WORST_TASKS) == {ranked[0]["task"], ranked[1]["task"]}, (
        "these are not the two tasks with the lowest share of participants on the right leaf"
    )


def test_directness_is_given_for_those_two_tasks():
    """DIRECTNESS holds the share who never left the branch they opened first"""
    assert set(DIRECTNESS) == set(WORST_TASKS), "give the directness of the two tasks you named"
    by_name = {t["task"]: t for t in TREE_TEST}
    for name, got in DIRECTNESS.items():
        want = directness(by_name[name])
        assert isinstance(got, float), f"{name!r}: give a share such as 0.42, not {got!r}"
        assert abs(got - want) <= TOLERANCE, f"{name!r}: {got} is not within 0.01 of the log"


def test_success_and_directness_disagree_on_one_task():
    """the weaker pair differ: one task was hunted for, the other walked into confidently"""
    picked = sorted(DIRECTNESS.values())
    assert len(picked) == 2 and picked[1] - picked[0] >= 0.10, (
        "on this log the two weakest tasks differ in directness by 0.10 or more; check both shares"
    )
A hint

Start with the lookup, because two of the five pairs are stored the other way round:

def stored(a, b):
    if b in SIMILARITY.get(a, {}):
        return SIMILARITY[a][b]
    return SIMILARITY[b][a]

For a task, success counts the sessions whose last step is the leaf in answer, and directness counts the sessions with one branch before that leaf. Splitting a session on ">" gives you both: the last element is the leaf, and a direct session has exactly two elements.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Once its tests pass, the self-check below starts with whichever idea you have answered least well:

Practice that adapts to you

Practise this lesson

11 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 11 What does an open card sort collect that a closed sort cannot?

    Choose one answer.

    Show the answer: Question 1 of 11

    Answer: The groups participants make and the names they give them

    An open sort is generative: it can produce a grouping and a label nobody on the team proposed. A closed sort can only report how well the given categories absorbed the cards.

  2. Question 2 of 11 A team runs a closed sort on its six categories, and 84 % of cards land where the team expected. A month later, users still cannot find two features. What did the sort fail to show?

    Choose one answer.

    Show the answer: Question 2 of 11

    Answer: That a category the team never listed was needed

    Every card had to go somewhere, so a category that should have existed stayed invisible and the ones offered filled up. An open or hybrid sort, or a tree test of the structure, would have let the gap show.

  3. Question 3 of 11 Why does a card sort need about three times as many participants as a usability test of the same product?

    Choose one answer.

    Show the answer: Question 3 of 11

    Answer: It generates a structure from varied mental models rather than evaluating one that exists

    The split is between evaluative and generative work. Evaluating needs few people because one failure is a finding; generating a structure needs enough people for the agreement between them to settle.

  4. Question 4 of 11 Going from fifteen to thirty participants moves the correlation from 0.90 to 0.95. By how much does the correlation rise, as a decimal?

    Type a number.

    A hint

    Subtract one reported correlation from the other.

    Show the answer: Question 4 of 11

    Answer: 0.05 (anything from 0.049 to 0.051 counts)

    The second fifteen participants buy 0.05, which is why most projects stop at fifteen and only a structure with a great deal riding on it pays for thirty.

  5. Question 5 of 11 Twenty participants sorted the cards and fifteen of them put Pause plan and Cancel plan in one pile. What is the similarity of that pair, as a decimal?

    Type a number.

    Show the answer: Question 5 of 11

    Answer: 0.75 (anything from 0.749 to 0.751 counts)

    Similarity is the share of participants who grouped the pair, so 15 divided by 20 is 0.75. The five who split them are the reason the pair is worth a tree test.

  6. Question 6 of 11 In average-linkage clustering, which two groups are joined at each step?

    Choose one answer.

    Show the answer: Question 6 of 11

    Answer: The two whose average distance across every pair of their cards is smallest

    Distance is 1 minus similarity, and at each step the pair of groups with the smallest average distance is joined, at a height equal to that distance. Those heights are what you later cut through.

  7. Question 7 of 11 The five-group cut puts Skip one day with the two delivery cards rather than with Pause plan and Cancel plan. How should that be treated?

    Choose one answer.

    Show the answer: Question 7 of 11

    Answer: As a claim to put to new people in a tree test, since four participants decided it

    A contested card is the most useful thing a sort produces: it names the decision the structure has to make. Write both options into trees and let a tree test say which one people can use.

  8. Question 8 of 11 When is a tree test the right next step?

    Choose one answer.

    Show the answer: Question 8 of 11

    Answer: When a structure is named end to end but nothing has been designed or written

    The method's whole advantage is its timing: a tree is a spreadsheet, so several structures can be compared and discarded before anyone spends a day on design or copy.

  9. Question 9 of 11 A category in your menu has its own landing page that answers one of the tasks, but the tool will not let you mark a category as a correct answer. What do you do?

    Choose one answer.

    Show the answer: Question 9 of 11

    Answer: Say so when reporting, and read that task's first clicks and paths instead of its success score

    Most tree-testing tools treat the tree the way a file manager treats folders, so only a bottom-level item can be an answer. Name the gap in the report and lean on first clicks and paths for that task.

  10. Question 10 of 11 Two tasks failed. On one, 57 % of first clicks went to a single wrong branch; on the other, first clicks were spread almost evenly over four branches. Which failure is easier to fix, and why?

    Choose one answer.

    Show the answer: Question 10 of 11

    Answer: The first, because the data names the branch people expect and the change is a move or a second home

    A concentrated wrong first click tells you where people look, which is a change you can make and retest. A flat spread says the categories give no scent at all, which usually sends you back to the sort.

  11. Question 11 of 11 Where should the new wording for a branch people misread come from?

    Choose one answer.

    Show the answer: Question 11 of 11

    Answer: From the words users use, taken from research, pile names and search terms

    Labels come from people's vocabulary, and the Service Manual adds two useful rules for service names: describe the task rather than the technology, and prefer a verb. Then tree-test the new name against the same tasks.

Interview questions

Warm-up (fresher to 2–5 years): when would you reach for an open sort, and when for a closed one?

An open sort when you do not yet have a structure, or when you need the words people use for these groups, because only an open sort collects pile names in their own vocabulary and only an open sort can surface a category you never thought of. A closed sort when a structure exists, named end to end, and the question is narrower: do cards land where we put them, and which cards do not. Two practical notes make the answer stronger. A closed sort is much faster to analyse, since the counts fall straight out, while an open sort needs the pile names people wrote coded first, and that coding is the work. And a closed sort leads people, because every card has to go somewhere, so a category that should not exist still fills up. A hybrid sits between them: your categories, plus room for new ones, which is a reasonable default when a structure exists but you suspect a gap in it.

The two questions below go further: one on sample size when a stakeholder quotes the wrong rule of thumb, the other on a tree test with two tasks that failed. Sketch the first two sentences of your own answer to each, then open the question to compare it with the first step given there:

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Mid to senior

    A stakeholder cuts your card-sort recruitment from 15 people to 5, saying "Nielsen showed five users are enough". What does the sample-size evidence for card sorting actually say, and when is five genuinely enough?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to staff

    A Ward Desk tree test of its payments area gives 8 tasks and 40 participants. Two tasks come in under 50 % success with directness under 30 %. The lead wants to announce a restructure this week. What do you do first, and how do you choose which change to try?

    Model answer, Pro. First step and follow-ups

Flashcards · 8 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. What can open, closed and hybrid card sorts each tell you?

    An open sort generates groupings and vocabulary you did not have; a closed sort only reports how well your own named categories absorb the cards; a hybrid sort gives your categories and allows new ones, trading some discovery for much less coding work.

  2. 80 % of cards in a closed sort landed where the team put them. Does that prove the menu is good?

    No. It shows people can place cards into the categories you named, not that they can find things in the structure. Propose a structure from a sort, then test findability with a tree test.

  3. How many participants does a card sort need, by Tullis and Wood's evidence?

    More than a usability test: the correlation with the full 168-person result was 0.75 at 5 participants, 0.90 at 15 and 0.95 at 30. Fifteen is the usual stopping point; Tullis and Wood recommended 20 to 30.

  4. What does a card sort's similarity matrix hold?

    One number per pair of cards: the share of participants who grouped them together. It is the only quantitative thing a sort produces.

  5. Is a cut through a card-sort dendrogram a structure you can ship?

    No, it is a hypothesis. Average-linkage clustering turns the matrix into a tree, and every height you cut it at gives a different structure, which must survive a test with new people and real tasks before it becomes a menu.

  6. What do task success, directness and the first click measure in a tree test?

    Success is the share who ended on an item that answers the task; directness is the share who never climbed back out of a branch they opened, a proxy for confidence; the first click is the branch each participant opened first. Time on task is a fourth measure.

  7. Why must a tree-test task not contain the category's own label?

    Participants then match words instead of finding things, and the task passes whatever the structure does. Describe the situation instead and let them choose.

  8. A tree-test task has low success but high directness. What does that tell you?

    A label or a placement is confidently sending people to the wrong branch. Low success with low directness means instead that no branch gave them a reason to try it.

Key takeaways

  • An open sort generates groupings and vocabulary you did not have, a closed sort only reports how well your own categories absorb the cards, and a hybrid sort trades some of the first for much less coding work.
  • Five participants settle a usability test but not a sort: the correlation with a full result is 0.75 at five participants, 0.90 at fifteen and 0.95 at thirty, so fifteen is the usual stopping point.
  • The similarity matrix holds one number per pair of cards, the share of participants who grouped them, and it is the only quantitative thing a sort produces.
  • Average-linkage clustering turns that matrix into a tree, and every height you cut it at gives a different structure, so a cut is a hypothesis to test rather than an answer.
  • A tree test scores that hypothesis on bare categories with no visual design, and success read together with directness and the first clicks tells you whether to move an item, rename a branch, or list the item twice.

Terms in this lesson

card sort
A study in which participants group content items written on cards, and often name each group, so that a structure can be proposed from how people think the content belongs together.
closed card sort
A card sort in which the categories are given to participants already named, so the study reports how well those categories absorb the cards rather than what groups people would make.
dendrogram
The tree hierarchical clustering produces, with each join drawn at the distance at which it happened, so cutting it at a chosen height gives a set of groups.
directness
In a tree test, the share of participants who reached their answer without climbing back out of a branch they had opened, read as a sign of how confident the path felt.
first click
The branch or link a participant opens first in a task, counted across participants to show where a structure sends people before anything else happens.
hierarchical clustering
Building a tree of groups by repeatedly joining the two closest groups; average linkage measures closeness as the mean distance over every pair of their members.
open card sort
A card sort in which participants are given the cards only: they make as many groups as they like and name each group in their own words.
similarity matrix
A table holding one number per pair of cards in a sort: the share of participants who put those two cards in the same group, from 0 for none to 1 for all of them.
task success
In a tree test, the share of participants who finished a task on an item that answers it; reported per task rather than as one figure for the study.
tree test
A study that evaluates a category structure by asking participants to find where given things would live in it, shown as bare expanding lists with no visual design or page content.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.