피드백 루프

체인지로그 항목이 되는 기능 요망 템플릿

4분 분량

기능 요망 템플릿은 네 가지 질문으로 이루어진 폼이다. 그 사람이 무엇을 하려고 하는지, 무엇이 그것을 막는지, 대신 무엇을 시도해봤는지, 그리고 완료되었을 때 어떻게 통보받고 싶은지. 보통 여기에 함께 등장하는 다른 모든 것, 즉 우선순위 선택지, 공수 추정, 비즈니스 가치 점수는 요청을 받는 팀을 위한 것이며, 그것을 보내는 사람이 잘못 채워 넣게 되는 부분이다.

깔끔한 요청은 템플릿에 대한 잘못된 시험이다. 옳은 시험은 이렇다. 여섯 달 후, 그 기능이 출시되었을 때, 누군가 그 요청을 찾아내고, 이해하고, 그것을 쓴 사람에게 알릴 수 있는가? 대부분의 템플릿은 접수를 위해 설계되어 있다. 이것은 루프가 닫히는 그날을 위해 설계되어 있다.

기능 요망 템플릿에는 무엇을 포함해야 하는가

목표, 장애물, 우회 방법, 그리고 요청자로 돌아가는 길을 포함해야 한다. 그 순서로 네 개의 필드가 있으며, 각각이 팀이 나중에 물을 질문에 답한다.

필드나중에 답하는 질문폼에 있는 이유
무엇을 하려고 하십니까?우리가 만든 기능이 그들이 필요로 했던 것인가?목표는 어떤 구체적인 제안보다도 오래 살아남는다
오늘 무엇이 그것을 막고 있습니까?“완료”란 어떤 모습인가?수정 방법을 규정하지 않고 간극만을 이름으로 밝힌다
대신 무엇을 하고 계십니까?이것이 실제로 얼마나 긴급한가?고통스러운 우회 방법은 우선순위 선택지보다 강한 신호다
어떻게 알려드리면 될까요?“출시됨” 메시지는 누구에게 가는가?대부분의 템플릿이 빠뜨리는 필드다

의도적으로 빠져 있는 것: 필수 필드로서의 제안된 해결책(코멘트로는 환영하지만 틀에는 넣지 않는다), 우선순위 선택지(모든 제출자가 높음을 고른다), 그리고 공수나 가치에 대한 어떤 추정치도(그것은 트리아지 이후 팀의 몫이다). 해결책을 묻는 템플릿은 버튼에 대한 요청을 얻고, 목표를 묻는 템플릿은 결과에 대한 요청을 얻으며, 결과야말로 체인지로그 항목이 쓰이는 대상이다.

템플릿

이것은 우리가 사용하는 GitHub issue 템플릿을 폼으로 나타낸 것이다. .github/ISSUE_TEMPLATE/feature_request.yml에 붙여넣으면 New Issue 페이지에서 구조화된 폼으로 렌더링된다. 이를 통해 접수된 요청은 피드백 위젯으로 접수된 것과 같은 필드를 가진 issue로 착지하며, 이는 바로 다음 절에서 중요해진다.

name: Feature request
description: What you are trying to do, and what stops you.
labels: ["feature-request"]
body:
  - type: textarea
    id: goal
    attributes:
      label: What are you trying to do?
      description: >-
        The outcome, not the button. "Export a month of invoices as one
        PDF" beats "add a PDF export".
    validations:
      required: true
  - type: textarea
    id: blocker
    attributes:
      label: What stops you today?
      description: >-
        Where the product runs out. An error, a missing option, a limit.
    validations:
      required: true
  - type: textarea
    id: workaround
    attributes:
      label: What do you do instead?
      description: >-
        The spreadsheet, the script, the manual step. "Nothing, I gave
        up" is a valid answer.
  - type: input
    id: contact
    attributes:
      label: How should we tell you when it ships?
      description: >-
        An email address, or leave blank to be notified only on this
        issue.

두 가지 세부 사항이 일을 해낸다. labels: ["feature-request"]는 누군가 트리아지할 때까지 기다리는 대신 생성 시점에 요청이 분류된다는 뜻이다. 그리고 마지막 필드가 존재하는 이유는 “알려드리겠습니다”가 약속이며, 약속에는 주소가 필요하기 때문이다.

기능 요망은 어떤 라벨을 가져야 하는가

기능 요망은 그것이 무엇인지에 대한 라벨 하나, 얼마나 긴급한지에 대한 라벨 하나, 그리고 어디서 왔는지에 대한 라벨 하나를 가져야 한다. 세 개의 라벨, 세 개의 축이며, 각각을 서로 다른 독자가 읽는다.

라벨값누가 읽는가
종류feature-request, bug어느 큐에 들어갈지 결정하는 사람
우선순위priority:low, priority:medium, priority:high다음 사이클을 계획하는 사람
출처from-widget, from-form, from-support요청이 어디서 오는지 측정하는 사람

위젯은 제출물을 issue로 접수할 때 처음 두 축과 from-widget을 적용한다. from-form과 from-support는 다른 경로로 들어오는 요청을 위한 제안이다. 위젯의 라벨은 종류(bug인지 feature-request인지는 메시지만으로 분류기가 판단한다), 우선순위(차분하고 구체적인 크래시 리포트는 높음, 이미 물었던 것의 중복은 낮음, 보안 문제를 조금이라도 암시하는 것은 문구와 무관하게 bug이면서 높음), 그리고 from-widget. 같은 세 개의 축은 위의 템플릿을 통해 손으로 도착하는 요청에도 똑같이 작동하며, 그것이 요점이다. 요청은 어디로 들어왔든 요청이라는 것.

한 가지 관례가 더 있다. 위젯은 접수하기 전에 issue 본문에서 제출자의 이메일 주소를 제거한다. issue는 공개될 수도 있는 저장소 안에 있기 때문이다. 대신 제출 참조 번호로 대체한다. 그 주소는 issue에 들어가지 않으며, 제출자는 결과를 위젯 자체에서 확인한다. 여러분의 트래커가 팀 밖에서 보인다면 연락처 필드에도 똑같이 하라.

기능 요망은 어떻게 체인지로그 항목이 되는가

기능 요망이 체인지로그 항목이 되는 것은 pull request가 그 issue를 닫고, 그 pull request로부터 초안이 작성된 항목이 그곳으로 링크를 걸 때다. 그 메커니즘은 GitHub 자체의 클로즈 키워드다. 설명에 Fixes #142라고 적힌 PR은 병합 시점에 issue 142를 닫는다. 여러분의 체인지로그 항목이 병합된 pull request로부터 초안이 작성된다면, 그 초안은 issue 번호를 함께 운반할 수 있고, 항목은 누가 요청했는지 알게 된다.

그것이 템플릿이 해결책이 아니라 목표를 묻는 이유다. 항목이 쓰일 때, 목표는 작성자가 필요로 하는 문장이 된다. “이제 청구서를 한 달치씩 모아 하나의 PDF로 내보낼 수 있습니다”는 체인지로그 항목이다. “PDF 내보내기 추가”는 커밋 메시지다. pull request로부터 초안을 작성하는 체인지로그 도구는 수집과 링크를 처리할 수 있지만, 문구에는 여전히 사람이 필요하며, 그 사람에게는 목표가 필요하다.

출시되었을 때 무슨 일이 일어나는가

요청자는 항목으로의 링크와 함께 통보받는다. 우리의 방식에서 그것은 위젯을 통해 들어온 요청에 대해 자동으로 이루어진다. 사람이 그 항목을 승인한 후 issue에 게시되는 “Shipped — <항목 제목>“이라는 코멘트이며, 발행된 항목으로의 링크가 함께 오고, 동시에 위젯은 제출자에게 같은 항목을 보여준다. 이 템플릿으로 손으로 접수한 issue에는 자동 코멘트가 달리지 않으니, 같은 규칙에 따라 그 루프를 직접 닫아라. 코멘트는 병합 시점이 아니라 승인 시점에 의도적으로 게시된다. 무언가가 아직 공개되지 않았는데 공개되었다고 말하는 코멘트는 타임스탬프가 찍힌 깨진 약속이기 때문이다. 각 요청은 많아야 한 번 통보받는다. 같은 항목을 두 번 승인해도 두 번째 코멘트는 생기지 않는다.

이것을 손으로 한다면, 규칙은 같다. pull request 시점에 루프를 닫지 마라. 발행된 항목에서 닫고, 한 번만 닫아라. 피드와 위젯은 요청하지 않았던 사람들, 즉 대부분의 사람들에게 같은 항목을 전달한다. 코멘트는 요청했던 그 사람을 위한 것이다.

왜 대부분의 기능 요망 템플릿은 실패하는가

그것들은 트리아지를 쉽게 만들도록 설계되어 있고 실제로 성공하지만, 요청자에게 중요한 유일한 순간을 희생시킨다. 열두 개의 필드를 가진 템플릿은 요청 수가 줄어들고, 그것이 얻는 요청은 열두 개의 필드를 채울 인내심을 가진 사람들로부터 온다. 이는 그 기능을 필요로 하는 사람들과 같은 집단이 아니다. 네 개의 필드를 가지고, 그중 하나가 “어떻게 연락드리면 될까요”인 템플릿은 더 많은 요청을 얻으며 그 모두에 응답할 수 있다.

FAQ

기능 요망 템플릿은 우선순위를 물어야 하는가? 아니다. 대신 우회 방법을 물어라. “매주 금요일마다 스프레드시트로 내보내서 다시 입력하고 있습니다”는 제출자가 높음으로 설정한 드롭다운보다 우선순위에 대해 더 많은 것을 말해준다.

요청자는 해결책을 제안해야 하는가? 자유 텍스트 안에서는 그럴 수 있다. 그것을 틀로 만들지는 마라. 해결책으로 쓰인 요청은 서로 합치기가 더 어렵고, 그것에 대해 체인지로그 항목을 쓰기도 더 어렵다.

기능 요망은 공개 로드맵에 나타나야 하는가? 계획된 후에는 그렇다. 같은 issue에 붙은 라벨이 그것을 계획됨 열에 넣고, 요청자는 그것이 움직이는 것을 지켜볼 수 있다. 공개 로드맵 글이 그 메커니즘이다.

중복은 어떻게 처리하는가? 새 요청을 기존 issue에 링크하고 낮은 우선순위 라벨을 붙여라. 닫지는 마라. 각각의 중복은 그것이 출시될 때 알려야 할 또 한 명이다. Changeloop의 자동 코멘트로는 pull request가 그 사람의 issue도 지정해야만(Fixes #142, fixes #187) 그 사람에게 알림이 간다.

템플릿은 어디에 있어야 하는가? pull request를 받게 될 저장소 안이다. 그래야 클로즈 키워드가 작동한다. 별도의 트래커에 있는 요청은 병합 시점에 손으로 링크해야 하며, 그 단계가 바로 생략되기 쉬운 단계다.


이 글의 기술적 내용은 독립적으로 검토되지 않았습니다. 잘못된 부분이 있으면 알려 주세요. 바로잡겠습니다.

changeloop 관련 페이지: 개발자 문서, changelog 도구 비교

changeloop
루프를 닫는 changelog를 만드는 팀입니다. 사용자가 무언가를 요청하면 팀이 전달하고, 요청한 사람은 알게 됩니다.