工作项和工作项类型

挑战

议题有可能成为一个集中的协作中心。 不同的议题类型需要不同的字段和不同的上下文,这取决于它们需要完成的工作。例如:

  • 需要列出重现一个 bug 的步骤。
  • 事件(incident)需要引用堆栈跟踪和仅与该事件相关的其他上下文信息。

我们可以标准化一个底层的通用模型,而不是每个对象类型都分成一个单独的模型,我们可以使用响应的配置部分(一个或多个属性)对其进行自定义。

以下是当前议题使用的一些问题,以及为什么要研究工作项:

  • 使用标记显示议题类型很麻烦,并且使报告视图更加复杂。
  • 议题类型是标记的最常见用例之一,因此为它们提供一流的支持是有意义的。
  • 随着添加了更多的功能,议题开始变得混乱,而且它们并不完美:

    • 对于如何呈现与其他对象的关系没有一致的模式。
    • 因为我们使用标记,没有跨不同类型议题的连贯交互模型。
    • 议题类型的各种实现缺乏灵活性和可扩展性。
  • 在常见交互中,史诗、议题、需求和其他对象都有相似但足够细微的差异,用户需要对他们各自的行为方式持有一个复杂的心理模型。
  • 议题的扩展性不足以支持他们需要促进的所有新兴工作。
  • 随着我们将议题类型,从议题跟踪的核心角色扩展到支持不同的工作项类型以及处理逻辑和结构差异,代码库的可维护性和功能开发成为一个更大的挑战。
  • 新功能通常使用一流的对象实现,这些对象通过共享关注点从议题中导入行为。这会导致重复工作,并最终导致常见交互之间的微小差异,从而导致用户体验不一致。

工作项术语

为避免混淆并确保沟通高效,我们将在讨论工作项时专门使用以下术语。

术语 描述 误用示例 正确示例
工作项类型 工作项目的类别; 例如:议题、需求、测试用例、事件或任务 史诗终将成为议题 史诗最终将成为工作项目类型
工作项 工作项类型的实例    
工作项视图 呈现任何类型的工作项的新前端视图    
旧议题视图 用于呈现议题和事件的现有视图    
议题 现有议题模型    
议题化 当前使用议题化模型的任何模型(议题、史诗和 MR) 事件是议题化的 事件是工作项类型

过去曾使用过一些术语,但后来变得令人困惑,现在不鼓励使用。

术语 描述 误用示例 正确示例
议题类型 以前引用工作项类别的方法 任务是一种议题类型 任务是一种工作项类型

迁移策略

WI 模型将建立在现有的 Issue 模型之上,我们将逐步将 Issue 模型代码迁移到 WI 模型。

一种方法是:

class WorkItems::WorkItem < ApplicationRecord
  self.table_name = 'issues'

  # ... all the current issue.rb code
end

class Issue < WorkItems::WorkItem
  # Do not add code to this class add to WorkItems:WorkItem
end

我们已经通过 issue_type 列在 issues 表中使用了 WIT 的概念,包含 issueincidenttest_case 议题类型。为了扩展它以便将来我们可以允许用户定义自定义 WIT,我们将把 issue_type 移动到一个单独的表中:work_item_typesissue_typework_item_types 的迁移过程将涉及为所有根级别组创建 WIT 集。

note起初,WIT 只能在根级组中定义,然后由子组继承。我们将在以后的迭代中研究在子组级别定义新 WIT 的可能性。

引入 work_item_types 表

例如,假设有三个具有 ID 的根级别组:111213。此外,假设以下基本类型:issue: 0incident: 1test_case: 2

各自的 work_item_types 记录:

group_id base_type title
11 0 Issue
11 1 Incident
11 2 Test Case
12 0 Issue
12 1 Incident
12 2 Test Case
13 0 Issue
13 1 Incident
13 2 Test Case

我们将做些什么来实现这一目标:

  1. issues 表中添加 work_item_type_id 列。
  2. 确保我们写入 issues#issue_typeissues#work_item_type_id 列以获取新建的或更新的议题。
  3. 回填 work_item_type_id 列,指向议题的项目根组对应的 work_item_types#id。例如:

    issue.project.root_group.work_item_types.where(base_type: issue.issue_type).first.id.
    
  4. issues#work_item_type_id 填充后,我们可以将查询从使用 issue_type 切换到使用 work_item_type_id

要引入新的 WIT,有两种选择:

  • 按照上述过程的第一步。我们仍然需要运行迁移,为所有根级别组添加新的 WIT,以使 WIT 可供所有用户使用。除了长期迁移之外,我们还需要将几百万条记录插入到 work_item_types 中。对于不希望或不需要在其工作流程中使用额外 WIT 的用户来说,这可能是不受欢迎的。
  • 创建一个选择加入流程,以便特定根级别组的 work_item_types 中的记录仅在客户选择加入时创建。但是,这意味着新引入的工作项类型的可发现性较低。

工作项类型部件

所有 WIT 将共享相同的预定义部件池,并将通过在特定 WIT 上激活的部件进行自定义。每个属性(列或关联)都将成为具有自封装功能的部件,无论它属于哪个 WIT。 因为任何 WIT 都可以有任何部件,我们只需要定义哪个小部件对特定 WIT 是活动的。因此,在切换特定工作项的类型后,我们会显示一组不同的部件。

部件元数据

为了使用相应的活动部件自定义每个 WIT,我们需要一个数据结构来将每个 WIT 映射到特定的部件。

noteWIT 部件元数据的确切结构仍有待定义。

自定义工作项类型

通过 WIT 部件元数据和将 WIT 映射到特定部件的工作流程,我们将能够向用户公开自定义 WIT。 用户将能够创建自己的 WIT 并使用预定义池中的部件对其进行自定义。

自定义部件

最终目标是允许用户定义自定义部件,并在任何 WIT 上使用这些自定义部件。但这是一个更进一步的迭代,需要额外的调查来确定要使用的数据和应用程序架构。

将需求和史诗迁移到工作项类型

我们将使用自己的一组部件将需求和史诗迁移到工作项类型中。为了实现这一点,我们将数据迁移到 issues 表,我们将保留当前的 requirementsepics 表作为旧引用的代理,以确保与现有引用的向后兼容性。

将需求迁移到工作项类型

目前 Requirement 属性是 Issue 属性的子集,因此迁移主要包括:

  • 数据迁移。
  • 在 API 级别保持向后兼容性。
  • 确保旧参考继续有效。

对最终用户来说,迁移到不同的底层数据结构应该是无缝的。

将史诗迁移到工作项类型

Epic 有一些 Issue WIT 目前没有的额外功能。因此,将史诗迁移到工作项类型需要在当前的 Epic 对象和 WIT 之间提供功能奇偶性。

主要缺少的功能是:

  • 将 WI 提高到群组级别。这取决于合并群组和项目计划。
  • 层次结构部件:将工作项结构化为层次结构的能力。
  • 继承日期部件。

为了避免中断已经在使用史诗的用户的工作流程,我们将引入一个名为 Feature 的新 WIT,它将在项目级别提供与史诗相同的功能。 将这一点与整合群组和项目方面的进展相结合,将帮助我们提供一条将史诗顺利迁移到 WIT 的路径,同时最大限度地减少对用户工作流程的干扰。

工作项、工作项类型和部件路线图

我们将在迭代过程中转向工作项、工作项类型和自定义部件(CW)。

Redis HLL 计数器架构

我们需要一个更具可扩展性的 Redis 计数器模式来处理工作项,包括 Plan xMAU、Project Management xMAU、Certify xMAU 和 Product Planning xMAU。我们无法使用当前的 Redis 插槽架构,在群组内或阶段级别跨功能聚合和重复数据删除事件。

所有三个计划产品组都将使用相同的基础对象(工作项)。 每个产品组仍然需要跟踪 MAU。

建议的聚合计数器模式

graph TD Event[Specific Interaction Counter] --> AC[Aggregate Counters] AC --> Plan[Plan xMAU] AC --> PM[Project Management xMAU] AC --> PP[Product Planning xMAU] AC --> Cer[Certify xMAU] AC --> WI[Work Items Users]