gantt
Two people are looking at the same schedule and talking about different tasks. One means the survey work in the second phase, the other means the survey line item on the invoice, and both are calling it the survey. Somebody suggests numbering everything, which is correct, and then the numbering is done in a way that renumbers itself the first time a task is dragged, which puts the team back where it started with more confidence and worse data.
A coded work breakdown structure exists to make that conversation impossible. The number is the name, it is written on documents that leave the tool, and it has to mean the same thing in March that it meant in January. Most of the difficulty is in that last requirement, and almost none of it is in choosing a format.
The structure comes first. A work breakdown structure covers, in the standard formulation, 100 percent of the work defined by the project scope and captures all deliverables, with nothing outside that scope included. Elements are meant to be defined in terms of outcomes or results rather than actions, which is why Foundation complete reads better as a WBS element than Pour foundation.
The code is the handle on each of those elements. Elements are numbered sequentially so the hierarchy is visible in the number itself, so that 1.1.2 Propulsion is recognisably a third level element from its three digits and two separators alone. The point of doing this, as the convention itself explains, is that the element can then be referred to in any written context, including progress tracking, scheduling and billing.
That last word is the reason stability matters more than elegance. Once 3.2.4 appears on a purchase order, a drawing register, a subcontractor's invoice or a change request, the code has left the tool and is being used as an identifier by people who cannot see the schedule. A code that renumbers itself when the plan is reorganised is not an identifier, it is a position, and the two are being confused constantly.
On depth, the usual guidance is that two to four levels suffice, with the lowest level bounded by one of three rules of thumb: no activity longer than 80 hours of effort, no activity longer than one reporting period, or plain common sense about what is worth tracking separately. Deeper structures are usually a sign that someone is listing actions rather than results.
Microsoft Project carries both, and conflating them causes most of the trouble.
Basic outline numbers are positional and automatic. They change automatically when a task moves up or down in the list and when a task is indented or outdented, they consist of numbers only, and they cannot be edited. They are a perfect description of where a row currently sits and a terrible identifier, because moving one task changes the labels on its neighbours.
WBS codes are outline numbers that can be shaped and edited to match how a business actually refers to work. They are defined through a mask, and once assigned they are not rewritten by ordinary reorganisation. Project's own behaviour makes the distinction explicit: when tasks are moved or deleted, it does not renumber the WBS codes automatically. Renumbering happens only when someone chooses to do it, for selected tasks or for the entire project.
Read in isolation, the fact that codes do not follow the structure looks like a defect. It is the feature. A code that stops matching its position is telling the truth about a plan that was reorganised, and the moment it is renumbered to look tidy, every document outside the tool that cited the old number becomes wrong without anyone editing it.
In Project, the definition lives under the Project tab, in the Properties group, through WBS and then Define Code. Three decisions fill the dialog.
The project code prefix sits in front of every code in the plan, and it accepts numbers, uppercase and lowercase letters, and symbols. This is where a short project identifier belongs, so that a code quoted in an email is unambiguous across concurrent projects. Teams running several jobs for the same client get more value from this one field than from anything else in the dialog.
The sequence per level decides the character type: numbers in order, uppercase letters in order, lowercase letters in order, or unordered characters. Mixing types across levels is the main readability trick available. A scheme where the first level is a letter and the deeper levels are numbers, giving A.1.3, makes the top level phase recognisable at a glance without reading the whole code.
The length and separator per level decide how the code sorts and how it reads. Length can be a fixed number of characters or Any. Fixed length is the right default despite being more restrictive, because two digits at every level means codes sort correctly as text everywhere they are pasted, including spreadsheets, file names and document registers. With Any, 1.10 sorts before 1.2 in any alphabetical list, which will eventually appear in a document nobody can correct. The separator defaults to a period and can be any character, and the only real advice is to pick one that survives being pasted into other systems.
Two checkboxes in the same dialog change the behaviour more than the mask does. One controls whether a WBS code is generated for each new task, and turning it off means new work arrives uncoded, which is appropriate when codes are assigned by a planner rather than typed by whoever created the row. The other verifies uniqueness of new codes, and unchecking it permits the same code on multiple tasks. There is almost never a good reason to allow that, since a duplicated identifier defeats the entire purpose, and it is worth checking that it has not been switched off in an inherited template.
The pressure to renumber comes from tidiness. A phase was cancelled, so there is a gap. Two tasks were merged, so a number is unused. Somebody wants the codes to run in a clean sequence again.
Renumbering satisfies that urge and breaks every external reference at once. The safer discipline has three parts.
Leave gaps on purpose. Numbering the first pass in tens at each level, so the children run 10, 20, 30, leaves room for insertions that fit the structure without disturbing anything. This costs nothing and removes most future pressure to renumber.
Retire codes, never reuse them. A cancelled element keeps its code and is marked cancelled. Reassigning 2.3 to different work is the single most damaging thing possible to a coded structure, because documents citing the old 2.3 remain legible and are now wrong.
Decide who may renumber, and when. In practice the answer should be that a whole project renumber happens between baselines or not at all, and that it is announced. Where a plan genuinely has to be restructured, the professional move is to publish a mapping from old codes to new ones alongside the change, rather than expecting readers to work it out.
Most task and board tools have no WBS code field at all, and most teams outside heavy construction and defence are working in one of them. The structure still needs codes if invoices and documents reference tasks, so the code has to be carried in a field that does exist.
A prefix in the task title is the crudest option and often the best one. Every list, every notification, every mobile view and every export shows the title, so the code travels everywhere without configuration. Pad the numbers, so that A.01.03 sorts correctly and lines up visually, and keep the prefix short enough that the title is still readable in a narrow column.
A dedicated custom field is cleaner and more limited. It keeps titles readable, it can be grouped and filtered, and it usually does not appear in notifications or in mobile summaries, so the code is invisible exactly when somebody is reading quickly. Tools where estimated hours, priority and client name already sit on the card as custom fields handle a WBS code the same way, and a text field sorts correctly as long as the padding is consistent.
Both is the arrangement that actually works in teams doing external billing: the code in the title for visibility, the same code in a field for filtering and reporting. The duplication is a maintenance cost and it is smaller than the cost of a code nobody can see.
What does not work is relying on the tool's own task identifiers. Automatic numbers are allocated in creation order, they carry no hierarchy, they cannot be chosen to match a cost account, and they are frequently renumbered or re-scoped when work is moved between projects. They identify a record in a database, which is a different job.
| Approach | Stable when work is reorganised | Sorts correctly | Usable outside the tool | Setup effort |
|---|---|---|---|---|
| WBS codes with a custom mask | Yes, renumbering is a deliberate action | Yes, with fixed lengths | Yes, this is the purpose | An hour, plus a convention to maintain |
| Basic outline numbers | No, they follow position automatically | Yes | Only as a snapshot | None |
| Padded code as a title prefix | Yes, it is typed and stays typed | Yes, if padded consistently | Yes, it appears everywhere | Low, plus discipline on new tasks |
| Code in a custom field | Yes | Yes, if padded consistently | Only where the field is shown | Low |
| Tool generated task identifiers | No, they are database keys | Not hierarchically | Poorly, they carry no structure | None |
The honest counterweight is that a coded WBS is overhead, and for a lot of teams it is overhead with no return. If nothing outside the tool ever refers to a task, if there is no cost account to map to, and if the team is small enough that pointing at a card in a shared view settles any ambiguity, then the codes are ceremony. Subtasks under a parent already express the hierarchy, and a board where the Gantt view and the card list are the same records makes the structure visible without any numbering at all.
The threshold is external reference. The moment a number has to appear on an invoice, a drawing, a permit, a grant report or a subcontractor's scope, codes stop being ceremony and start being the cheapest possible way to prevent an expensive misunderstanding.
Decide whether anything outside the tool needs to cite a task. If nothing does, drop the numbering and use the parent and child structure the tool already gives. If something does, write down the mask, including the prefix, the character type and the fixed length at each level, number the first pass in tens to leave room, and agree in one sentence that codes are retired rather than reused. Where the work sits on boards rather than in a desktop scheduler, carry the code in the title and in a field on the same cards the team already works from, and confirm the plan limits for custom fields before designing a scheme that depends on them.
An outline number describes a task's current position in the list. It is generated automatically, consists of numbers only, cannot be edited, and changes as soon as a task is moved, indented or outdented. A WBS code is assigned rather than derived, can be shaped with a prefix and a custom character scheme, and is not rewritten when the plan is reorganised, which is what makes it usable as an identifier on documents.
Only deliberately, and rarely. Microsoft Project does not renumber automatically when tasks are moved or deleted, and that behaviour protects every document that already cites a code. Numbering the first pass in tens leaves room for insertions, and a cancelled element should keep its code and be marked cancelled rather than have the number reassigned to different work.
Two to four levels covers most projects. The lowest level is bounded by practical rules of thumb, commonly that no activity exceeds 80 hours of effort, or that no activity runs longer than one reporting period. Structures deeper than that are usually listing actions rather than outcomes, which also breaks the principle that elements should be defined as results.
Because codes get pasted into places that sort text alphabetically, including spreadsheets, file names and document registers. Without padding, 1.10 sorts before 1.2 and the error surfaces in a document that is hard to correct after the fact. Setting a fixed length for each level in the code mask, rather than allowing any length, enforces this at the point the code is created.
By carrying the code in fields that do exist. A padded prefix in the task title makes the code visible in every list, notification and export, and a custom text field holding the same code allows grouping and filtering. Relying on the tool's own automatically generated task identifiers does not work, because those are creation-order database keys with no hierarchy and no relationship to a cost account.