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

对于一个人数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 模板:

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 环境,我们明天一早会受到一封邮件:

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,914评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,935评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,531评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,309评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,381评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,730评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,882评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,643评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,095评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,448评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,566评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,253评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,829评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,715评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,945评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,248评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,440评论 2 348

推荐阅读更多精彩内容