Skip to main content

Designing a Work Item Engine: A Blueprint for Kindness in Product Craft

How a project management tool's meta-model mirrors kindness: defining types, fields, workflows, and scopes to create systems that respect people's time and effort.

The Quiet Demand Behind a Simple Request

In the middle of building a project management tool, a request arrives: "When a defect moves from 'Pending Verification' to 'Closed,' can we require the tester to fill in a verification result?"

It sounds like a small tweak, but it's not. It's a plea for clarity—a way to make sure that when someone closes a bug, they're not just clicking a button. They're saying something meaningful: "I verified it works," "I couldn't reproduce it," or "We decided not to fix it."

That's kindness in product design: giving people the structure to communicate what matters, without forcing them to jump through hoops that don't.

But here's the twist: if you swap "verification result" for "root cause," the rule falls apart. Root cause analysis often happens before a fix is even submitted, and for low-risk bugs, it might be overkill. So the right design is: require the verification result on close, but only require a root cause if the severity is high enough.

Suddenly, you're not just adding a field. You're designing an entire system that understands the difference between a mandatory input and a conditional quality gate.

Six Questions Every Work Item Must Answer

At its core, a work item platform has to answer six questions for every task, bug, or story:

  • What is this object? (Type identity)
  • What can it store? (Attributes)
  • How do people interact with it in different contexts? (Layouts and forms)
  • How is it allowed to change? (Workflow)
  • How does it connect to other objects? (Relationships)
  • Where do these rules apply? (Configuration scope)

These six layers can't be mashed into one giant configuration table. They change at different speeds and serve different purposes. A type identifies the object; an attribute states a fact; a layout organizes interaction; a workflow constrains change; a relationship links objects; and scope determines who's affected.

Why split them? Because if you bake everything into the type, you end up with dozens of duplicated configs. If you make everything standalone, you get a maze only experts can navigate. The art is balancing reuse, isolation, and understanding.

Type Defines Identity, Not Defaults

When you create a new work item type in most tools, you just pick a name and an icon. That makes it easy to think of types as mere labels. But a type is the stable identity of a class of work—and the entry point for all other configuration.

Here's a boundary that matters: a type should not own a default assignee.

Why? Because the assignee is a property on the instance, not on the type. Different teams might want different defaults. The product team might default to the creator; the delivery team might default to the business line owner; a support team might not even need an assignee. If you hardcode that into the type, you'll need three separate "requirement" types just to handle minor variations.

Also, default values are just computed results at creation time. If the default person leaves, the system should recalculate, not change the type definition.

So the model is simple: type answers "what is this?" The assignee attribute answers "who's responsible now?" And the attribute rules answer "who can be assigned, and what's the default?"

From Fields to a Unified Attribute Model

Early project tools called these "custom fields." That name is misleading. A field suggests a text box or a date picker. Real work items are richer: an assignee is a person object, attachments come with metadata, effort includes estimates and logs, and a parent link is a reference to another object.

So I prefer to think of it as an attribute model. The idea: everything is an attribute, and attributes compose forms.

Attributes aren't just about input. They drive filtering, grouping, statistics, permissions, imports, and automation. The control is just the UI; the attribute is the underlying fact.

A proper attribute definition includes a stable ID, a data type, value structure, cardinality, default rules, validation, applicable scopes, query capabilities, and history. And here's a painful lesson: once you have data, you can't casually change an attribute's type. Turning a free-text priority into a single-select requires mapping old values like "high," "urgent," and "P0." Changing effort from hours to person-days needs a conversion rate. Changing a single-select to a person field? Good luck.

Scope: Global vs. Space Attributes

Many platforms have "global fields" and "project fields." But if you've already adopted the concept of a Space as a long-lived container, then having both is redundant. The model should be simpler: organizations define shared, cross-space attributes; spaces define their own specialized ones.

So you have two layers of configuration, plus the actual runtime data. The governance principle: the organization maintains the common language, the space maintains the business language, and the work item instance just stores the current facts.

How do you know if two attributes can be shared? Ask: can their values be compared in a cross-space report? If one team's "customer" means the contracting entity and another's means the end user, they shouldn't share an ID just because they share a name.

Layouts: Not a Table, But a Scenario

Once you have a unified attribute library, layouts don't define data—they define what users see and do in each context.

Take closing a defect. On creation, you don't want verification result or root cause to block the user. During analysis, you might fill in root cause and fix plan. On the transition from "Pending Verification" to "Closed," the form requires a verification result. If severity is critical, the system checks that a root cause exists. After closing, those fields are read-only for most users.

There are at least five layout types: new, detail, transition, list, and card. The new layout should be minimal—just enough to establish the object and route it correctly. If a field like "business line" determines the workflow, it must appear at creation. But don't ask for root cause before it's even possible to know it.

The detail page should let users quickly judge what's going on: status, assignee, next actions—then dive into descriptions, acceptance criteria, and related items. And remember: the layout is not the source of truth. Server-side validation must enforce mandatory rules, because imports, APIs, and automation can bypass the UI.

Workflow: States and Steps

A workflow isn't just a list of statuses. States are stable phases; steps are constrained actions that move an item from one state to another.

"Pending Verification" and "Closed" are states. "Verify and close" is a step. That step can require a tester role, open a transition form, collect the verification result, check for root cause on critical bugs, update the closed-by fields, and write an audit log.

A step definition includes start and end states, the action name, who can execute it, what fields to show, preconditions, post-actions, and triggers.

Different products handle multiple workflows differently. Jira maps types to workflows via schemes. ONES uses a default workflow per type. Feishu (Lark) routes based on a business line field. TAPD lets categories choose workflows, and those workflows can be serial or parallel.

The key insight: separate the configuration of candidate workflows, the selection logic at creation time, and whether the selected workflow supports parallel nodes. And always store the workflow version on the instance—otherwise, changing the config breaks running work items.

Relationships: More Than Lines on a Screen

Work items aren't islands. A requirement splits into frontend, backend, and QA tasks. A backend task might block another. A defect links to a test case and a release.

You need at least two relationship types: hierarchy (parent-child) and associations (dependency, block, etc.). Hierarchy defines aggregation and progress rollup. Associations are named, directional business links.

Don't just store a parent_id. Define which types can be parents and children, whether multiple parents are allowed, max depth, cross-space rules, and how progress and effort roll up.

For associations, define the name, reverse name, direction, cardinality, permissions, and business effects. "Blocks" and "is blocked by" are two sides of the same directed relationship. "Related to" might be undirected. These affect scheduling and critical paths.

Configuration Scope: Space, Not Project

Here's a naming correction: in this model, Space and Project are the same thing. Atlassian renamed Project to Space because "project" implies a temporary initiative with a start and end, but the container actually holds work for years. Space is a better term for a long-lived team boundary.

So where do one-off initiatives go? Not as new containers, but as groups of work within the space—defined by goals, versions, iterations, or tags. "Payment Internationalization Phase 1" is a goal, two versions, and a set of requirements and defects. After it ends, those items are archived, but the space continues.

The scope layers are: organization standards, space configurations, and delivery contexts (like versions or iterations). Delivery contexts can filter and route, but they shouldn't create new config containers.

Configuration Lifecycle: Treat Changes as Releases

Changing a config isn't saving a form—it's modifying running rules. If you make "verification result" required on close, bulk close operations might fail. Delete a status, and in-flight items lose their place.

So you need: stable IDs and reference analysis, draft/publish/versioning, and deprecation/migration strategies. High-risk changes should go through a new version, with clear upgrade paths for existing instances.

Converting a Task to a Bug isn't a dropdown change—it's a data migration that reinterprets attributes, workflow states, relationships, and permissions.

Kindness in the Details

When you build a system that respects these boundaries, you're actually being kind to your users. You're not asking them to fill in unnecessary fields. You're not letting them assign work to people who can't see it. You're not letting a config change break their workflow mid-sprint.

The original request—"require a verification result when closing a defect"—isn't about adding a required field. It's about adding an input to the close step. And the "root cause for critical bugs" rule isn't about making a field global; it's a conditional validation.

When a platform can answer what an object is, what facts it stores, how it's used, how it changes, and where rules apply, it becomes more than a configurable table. It becomes a system that helps people do their best work—without getting in their way.

Share this article:

Comments (0)

No comments yet. Be the first to comment!