Grading systems
Climbing has no single grading system. It has about eight, they are used in different countries for different kinds of climbing, and they are not interchangeable.
This page explains what we store, what an approximate conversion is worth, which conversions we
refuse outright, and what the normalised gradeBand is genuinely for.
The systems we support
| System | Used for | Where | Looks like |
|---|---|---|---|
| French | Sport | Europe, most of the world | 6a, 7b+, 9a |
| UK trad | Trad | UK and Ireland | HVS 5a, E2 5c |
| YDS | Sport and trad | USA | 5.10a, 5.13d |
| UIAA | Sport and trad | Germany, Austria, Italy | VI+, IX- |
| Font | Bouldering | Europe | 6A, 7B+, 8C |
| V scale | Bouldering | USA | V0, V7, V15 |
| Aid | Aid climbing | Everywhere | A0–A5, C1–C5 |
| Ice | Ice and mixed | Everywhere | WI3, WI6 |
Enum: GradeSystem in packages/shared/src/grades.ts.
UK trad grades have two parts, and both are required
E2 5c is not one grade. It is two:
E2— the adjectival grade. Overall seriousness: how hard, how sustained, how good the protection is, how far you might fall. This is the number that tells you whether the route is dangerous.5c— the technical grade. How hard the single hardest move is.
The relationship between them carries the information. E1 5a is a route with poor protection where
the moves are easy — bold and frightening. E1 5c is well-protected but physically hard. Same
adjectival grade; completely different day out.
A UK trad grade recorded without its technical part is incomplete data, not merely terse (ADR-0010). It is also the half that makes conversion possible at all — see below.
The adjectival ladder, roughly:
M · D · VD · HVD · S · HS · VS · HVS · E1 · E2 · E3 … E11
A route displays the grade it was published in
Always. Route.grade is a string, stored exactly as the guidebook has it, and it is what the screen
shows. That is principle 3 and it is not negotiable.
What sits next to it is the subject of the rest of this page.
Approximate conversion, and what it is worth
We show a rough conversion alongside the published grade, computed when the screen renders and never stored (ADR-0010).
The reasoning is not that conversions are good. It is that a British climber on their first trip to Spain is going to convert the grade anyway — in their head, from memory, or from a table on a forum — and refusing to help does not prevent the conversion. It only means it happens somewhere nobody can label with an error bar.
So the conversion comes with its uncertainty attached:
- Marked approximate, never as an equality.
≈ 6b, not= 6b. - Shown as a range where the mapping genuinely spans more than one grade, because pretending to a precision the consensus does not have is exactly the failure being avoided.
- Always secondary. The published grade is present, primary, and never replaced.
- Opt-in, through
User.preferredGradeSystems. - Never persisted. No converted value is written to a column, synced, or indexed. When the mapping improves, every screen improves at once, and there is no stale conversion in a database waiting to be mistaken for a published grade.
The systems measure different things
This is why the conversion is approximate rather than merely imprecise:
- Font weights how hard the moves feel; the V scale historically weighted the crux move. Related, not equivalent.
- UK trad encodes danger. Nothing else does. There is no French grade for "how likely am I to deck out".
- YDS and French are close in spirit but drift apart at the top end and disagree by region.
A conversion between two of these is a translation between languages that have different words for different things, not a unit change.
Conversions we refuse
These return nothing rather than a guess:
| Refused | Because |
|---|---|
| Boulder problem ↔ roped route | Different activities. A 7A problem and a 7a route share nothing but characters |
| Anything → a UK adjectival grade | The adjectival grade encodes seriousness and protection. No other system measures it, so there is nothing to convert from |
| Free ↔ aid, and free ↔ ice or mixed | Different activities again |
| An unparseable grade | normaliseGrade returns null rather than 0, and a conversion inherits that |
UK trad converts through the technical grade, because that is the only half with a counterpart elsewhere. The adjectival grade travels alongside as seriousness — it is never dropped and never converted.
So what is gradeBand?
A 0–100 integer, computed by normaliseGrade(), stored alongside the published grade.
It exists for one job: filtering and sorting across mixed systems. "Show me everything between f6a and f7a at this crag" has to work even when one sector is graded in UK trad, and a single sortable integer is the only sane way to express that in SQL.
It is never displayed. Not in a tooltip, not in small grey text, not as an "≈" — that is what the
read-time conversion above is for, and it is a different calculation with a different purpose. If
you find yourself rendering gradeBand, something has gone wrong.
It is lossy on purpose. The comment in grades.ts says so:
This is deliberately coarse. It is NOT an assertion that V4 and 6C are the same climb.
:::warning Two normalisations, two jobs
For UK trad, gradeBand derives from the adjectival grade, because seriousness is what a "can I
safely get on this" filter should reflect. Display conversion derives from the technical grade,
because that is what has a counterpart in other systems.
They are different functions answering different questions. Anyone who merges them into one will silently break either filtering or display, and the breakage will look plausible. :::
It returns null, not zero
export function normaliseGrade(system: GradeSystem, grade: string): NormalisedGrade | null;
An unparseable grade — "project", "?", an empty string — returns null. Never 0.
A misparsed grade defaulting to 0 would sort as "easiest", so a filter for easy routes would
surface every route we could not parse. That is exactly the wrong failure direction in a
safety-adjacent filter, and it is the kind of bug that looks like it works.
Callers must handle null rather than defaulting it.
Stars are not a grade
Route.stars, 0–3. Quality, not difficulty. The guidebook convention:
| 0 | Worth doing if you are here |
| ★ | Good |
| ★★ | Very good |
| ★★★ | Do this one |
Rendered with SF Symbols. Zero stars renders nothing rather than three empty outlines — an unrated route and a bad route are different claims and should not look the same.
Which system to show when there is a choice
User.preferredGradeSystems is a per-route-type map: sport in French, trad in UK, bouldering in
Font, say. defaultGradeSystems in grades.ts has the conventional order per route type.
This chooses which published grade is primary when a route genuinely carries more than one, and which system any approximate conversion is offered in. It never rewrites one into another.
Alpine routes carry several grades at once
A route in the Alps may have an overall alpine grade (PD, AD, D, TD, ED) and a rock
grade and an ice grade and a mixed grade, all at the same time and all meaning different things.
The overall grade is a statement about commitment, length and seriousness; the rest describe
individual difficulties.
None of them convert into any of the others, and a route needs to be able to hold all of them. That is roadmap phase 2, and the storage shape is an open decision.
Consensus grades
Ascent.suggestedGrade and suggestedStars are collected from day one, even though nothing uses
them yet.
The eventual feature — showing what climbers actually think of a route's grade next to the published one — is roadmap phase 7. The data is collected now because it is cheap to collect and impossible to backfill.
When it ships, the consensus is shown alongside the published grade, never replacing it. Same reason as everything else on this page.
Related
- ADR-0010 — the decision this page implements
- Data model — why grades are stored twice
- Glossary — onsight, redpoint, and the rest
- Product principles — principle 3