작업 항목 및 작업 항목 유형

도전과제

이슈는 협업의 중앙 허브가 될 가능성이 있습니다. 다양한 이슈 유형은 수행하는 작업에 따라 다른 필드 및 컨텍스트가 필요하기 때문에, 각각의 유형은 다르게 다루어져야 합니다. 예를 들어:

  • 버그는 재현 단계를 나열해야 합니다.
  • 사건은 스택 트레이스와 해당 사건에만 관련된 다른 컨텍스트 정보가 필요합니다.

각 객체 유형이 별도의 모델로 발전하는 대신, 해당 모델에 포함된 위젯(하나 이상의 속성)을 사용하여 표준화된 기본 모델을 사용할 수 있습니다.

여기 현재 이슈 사용의 몇 가지 문제점 및 작업 항목을 검토하는 이유가 나와 있습니다:

  • 라벨을 사용하여 이슈 유형을 표시하면 보고 보기가 복잡해집니다.
  • 이슈 유형은 라벨의 최상위 사용 사례 중 하나이므로 이에 대한 우선 순위 지원이 합리적입니다.
  • 우리가 더 많은 기능을 추가함에 따라 이슈가 혼란스러워지고 완벽하지 않습니다:

    • 다른 객체와의 관계를 표현하는 일관된 패턴이 없습니다.
    • 이를 위해 라벨을 사용하고 있기 때문에 다른 유형의 이슈 간에 일관된 상호작용 모델이 없습니다.
    • 이슈 유형의 다양한 구현은 유연성과 확장성이 부족합니다.
  • 에픽, 이슈, 요구 사항 등 모두 유사하지만 약간의 차이가 있는 공통 상호작용으로 사용자는 각각의 동작 방식을 복잡한 정신적 모델로 이해해야 합니다.
  • 이슈는 지원해야할 신생 작업을 위한 확장성이 충분하지 않습니다.
  • 코드베이스 유지 보수 및 기능 개발은 이슈 추적의 핵심 역할을 넘어서 다른 작업 항목 유형을 지원하고 로직 및 구조적 차이를 처리함에 따라 더 큰 도전이 됩니다.
  • 새로운 기능은 일반적으로 이슈에서 행동을 가져오는 일등 객체로 구현됩니다. 이는 중복된 노력과 궁극적으로 일반적인 상호작용 간의 작은 차이로 이어지며, 불일치하는 사용자 경험으로 이어집니다.

작업 항목 용어

혼동을 피하고 효율적인 의사소통을 보장하기 위해 작업 항목에 대해 논의할 때는 반드시 다음 용어를 사용합니다. 이 목록은 작업 항목 용어의 단일 근원 (SSoT)입니다.

용어 설명 잘못된 사용 예 사용 되어야 하는 것
작업 항목 유형 작업 항목 클래스; 예: 이슈, 요구 사항, 테스트 케이스, 사건, 또는 작업 에픽은 결국 이슈가 될 것이다 에픽은 결국 작업 항목 유형이 될 것이다
작업 항목 작업 항목 유형의 인스턴스    
작업 항목 뷰 모든 유형의 작업 항목을 렌더링하는 새 프론트엔드 뷰 이것은 새 뷰에서 렌더링되어야 합니다 이것은 작업 항목 뷰에서 렌더링되어야 합니다
레거시 객체 작업 항목 유형으로 변환된 또는 변환될 객체 에픽은 독립/옛 낡은 객체에서 작업 항목 유형으로 이동될 것이다 에픽은 레거시 객체에서 작업 항목 유형으로 변환될 것이다
레거시 이슈 뷰 기존 뷰는 이슈와 사건을 렌더링하는 데 사용됩니다 이슈는 계속해서 이전 뷰에서 렌더링됩니다 이슈는 계속해서 레거시 이슈 뷰에서 렌더링됩니다
이슈 현재의 이슈 모델    
이슈 가능 이슈 모듈을 현재 사용 중인 모든 모델 (이슈, 에픽 및 MRs) 사건은 이슈 가능입니다 사건은 작업 항목 유형입니다
위젯 특정 작업 항목 데이터를 표시하거나 상호작용할 수 있게 하는 UI 요소    

일부 용어는 과거에 사용되었지만 혼란스럽고 현재 권장되지 않는 용어가 되었습니다.

용어 설명 잘못된 사용 예 사용 되어야 하는 것
이슈 유형 작업 항목 클래스를 지칭하기 위한 과거 방식 작업은 이슈 유형입니다 작업은 작업 항목 유형입니다

마이그레이션 전략

WI 모델은 기존 이슈 모델 위에 구축되며 이슈 모델 코드를 WI 모델로 점진적으로 마이그레이션할 것입니다.

접근 방법 중 하나는 다음과 같습니다:

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

  # ... 현재 issue.rb 코드 모두
end

class Issue < WorkItems::WorkItem
  # 이 클래스에 코드를 추가하지 마세요. WorkItems:WorkItem에 추가하세요.
end

이미 이슈 테이블 내에서 이슈 유형 개념을 사용하고 있으며 이슈 유형 열을 통해 이슈, 사건, 테스트 케이스 이슈 유형이 있습니다. 이를 확장하여 향후 사용자가 사용자 지정 WITs를 정의할 수 있도록 하기 위해 이슈 유형을 별도의 작업 항목 유형 테이블로 이동할 것입니다. 이슈 유형작업 항목 유형으로 마이그레이션하는 과정은 이 에픽에 설명된 대로 모든 최상위 그룹의 WIT 집합을 생성하는 것입니다.

참고: 처음에는 WIT를 최상위 그룹에서만 정의할 수 있으며, 그런 다음에하위 그룹에서 상속받게 될 것입니다. 나중에하위 그룹에서 새로운 WIT를 정의할 수 있는 가능성을 조사할 것입니다.

work_item_types 테이블 소개

예를 들어, ID가 11, 12, 13인 세 개의 루트 레벨 그룹이 있다고 가정해보겠습니다. 또한, 다음과 같은 기본 유형이 있다고 가정해보겠습니다: 이슈: 0, 인시던트: 1, 테스트 케이스: 2.

해당 work_item_types 레코드:

namespace_id base_type title
11 0 이슈
11 1 인시던트
11 2 테스트 케이스
12 0 이슈
12 1 인시던트
12 2 테스트 케이스
13 0 이슈
13 1 인시던트
13 2 테스트 케이스

이를 달성하기 위해 우리가 할 일:

  1. issues 테이블에 work_item_type_id 열을 추가합니다.
  2. 새로 추가되거나 업데이트된 이슈에 대해 issues#issue_typeissues#work_item_type_id 열 양쪽에 작성되도록 보장합니다.
  3. issues#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 레코드는 고객이 선택하는 경우에만 생성됩니다. 그러나 이는 새롭게 도입된 작업 항목 유형의 발견 가능성을 낮추는 것을 의미합니다.

작업 항목 유형 위젯

위젯은 작업 항목에 존재할 수 있는 개별 구성 요소입니다. 이 구성 요소는 하나 이상의 작업 항목 유형에서 사용될 수 있으며 구현 단계에서 가볍게 사용자 정의될 수 있습니다.

위젯에는 프론트엔드 UI(있는 경우)와 위젯에서 사용하는 모든 데이터를 표시하고 관리하는 관련 로직이 모두 포함됩니다. 데이터 모델과 위젯 간에 일대다 연결이 있을 수 있습니다. 이것은 동일한 데이터를 사용하거나 관리하는 여러 위젯이 있을 수 있으며 동시에 나타날 수 있음을 의미합니다(예: 읽기 전용 요약 위젯과 편집 가능한 상세 위젯 또는 동일한 모델의 두 가지 다른 필터링 뷰를 보여주는 두 위젯).

위젯은 목적에 따라 구별되어야 합니다. 가능한 경우, 이러한 목적은 최대한 고수준으로 추상화되어 재사용성을 극대화해야 합니다. 예를 들어, “작업”을 관리하는 위젯은 “하위 항목”으로 구축되었습니다. 하나의 유형의 하위 항목을 관리하는 대신에 어떤 하위도 관리할 수 있도록 추상화되었습니다.

모든 WIT는 미리 정의된 위젯 풀을 공유하며 특정 WIT에서 활성화된 위젯에 의해 사용자 정의될 것입니다. 모든 속성(열 또는 연관)은 속성이 속한 WIT에 관계없이 자체 캡슐화된 기능을 가진 위젯이 될 것입니다. 특정 작업 항목의 유형을 전환한 후에는 특정 WIT에 대해 다른 집합의 위젯을 표시합니다.

위젯 메타데이터

각 WIT를 해당 활성화된 위젯에 매핑하기 위해 데이터 구조가 필요합니다.

원격에서 다양한 작업 항목 계획을 구현하기 위해 GitLab에서 작업 항목 유형을 매우 유연하게 구성할 수 있기를 바랍니다(강약조 GitLab 워크플로우 또는 SAFe 5 등), 그리고 결국 고객이 자체 워크플로를 사용자 정의할 수 있기를 바랍니다.

이 경우 작업 항목 계획은 특정 특성(일부 위젯 활성화, 다른 것은 비활성화)을 가진 유형 집합으로 정의됩니다(에픽, 스토리, 버그, 작업 등).

새로운 작업 항목 아키텍처를 구축 중이므로 초기 시스템을 더욱 잘 구축하기 위해 먼저 GitLab에서이 시스템을 사용하도록 하고, 고객이 사용자 정의를 도입하지 않도록 합니다.

작업 항목의 base_type는 각 유형에 대해 사용 가능한 위젯을 정의하는 데 사용됩니다(현재 상태). 이 정의는 데이터베이스 테이블에 저장되어야 합니다. WIT 위젯 메타데이터의 정확한 구조는 아직 정의되지 않았습니다. 필요시 기억하기 위해 base_type가 추가되었습니다(요구 사항 및 사고를 작업 항목으로 변환하기 위해). 이러한 리소스가 일반적인 작업 항목이되면(이러한 리소스가 일반적인 작업 항목이되면), base_type가 제거될 것입니다.

WIT 위젯 아키텍처의 구조가 확정될 때까지 새로운 작업 항목 유형을 만들지 않겠습니다. 새로운 작업 항목 유형이 절대적으로 필요한 경우 프로젝트 관리 엔지니어링 팀의 구성원에게 연락하십시오.

데이터베이스에 새 작업 항목 유형 만들기

우리는 work_item_types 테이블을 사용하기로 결정하여 이슈 테이블에서 issue_type 열을 삭제한 작업을 완료했습니다. 이 Epic에서 설명한 대로입니다.

work_item_types 테이블을 도입한 후에 추가적인 work_item_types를 더 추가하고, 다른 팀이 이를 더 쉽게 할 수 있도록 하려고 합니다. 새로운 work_item_type을 도입하려면 다음을 수행해야 합니다:

  1. work_item_types 테이블에 새 레코드를 생성하기 위한 데이터베이스 마이그레이션 작성.
  2. Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter를 업데이트.

work_item_types를 도입하는 방법을 보여 주는 다음 MR을 확인하세요:

데이터베이스 마이그레이션 작성

우선, work_item_types 테이블에 새 레코드를 생성하는 데이터베이스 마이그레이션을 작성하세요.

마이그레이션을 작성할 때 다음 사항을 명심하세요:

  • 중요: 기존 API에서 새 유형 제외
    • 아마도 우리는 새로 생성된 작업 항목 유형을 완전히 릴리스할 때까지(이슈 목록 등의 기존 기능에) 표시되지 않도록 하려고 할 것입니다. 이를 위해 이 제외 목록에 새 유형을 추가해야 합니다. 단, 이 마이그레이션이 실행되자마자 사용자가 새 유형으로 새 이슈 및 작업 항목을 생성할 수 있다고 예상되는 경우에는 제외입니다.
  • 후속 MR을 위해 새 유형이 즉시 존재한다고 가정
    • 새 작업 항목 유형을 추가하는 경우 후속 MR에서 즉시 유형이 존재한다고 가정하는 대신 일반 마이그레이션을 사용하는 것이 유익하다고 믿습니다.
  • 마이그레이션은 실패를 피해야 함
    • 마이그레이션을 실행할 때 work_item_types와 관련된 데이터가 특정 상태에 있을 것으로 예상합니다. 현재, 데이터 상태를 시드 및 마이그레이션에 기반하여 얼마나 의존할 수 있는지에 대한 토론이 진행 중입니다. 데이터가 불일치 상태에 있는 경우에도 마이그레이션이 실패하지 않도록하는 마이그레이션을 작성합니다. 아마도 이를 변경하기 위해 일부 데이터베이스 작업을 업데이트해야 할 것입니다.
  • 새 유형에 대한 위젯 정의 추가
    • 마이그레이션은 새 작업 항목 유형과 각 작업 항목에 필요한 위젯 정의를 추가합니다. 선택하는 위젯은 새 작업 항목이 지원하는 기능에 따라 다르지만, Description와 같이 모든 새 작업 항목에 필요한 위젯도 있습니다.
  • 선택 사항: 계층 제한 생성
    • 예시 MR 중 하나에서는 work_item_hierarchy_restrictions 테이블에 레코드를 삽입하기도 합니다. 이는 새 작업 항목 유형이 Hierarchy 위젯을 사용할 경우에만 필요합니다. 이 테이블에서는 어떤 작업 항목 유형이 자식을 가질 수 있는지와 그 유형이 어떤 유형의 자식이 될 수 있는지 지정해야 합니다. 또한 동일 유형의 작업 항목에 대한 계층 깊이를 지정해야 합니다. 새로운 제약 조건을 생성할 때 교차 계층(cross group 또는 프로젝트) 관계는 기본적으로 비활성화되지만, cross_hierarchy_enabled에 값을 지정함으로써 활성화할 수 있습니다. 제약 조건이 작업 항목 유형에 대해 캐시되므로 연결된 작업 항목 유형의 clear_reactive_cache!를 호출하는 것도 필요합니다.
  • 선택 사항: 연결된 항목 제한 생성
    • Hierarchy 위젯과 유사하게, Linked items 위젯도 다른 유형의 작업 항목이 어떤 유형에 연결될 수 있는지를 정의하는 규칙을 지원합니다. 제약 조건은 소스 유형이 대상 유형에 관련될 수 있는지 또는 대상 유형을 차단할 수 있는지를 지정할 수 있습니다. 현재의 제약 조건:
    유형 관련될 수 있는 유형 차단할 수 있는 유형 차단될 수 있는 유형
    Epic Epic, issue, task, objective, key result Epic, issue, task, objective, key result Epic, issue, task
    Issue Epic, issue, task, objective, key result Epic, issue, task, objective, key result Epic, issue, task
    Task Epic, issue, task, objective, key result Epic, issue, task, objective, key result Epic, issue, task
    Objective Epic, issue, task, objective, key result Objective, key result Epic, issue, task, objective, key result
    Key result Epic, issue, task, objective, key result Objective, key result Epic, issue, task, objective, key result
티켓 작업 항목 추가 예시

데이터베이스에 티켓 작업 항목 유형이 이미 존재하지만, 마이그레이션 예시로 사용할 것입니다. 새 유형의 경우 새 이름과 ENUM 값을 사용해야 함을 유의하십시오.

class AddTicketWorkItemType < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!
  restrict_gitlab_migration gitlab_schema: :gitlab_main

  ISSUE_ENUM_VALUE = 0
  # Enum value comes from the model where the enum is defined in
  # https://gitlab.com/gitlab-org/gitlab/-/blob/1253f12abddb69cd1418c9e13e289d828b489f36/app/models/work_items/type.rb#L30.
  # A new work item type should simply pick the next integer value.
  TICKET_ENUM_VALUE = 8
  TICKET_NAME = 'Ticket'
  # Widget definitions also have an enum defined in
  # https://gitlab.com/gitlab-org/gitlab/-/blob/1253f12abddb69cd1418c9e13e289d828b489f36/app/models/work_items/widget_definition.rb#L17.
  # We need to provide both the enum and name as we plan to support custom widget names in the future.
  TICKET_WIDGETS = {
    'Assignees' => 0,
    'Description' => 1,
    'Hierarchy' => 2,
    'Labels' => 3,
    'Milestone' => 4,
    'Notes' => 5,
    'Start and due date' => 6,
    'Health status' => 7,
    'Weight' => 8,
    'Iteration' => 9,
    'Notifications' => 14,
    'Current user todos' => 15,
    'Award emoji' => 16
  }.freeze

  class MigrationWorkItemType < MigrationRecord
    self.table_name = 'work_item_types'
  end

  class MigrationWidgetDefinition < MigrationRecord
    self.table_name = 'work_item_widget_definitions'
  end

  class MigrationHierarchyRestriction < MigrationRecord
    self.table_name = 'work_item_hierarchy_restrictions'
  end

  def up
    existing_ticket_work_item_type = MigrationWorkItemType.find_by(base_type: TICKET_ENUM_VALUE, namespace_id: nil)

    return say('Ticket work item type record exists, skipping creation') if existing_ticket_work_item_type

    new_ticket_work_item_type = MigrationWorkItemType.create(
      name: TICKET_NAME,
      namespace_id: nil,
      base_type: TICKET_ENUM_VALUE,
      icon_name: 'issue-type-issue'
    )

    return say('Ticket work item type create record failed, skipping creation') if new_ticket_work_item_type.new_record?

    widgets = TICKET_WIDGETS.map do |widget_name, widget_enum_value|
      {
        work_item_type_id: new_ticket_work_item_type.id,
        name: widget_name,
        widget_type: widget_enum_value
      }
    end

    MigrationWidgetDefinition.upsert_all(
      widgets,
      unique_by: :index_work_item_widget_definitions_on_default_witype_and_name
    )

    issue_type = MigrationWorkItemType.find_by(base_type: ISSUE_ENUM_VALUE, namespace_id: nil)
    return say('Issue work item type not found, skipping hierarchy restrictions creation') unless issue_type

    # This part of the migration is only necessary if the new type uses the `Hierarchy` widget.
    restrictions = [
      { parent_type_id: new_ticket_work_item_type.id, child_type_id: new_ticket_work_item_type.id, maximum_depth: 1 },
      { parent_type_id: new_ticket_work_item_type.id, child_type_id: issue_type.id, maximum_depth: 1 }
    ]

    MigrationHierarchyRestriction.upsert_all(
      restrictions,
      unique_by: :index_work_item_hierarchy_restrictions_on_parent_and_child
    )
  end

  def down
    # There's the remote possibility that issues could already be
    # using this issue type, with a tight foreign constraint.
    # Therefore we will not attempt to remove any data.
  end
end

Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter 업데이트

BaseTypeImporter 에서는 보유한 유형 및 각 유형에 연결된 위젯의 구조를 명확히 시각화할 수 있습니다. BaseTypeImporter는 신설된 GitLab 설치 및 테스트 스위트의 단일한 진실의 원천입니다. 이것은 항상 마이그레이션으로 바꾸는 내용을 반영해야 합니다.

사용자 정의 작업 항목 유형

WIT 위젯 메타데이터 및 WIT를 특정 위젯에 매핑하는 워크플로를 통해 사용자에게 사용자 정의 WIT를 노출시킬 수 있을 것입니다. 사용자는 고유의 WIT를 생성하고 미리 정의된 풀에서 위젯을 사용자 정의할 수 있을 것입니다.

사용자 정의 위젯

최종 목표는 사용자가 사용자 정의 위젯을 정의하고 이를 모든 WIT에서 사용할 수 있도록 하는 것입니다. 그러나 이것은 훨씬 더 나아진 반복 작업이며 데이터 및 응용 프로그램 아키텍처를 결정하기 위해 추가 조사가 필요합니다.

요구 사항 및 에픽을 작업 항목 유형으로 마이그레이션

우리는 요구 사항과 에픽을 작업 항목 유형으로 마이그레이션할 것이며, 그들만의 위젯 세트를 갖게 될 것입니다. 이를 달성하기 위해 데이터를 issues 테이블로 마이그레이션하고 현재 requirementsepics 테이블을 유지하여 기존 참조와의 하위 호환성을 보장할 것입니다.

요구 사항을 작업 항목 유형으로 마이그레이션

현재 Requirement 속성은 Issue 속성의 하위 집합이므로 마이그레이션은 주로 다음으로 구성됩니다:

  • 데이터 마이그레이션.
  • API 수준에서의 하위 호환성 유지.
  • 이전 참조가 계속 작동하도록 보장.

다른 기반이되는 데이터 구조로의 마이그레이션은 최종 사용자에게는 무리가 없어야 합니다.

에픽을 작업 항목 유형으로 마이그레이션

Epic에는 현재 Issue WIT에는 없는 몇 가지 추가 기능이 있습니다. 따라서 에픽을 작업 항목 유형으로 마이그레이션하려면 현재 Epic 객체와 WIT 간의 기능 동등성을 제공해야 합니다.

주요 누락된 기능은 다음과 같습니다:

  • 그룹 수준의 작업 항목 가져오기. 이것은 그룹 및 프로젝트 통합 이니셔티브에 종속됩니다.
  • 계층 위젯: 작업 항목을 계층 구조로 구성하는 기능.
  • 상속된 날짜 위젯.

이미 에픽을 사용하고 있는 사용자들의 작업 흐름을 방해하지 않으려면, 프로젝트 수준에서 에픽과 기능 동등성을 제공할 Feature라는 새로운 WIT를 도입할 것입니다. 그것은 그룹 및 프로젝트 통합 계획과 결합함으로써 에픽을 WIT로의 원활한 마이그레이션 경로를 제공하여 사용자 작업 흐름에 최소한의 방해를 줄일 것입니다.

작업 항목, 작업 항목 유형 및 위젯 로드맵

우리는 작업 항목, 작업 항목 유형 및 사용자 정의 위젯(CW)으로 순차적인 과정을 통해 진행할 것입니다. 우리의 앞으로의 작업 개요에 대한 개략적인 개요는 에픽 6033을 참조하세요.

Redis HLL 카운터 스키마

우리는 현재의 Redis 슬롯 스키마로는 그룹 내 또는 단계 수준에서 기능 간 이벤트를 집계하고 중복 제거할 수 없는 작업 항목을 위한 확장 가능한 Redis 카운터 스키마가 필요합니다.

세 가지 Plan 제품 그룹은 모두 동일한 기본 개체(work item)를 사용할 것입니다. 각 제품 그룹은 여전히 MAU를 추적해야 합니다.

제안된 집계 카운터 스키마

graph TD Event[특정 상호작용 카운터] --> AC[집계 카운터] AC --> Plan[Plan xMAU] AC --> PM[Project Management xMAU] AC --> PP[Product Planning xMAU] AC --> Cer[Certify xMAU] AC --> WI[Work Items 사용자]

구현

새로운 집계 스키마는 이미 구현되어 있으며 우리는 이미 GitLab.com에서 작업 항목의 고유한 액션을 추적하고 있습니다.

자세한 구현 내용은 MR를 참조하세요. 해당 MR은 새로운 고유한 액션의 정의, 코드에서의 이벤트 추적, 그리고 필요한 집계 카운터에 새로운 고유한 액션을 추가하는 것을 다룹니다.