探讨-如何让团队了解产品进度

对于一个人数30-50的创业团队来说,如何让每个成员及时了解产品的变化是一个有意思的话题

为什么让产品信息共享?

因为,团队不同的 team 的工作侧重点各有不同:

  1. Support Team 关注用户使用产品时出现的问题,及时掌握产品变化,可以让 Support Team 的工作有预先准备,给用户提供最新指导,工作效率更高。
  2. Growth Team 关注产品的价值,旧功能的优化和新功能的上线都会影响他们的决策,例如 A/B Test,QA 等。
  3. Develop Team 关注产品的功能实现,别人代码的更新可能会影响自己的开发。

“变化”的分类:

  1. 大变化:可以理解为milestone (里程碑),比如大功能的上线。
    1. 这种大的变化适合专门的文档去记录,比如产品更新Blog:简单明了的文字、截图、视频教程等。
  2. 小变化:
    1. 用户看得见的变化:小的UI优化,Bug修复。
    2. 用户体验的变化:性能的优化(更加流畅),交互的变化(更加合理)。

解决思路

第一个问题,如何搜集变化?

  1. 方案一:成员每日写工作总结。
    1. 根据工作内容抽离出“变化”。
    2. 有个成员专门去维护一个或一系列文档。
    3. 优点:系统性强。
    4. 缺点:人力成本高,适合有一定规模的团队。
  2. 方案二:可以维护一个 wiki。
    1. 每个成员主动将“变化”写入文档中去。
    2. 优点:调动成员的积极性。
    3. 缺点:
      1. 需要一些工具,如文档协作,存储与检索。
      2. 需要一些规范和 review。
  3. 方案三:利用第三方工具 (github) 自动搜集。
    1. 检索 Pull Request 中详细描述这次PR的内容,搜集起来。
    2. 优点:节省人力,有利于PR规范化。
    3. 缺点:需要相关开发工作。

我们 Strikingly 崇尚敏捷,高效和自动化。采用了方案三。Develop Team 使用 Pull Request 模板,每天将特殊的 Pull Request 搜集起来。

第二个问题,如何通知每个成员?

  1. 发消息:“去看文档的更新”
    1. 优点:可以融入我们的日常交流中去。
    2. 缺点:消息流很容易丢失在其他讨论中
  2. 发邮件:将产品的“变化”写进邮件里面。
    1. 优点:比较正式,可自定义HTML,样式可控制,便于给归档。
    2. 缺点:需要相关开发工作。

我们决定采用邮件的方式,每天发送一个以邮件的形式通知团队的每个人。

结合起来后大致分成7个步骤:

  1. 设置Github
  2. 约定PR模板
  3. 记录部署时间
  4. 处理 github webhook
  5. 数据存储到 Redis
  6. 准备邮件
  7. 循环发邮件

开发配置相关:

  1. Rails 后端
  2. Redis 临时存储
  3. github 的 webhook 服务
  4. 用到的一些 gem:
    1. git_api: 访问 github API
    2. maruku: 将 Markdown 转换为 HTML
    3. sidekiq/sidetiq: 后台循环发送邮件

Step 1: 设置 Github

  1. 项目的 settings 中设置 webhook
    1. 设置 Payload URL
    2. 设置 events 类型:勾选 Pull Request
  2. 添加 Personal access token
  3. 使用 gem github_api, 添加配置文件

Step 2: 约定 PR 模板

示例 PR 模板:

探讨-如何让团队了解产品进度_第1张图片
pr_template.png

模板的重要性:

  1. 规范 Develop Team 的开发流程。
  2. 如果不使用模板,我们开发的这个工具,没有任何卵用O__O"…

Step 3: 记录部署时间

由于有些PR虽然被merge了,但并不一定被部署了,所以我们需要将最近一次部署的时间记录下来。

class DeployLogger
  DEPLOY_KEY = 'AWESOME_REPO_LAST_DEPLOY_KEY'

  # 根据实际情况使用默认时间
  def self.deploy_at
    $redis.get(DEPLOY_KEY) || '2015-08-10T02:27:38Z'
  end

  def self.deploy_at= time
    $redis.set DEPLOY_KEY, time
    $redis.expire DEPLOY_KEY, 60.days
  end
end

Step 4: 处理 github webhook

  1. 获取 PR 的 Number
  2. 因为我们并不能相信 webhook,所以我们应该基于 webhook 的信息主动通过 github API 获取数据。
  3. 筛选PR: merged 到指定分支(develop) 且含有 - [x] contains user facing changes
  4. 整合信息,提取出 PR 的Number,title,body,获取 PR 提交username等。

Step 5: 存储到 Redis

class PrDescription
  LAST_SEND_EMAIL_KEY = 'LAST_SEND_EMAIL_KEY'

  # 使用 redis ordered set
  # merged_at 时间秒数作为 score
  def self.create columns = {}
    key_word = columns[:key_word]
    merged_at = columns[:merged_at]
    content = columns[:content]
    $redis.zadd key_word, Time.parse(merged_at).to_i, content
    $redis.expire key_word, 2.days
  end

  def self.count_of key_word
    $redis.zcount key_word, '-inf', '+inf'
  end

 # 根据情况做适当调整
  def self.last_send_email_at
    $redis.get(LAST_SEND_EMAIL_KEY) || '2015-08-10T02:27:38Z'
  end

 # 根据情况做适当调整
  def self.last_send_email_at= time
    $redis.set LAST_SEND_EMAIL_KEY, time
    $redis.expire LAST_SEND_EMAIL_KEY, 5.days
  end

  # 获取当前部署好的PR
  # 范围:最近的一次发邮件的时间到最近的一次部署时间
  def self.descriptions_need_to_send_for key_word
    from_time_score = Time.parse(self.last_send_email_at).to_i + 0.01
    to_time_score = Time.parse(DeployLogger.deploy_at).to_i
    $redis.zrangebyscore key_word, from_time_score, to_time_score
  end
end

Step 6: 准备邮件

# 在每个 PrDescription 之间要用"\n"隔开,注意是双引号,需要转义,不能加空格,会影响 Markdown 的转化
markdown_string = PrDescription.descriptions_need_to_send_for(USER_FACING_CHANGES_KEY).join("\n")

if markdown_string.present?
  # 使用gem maruku 将Markdown
  # 有些小问题:
  #   1. 需要将 " 转换成 '
  #   2. 去除 '\n'
  @content = Maruku.new(markdown_string).to_html.gsub(/\"/,"'").gsub(/\n/, '').html_safe
  mail subject: "User Facing Changes #{Date.today}", from: PRODUCT_EMAIL, to: TEAM_EMAIL
  # 设置最后一次发邮件的时间
  PrDescription.last_send_email_at = DeployLogger.deploy_at
end

Step 7: 循环发邮件

# 用到 gem: sidekiq 和 sidetiq
class SendToSupportTeamChangesEmail
  include Sidekiq::Worker
  include Sidetiq::Schedulable

  sidekiq_options :queue => :default, :backtrace => true, :retry => 2

  # everyday 8:30 AM 
  # 请注意时区,可能需要转换
  recurrence { daily.hour_of_day(8).minute_of_hour(30) }

  def perform
    # 负责发送内部邮件的类
    InternalMailer.user_facing_change_email.deliver
  end
end

最后,如果今天有PR merged 到 develop 分支并且还被部署到 production 环境,我们明天一早会受到一封邮件:

这方面的探索还在继续,思路和代码还有可多地方可以提升,请各位多多指教。

你可能感兴趣的:(探讨-如何让团队了解产品进度)