Skip to main content

How We Actually Design Work Item Models for Assistive Tech Teams

Managing assistive tech projects is messy. A tiny workflow tweak can break a screen reader. Here's what we learned about structuring work items—types, fields, workflows, scopes—to keep things agile without losing accessibility.

Why This Matters More Than Your Average Project

You know that moment when someone says, "Just add a field to the bug form"? And you think, sure, it's one field. But then you realize that field has to appear only on the close transition, require a value if the bug is critical, and maybe show a dropdown of verification results. Suddenly you're redesigning half your workflow.

I've been there. I remember a time when we shipped a speech-to-text update, and a tester couldn't close a bug because the "verification result" field wasn't in the right layout. It sounds trivial, but it blocked the entire release. That's when I knew the underlying work item model had to be fixed, not patched.

Six Questions, Not Six Thousand Fields

We started asking six questions for every work item type. They became our checklist:

  • What is this object? Its stable identity, name, icon, and lifecycle.
  • What can it hold? Text, people, enums, time, attachments, links, computed results.
  • How do users interact? Forms for create, edit, transition, and list views.
  • How can it change? Workflow states, steps, permissions, validations, and triggers.
  • How does it connect? Parent-child, dependencies, blocks, and plain links.
  • Where do rules apply? Organization, space, type, context, and version.

If you mix these layers into one giant config, you'll end up with a system only a guru can maintain. We learned that the hard way. Once, we had a "default assignee" set at the type level. That caused chaos because different teams needed different defaults for the same type. So we separated everything: each layer changes at its own pace. The type can be renamed without breaking APIs. The same attribute can appear on different types but have different candidate pools per space.

Types Are Just Identity, Not a Place for Defaults

Some tools let you create a type with a name and icon, and that's it. But then they let you set a default assignee on the type. Bad idea. Assignee is an attribute on the instance, not a property of the type.

Why? Because the same "requirement" type may need different assignee rules across teams. Our product team defaults to the creator; our delivery team picks a business owner; the functional team has no default. If we baked that into the type, we'd have three requirement types. Instead, we keep types lean. Defaults are computed results at creation time. If the default person leaves, the system recalculates or warns—doesn't change the type definition.

From Custom Fields to a Unified Attribute Model

I remember when "custom fields" meant a text box or a dropdown. That's too limited. Real work items are richer: an assignee is a person object, attachments have metadata, time tracking includes logs, and parent items are references to other objects.

So we started thinking of everything as an attribute. Each attribute has an ID, type, value structure, cardinality, default rules, permissions, query capability, layout hints, and change history. Then forms become compositions of attributes, not bespoke field sets.

This unified model pays off. The create page, detail page, transition page, and list view all draw from the same attribute pool. APIs, imports, exports, and automations speak the same language. But watch out: attribute types are sticky. Once you have data, you can't just flip a text field to an enum without handling old values. "Very high," "Urgent," and "P0" don't merge cleanly.

Scope Fields: Global vs. Space Attributes

We used to talk about "global fields" and "project fields." Then we adopted the concept of a Space—a long-lived container for team collaboration. Don't add another layer of "project fields." That just recreates the old mess.

In our clean model, there are two configuration sources: organization-level common attributes, and space-level specialized ones. A one-time migration batch or a special validation number is still a space attribute, just used within certain contexts.

Here's a rule of thumb: the organization maintains the common language; the space maintains its business language; work items hold the actual facts. Don't duplicate an attribute just because one team needs a tweak. And don't promote every local concept to global.

How do you know if two attributes can share an ID? 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 even if they have the same name.

Page Layouts Are Not Data Tables—They're Views

Once you have a unified attribute resource, page layouts stop defining data and start answering: what does the user see, fill in, and do here?

Take the bug closure example. On creation, you don't ask for root cause—that comes later. On closing, you require the verification result. If severity is critical, you also require root cause. After closure, the fields remain visible but read-only for most users.

We build five kinds of layouts: create, detail, transition, list, and card. The create page should first establish the object—confirm space, type, title, and necessary context—then handle routing. Don't cram every attribute into the create modal. The detail page should give a quick judgment: what is this, where is it stuck, who's on it, what's next? Then show the details.

Remember, validation must happen server-side. A front-end required flag can be bypassed by bulk operations, APIs, or automation. So any rule that affects data validity has to be enforced by the backend.

Workflows: States Are Static, Steps Are Actions

A workflow isn't just a list of states. States are stable phases; steps are the 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 specific role, open a transition form, collect the verification result, check for root cause on critical bugs, update the closer and timestamp, and write an audit record.

A well-defined step includes: start and target states, action name, who can execute, what to show and collect, preconditions, server-side validations, post-actions like notifications and automations, and entry points for external triggers.

If you only configure states, users can flip statuses arbitrarily. With steps, the platform can explain why a change happened.

Multiple Workflows for One Type? Three Different Meanings

People often say "one type supports multiple workflows," but that phrase can mean three different things. First, can you prepare multiple candidate workflows for the same type? Second, how does the system pick one at creation? Third, does the chosen workflow support parallel nodes internally?

Jira uses workflow schemes to map types to workflows within a space. ONES configures one workflow per type per project, but lets you copy. Feishu uses a business-line field to route to different process templates. TAPD lets categories choose workflows, and workflows can be serial or parallel.

Each approach has trade-offs. Fixed mapping is simple and auditable. Conditional routing is flexible but needs careful rule priority. Parallel nodes express collaboration but introduce complexity around node owners, merge conditions, and progress calculation.

Whatever you choose, save the workflow version on each instance. Otherwise, a config change can break running items.

Relationships Turn Forms into a Network

Work items don't live in isolation. A requirement splits into subtasks for product, frontend, backend, and testing. A backend task might block integration. A bug links to a requirement, a test case, and a release.

Distinguish between hierarchy (parent-child, for breakdown and rollup) and associations (named business links like dependencies, blocks, or related). Don't store hierarchy as just a parent_id. Define which types can be parents, whether multiple parents are allowed, max depth, cross-space rules, and how progress rolls up.

Associations need names, reverse names, direction, cardinality, cross-domain rules, and permissions. "Blocks" and "is blocked by" are two ends of the same directed relation. "Related" is undirected. "Precedes" might affect scheduling and critical path.

Configuration Scope: From Project to Space

Here's a common mistake: treating "space" and "project" as two nested containers. In modern tools, a Space is exactly what a Project used to be—just renamed. Atlassian renamed Project to Space in 2025 to emphasize that it's a long-lived container, not something that ends when a project does.

In assistive tech, your product team may work in the same space for years. People change, versions release, special initiatives end—but the team's language and history stay. A specific delivery, like "Payment Internationalization Phase 1," is not a separate container. It's a goal, a couple of versions, and a set of requirements and bugs inside the space. When it's done, you archive or close those items, and the space continues.

So the configuration scope is three layers: organization, space, and delivery context. Versions, iterations, and goals are delivery context—they participate in filtering, routing, and reporting, but they shouldn't spawn their own field and workflow copies.

Config Changes Are Rule Changes, Not Form Edits

Changing an attribute from optional to required on closure might break a bulk-close API. Deleting a state could orphan live bugs. Changing a 1:n relationship to n:n alters rollup and permissions. That's why you need lifecycle management for configs.

Give every type, attribute, state, step, layout, and relationship a stable ID. Provide reverse references: which spaces, filters, reports, automations, and API calls use this config? Without that, impact analysis is guesswork.

Use drafts, versions, and controlled publishing. Low-risk changes like adding a help text can propagate instantly. High-risk ones—deleting a state, changing a field type, making a transition require a field—should go into a new version, and you should decide which spaces and instances upgrade.

When you stop using a field, retire it gracefully. Converting a Task to a Bug is a constrained data migration, not a simple dropdown switch. You have to map attributes, states, workflows, and relationships—and decide what happens if a required attribute is missing.

Wrapping Up

At the surface, configuring a project tool looks like creating types, dragging fields, and drawing flowcharts. But the real craft is in the boundaries.

Types define identity only. Attributes carry facts. Workflows constrain change. Relationships connect objects. Spaces separate long-term team rules from one-off delivery scopes. And config versions protect running work from sudden edits.

Go back to the opening question. "Require verification result when closing a bug" isn't adding a required field to the detail page—it's adding an action input to the "close" step. "Critical bugs must have a root cause before closing" isn't making root cause globally required—it's adding a conditional server-side validation. Both can live on the same transition form, but they belong to different rule sets.

When a platform can answer "What is this object? What facts does it hold? How do users interact? How can it change? What can it connect to? Where do rules apply?"—then it stops being a configurable table and becomes an enterprise-grade work item platform. For assistive tech teams, that's not a luxury. It's a necessity.

Share this article:

Comments (0)

No comments yet. Be the first to comment!