谈谈我在平安两年的工作回顾和感受。

我是在 2015 年 5 月 20 日离开的百度,次日就来到了平安。当时平安的这个团队叫做移动开发二队。在百度的最后一天,我在朋友圈发了张合照,写了句“再见,我爱你”。很多人不理解我为什么在百度干了一年就走了,而且去的是平安这个并不以技术见长的公司。其实原因很简单:跟对的团队做对的事情。

说是“对的事情”是因为平安正处于技术转型期,其间会有大量的技术需求可以放手去做。而来平安这个团队的首要任务是开发完善底层的技术框架,有机会接触比较核心的技术,这对我的技术成长很有好处。

说是“对的团队”是因为我对平安这个团队的了解。平安那边的老大锋哥也是原先我们百度 LBS 团队的研发 leader,而 leader 定一也是之前在百度的时候合作非常愉快的一个同事。再加上鑫哥 @ASCE1885 每周一更的技术周报,让我觉得这个团队是非常尊重技术的团队,而我过去应该能有更多发挥的机会。

于是,在锋哥的几次劝说下,我来到了平安。时间过得很快,一转眼的功夫,我也已经在平安待了两年的时间。从团队初建,到发展壮大,我和这个团队携手共同成长。对平安,对金融壹账通,对这个团队,我有说不尽的感激。如今我即将告别平安,走向一段新的旅程。在启程之前,我想对这两年走过的路做一个回顾。

我这两年的工作基本是两条线的思路:主线任务保证做得漂亮,然后主动从日常工作中找问题和需求,做点支线任务。

2015年秋

主线:离线缓存

我刚去平安的时候就被安排去做应用端的离线缓存。那时候我们的手机应用里有大量的 H5 页面,在线加载非常耗时。而我在这之前其实并没有移动开发的经验,这么重要的一个框架功能全权交给我去设计和实现,心里还是没底的。所幸平安的入职流程很长,十天半个月都拿不到账号上不到网,所以 leader 也没指望我很快能完成这个项目。于是我刚去的头两个月自己一个人保持了 997 的全勤,拼命恶补移动端的开发知识。当时腾讯的 AlloyKit 提出了一种比较理想的 H5 离线缓存的方案,页面静态资源通过打包 APK 的方式解决了首次加载问题,另外还允许更新这些缓存资源。我就在这个想法的基础上结合团队的实际需要进行设计和完善。在这段期间,我和定一以及后台的同事马文经常会在细节上展开讨论,从基本的流程、包管理、插件ID分配、自动打包、资源加密等方面都进行了认真的思考和设计。大概两个月后,我们的离线缓存就正式上线了,插件的加载速度和以前相比有了质的提升,得到了团队的认可。我拿着这个第一次参加了公司的 3A 论坛技术峰会,认识了公司里头来自不同团队的大佬。

离线缓存
离线缓存

支线

在百度的期间我最大的收获在于对潜在的需求有着非常灵敏的嗅觉。当时的平安才刚面临技术转型,一些内部系统难免有不尽完善的地方,这对于来自 BAT 这类有着完善的内部系统的公司的同事而言很难适应。和大多数花时间吐槽环境的同事不同,我觉得这是一个好机会,这代表内部就有很多需求。所以,利用空余时间,我也做了一些“支线任务”,提高团队的效率。

日报助手

第一个让我们觉得麻烦的是每天的日报,科技要求我们每天写一篇日报,汇报当天的工作,并以此来作为确认考勤的依据。但实际的情况是,我们平常已经通过站会来确认沟通每天的工作了,日报便沦为了一种形式:填了没人看,但不填又影响考勤。所以我写了一个工具,在月底的时候跑一遍,就能一次性帮我们到日报平台上填写完整个月的日报,这样就节省了很多同事的时间。这个工具在内部一直工作良好,直到我们从科技独立出去,无需填写日报了才停止使用。

Wiki

团队里头也没有像样的 Wiki 平台,所以我用 Gollum 搭了个 Wiki 。为了方便在文档中插图,我又搭了一个相册平台,可以在上面上传照片并插到 Wiki 中。这两个平台一直使用到我们把代码由 SVN 切换到 Gitlab 时改用 Gitlab 的 Wiki 才结束服务。

wiki
wiki

任意门日志分析平台

当时我们有一个叫做“任意门”的项目,它的线上问题一直是使用 Excel 这种原始方式来跟进处理的,缺乏一个统一的问题跟进平台。而因为任意门是一个 SDK ,问题可能来自各种宿主应用,这些 Excel 报表的格式也有所不同,很影响问题的处理和定位。于是我又用了一个星期为任意门团队写了一个线上问题处理系统,可以导入 Excel 存到数据库中,然后提供一个报表页面,用于展示问题的描述、堆栈信息、出现次数、处理情况、宿主ID等数据。这个平台给开发维护任意门的同事带来了很大的便利。 这两年拿过的各种奖杯奖牌附图 1 这两年拿过的各种奖杯奖牌

日志分析平台
日志分析平台

本份工作顺利完成,再加上一些工具和平台加成,我在刚去的第一个季度就拿了团队的杰出贡献奖,奖品是一部 iPhone 6S 。

2015年冬

主线:RN热更新

之后我们团队进行了业务的调整,大部分人(包括我)转去做银行一账通的项目,代号 F 项目。这个项目的目标就是低成本地生成中小银行的直销银行 APP ,可想而知这对核心框架的稳定性、可定制性和交付能力带来非常大的挑战。为了节省人力成本以及提高业务交付能力,我们经过评估后,决定采用 React Native 的开发模式,核心功能都尽量封装成 RN 层可调用的模块,再通过 RN 热更新来提升业务交付能力。RN 热更新的设计和实现就成了我在平安的第二个季度的主线任务。

当时 Android 版本的 RN 才刚刚发布,RN 热更新还处于尝试阶段,一开始的思路是使用反射修改 PathClassLoader 来修改 bundle 包的路径。v0.15.0 开始 ReactInstantManager 新增了 setJSBundleFile 方法,可以在程序运行时执行 Bundle 包的位置,这就给实现热更新提供了更大的便利。RN 热更新本身技术倒没有什么特别的难度,后面出现的 Microsoft code-push 也给我们提供了有价值的参考。不过,技术实现往往不是最困难的地方,最困难的地方是落地。我们花费了较多时间在热更新平台的增量更新、图片热更新、图片精度选择性下发、包版本管理、跨版本升级支持等问题上。一直做到年前,这个项目正式上线,为应用的运营能力提供了强有力的支持。凭借着 RN 热更新我也拿了一个公司的创新奖。

RN热更新
RN热更新

支线

在第二个季度我也照旧从日常工作中找了一些问题,做了一些支线任务。

百宝箱

第一个问题就是团队的文件共享问题。在 F 项目中,开发、测试、运营、产品需要经常共享 UED 、设计稿等文档。这些文件共享都是通过邮件的形式,而公司的邮件有附件大小限制,并且邮件只保留一个月的时间,超过这个时间的会被归档,只能从公司的邮件找回平台中去找。所以经常出现诸如附件找回困难、附件上传失败的问题。所以我在内网搭了一个网盘“百宝箱”,可以很方便的让各个成员在内网共享文档。这个网盘很快得到成为了团队文档交流的主要方式,直到公司推广了 Jira 才退出了历史。

内部网盘
内部网盘

星黎殿

第二个问题是我们内网上的平台越来越多,缺乏一个统一的门户站点。所以我和一个实习生写了一个内部门户“星黎殿”,这个门户收录了我们日常使用的各种站点平台,并提供检索功能。这样,只需要将这个门户保存到书签就相当于保存了所有常用的站点的书签了。

内部门户
内部门户

壹瓴阁

平常大家的线上技术交流较少,所以我又用 discourse 搭建了一个内部技术交流论坛,为了鼓励同事们多发点文章,我搞了几个月的有奖征文活动,并且给每个小组都设立了文章数量指标,于是这个论坛同样受到了同事们的欢迎(甚至有同事在上面发布了征婚广告)。

内部技术论坛
内部技术论坛

金科大白

另外我发现的一个需求是图书馆的管理。我们团队自己搞了个小型图书馆,采用的是人工管理图书借阅的方式。由于缺乏自动化,借出去的书除非定期轮询一下,否则很难知道哪些书已经到期该归还。所以我带着几个实习生写了个基于公司内部聊天工具“天下通”的聊天机器人“金科大白”,可以与它对话查询和借阅书籍,当书籍借阅到期时,将自动邮件通知还书。这个机器人也给后面我开发内部群管家机器人以及叮当机器人带来了灵感。

金科大白
金科大白

二队网站

年前我还带了两个实习生做了一个我们团队的内部网站,主要用来展示团队在做的东西,以及一些可以对外开放的文档。后来我们从科技独立了出去,原来的团队随即更名,这个项目就胎死腹中了。

二队网站
二队网站

不过在这个过程中,我们却做了一个更有价值的东西。由于网站本身使用 Hexo 来编写,为了方便检索文档,我带着其中一个实习生写了个生成检索源的库 hexo-generator-search 并开源在 Github 上。这个库后来成了一个非常流行的 Hexo 插件,NPM 月均下载量一度达到 2000 次/月。

主线 + 支线双开,所以年底我被公司授予“一狼当先”创新员工奖。年底绩效沟通,leader 给我定级 A 档。

生活

平时的工作虽然比较忙,公司的文体活动还是挺丰富的。在团队里头可以申请俱乐部,于是我常年混迹在桌球俱乐部、游泳俱乐部、户外俱乐部中打酱油。 我和我的Ukulele附图 2 我和我的Ukulele

年底的时候公司的行政美女黎老板给我安排了一个的政治任务——年会表演。但纯粹上台唱歌实在无趣,所以我在年会前的一个月开始学习 Ukulele 。就跟学习一门新的语言范式一样,一开始面对陌生的东西依然会有一种恐惧感,但坚持下来很快就会发现它的确非常简单,很容易做到拿谱即弹,这也增强了我挑战新鲜事物的信心。放一段为年会准备的弹唱曲目吧: 秒拍:一小段I’m Yours

2016年

到了 2016 年,我们正式从平安科技独立了出来,成立了平安金科。后来公司名字又做了几次调整,到我离职时,公司的名字改成了“上海壹账通金融科技有限公司”。

主线

Git 迁移

平安的内部版本控制一直使用的是 SVN ,但 SVN 对分支和子模块的支持不给力,所以年前我们做了个决定,搭了内部的 Gitlab ,然后把整个工程代码拆分成主工程和多个子模块,然后全部迁移到 Gitlab 中托管。为了简化代码迁移的复杂度,我写了个内部工具 svn-git-transfer ,只需要配置好源 SVN 地址和目标 Gitlab 仓库地址,即可一键完成所有模块到 Gitlab 的同步,并保留了原先的所有提交记录。有了这个工具,我们只用了一天就完成了代码的迁移,然后大家高高兴兴的回家过年了。

年后回公司,我们遇到了更加头疼的一个问题:Git 的子模块极容易出错。具体可以参见我的一篇博文《化繁为简的企业级 Git 管理实践(一):多分支子模块依赖管理》。于是,在和同事们充分讨论之后,我们决定放弃子模块的 commit id ,采用我们自定义的一个 modules.json 的文件来维护子模块的版本。于是我用了几天时间完成了内部代码管理工具 fmanager 的第一个版本。这个工具将繁琐的子模块管理化繁为简,得到了同事们的一致好评。在之后我对这个工具不断的修改完善,加入了诸如分支切换、多分支 cherry-pick、Code Review、代码风格检查、Git-LFS 支持、钩子自动安装等,并且支持通过改配置文件动态增删子模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
fmanager - F 项目专用 Git 管理工具

- 开发者:潘伟洲 <panweizhou500@pingan.com.cn>
- 版本:v2.0
- 使用方法:

$ ./fmanager pull [-l modules.json路径] [-t 线程数] [--fast] 更新当前分支的主工程,并将每个子模块的代码更新到指定分支的最新状态。
$ ./fmanager update 和 ./fmanager pull 完全等效。只是一个别名。(缩写:up)
$ ./fmanager pull-rn [-t 线程数] 更新 React Native 及其子模块 (缩写:pullrn)。
$ ./fmanager init [-l modules.json路径] [-t 线程数] 重新根据 modules.json 的配置初始化工程,移除所有不在列表中的子模块。
$ ./fmanager checkout <分支名> [-l modules.json路径] 切换到某个银行分支,同时完成子模块的代码切换。(缩写:co)
$ ./fmanager cherry-pick <commit id> <分支列表> cherry-pick 某个 commit id 到分支列表。(缩写:cpick)
分支列表格式:逗号隔开,中间不带空格。
举例:
./fmanager cpick 023e937d JiLinBank,WeiHaiBank,TaiShanBank
$ ./fmanager cherry-push <commit id> <分支列表> cherry-pick 某个 commit id 到分支列表,并推送这些分支。(缩写:cpush)
$ ./fmanager submodule update [-l modules.json路径] <模块名列表> 更新指定子模块的代码到所处分支的最新状态。(缩写:sub)
$ ./fmanager showbranch 查看当前主工程和所有子模块的所属分支。(缩写:br)
$ ./fmanager status 查看当前主工程和所有子模块的修改状态。(缩写:st)
$ ./fmanager log 查看当前主工程和所有子模块的当前分支/标签和最新提交。
$ ./fmanager tag-diff [--detail] 生成当前modules.json配置的各个模块与各自最新标签的比对结果(只支持x.x.x形式的标签) (缩写:td)
参数说明:
--detail # 生成的结果包含代码的diff,默认只展示文件改动
举例:
./fmanager tag-diff # 生成文件改动的diff
./fmanager tag-diff --detail | less # 生成文件改动的diff,并在less中查看(比一次性全部打印到终端更流畅)
./fmanager tag-diff --detail > ~/res.md # 生成文件改动的diff并保存到 res.md 文件中
$ ./fmanager master <模块名|仓库地址> 查看当前工程下某个模块的仓库的 master 名单,目前只支持 10.20.11.218 下的的仓库
举例:
./fmanager master PAFFUIKit # 查看 PAFFUIKit 模块的 master 名单

$ ./fmanager login 登录 Phabricator 代码评审平台
$ ./fmanager setup 将当前工程引入代码评审
$ ./fmanager diff [--create | --update] 提交一个代码评审任务
举例:
./fmanager diff # 提交代码评审,并自动判断当前仓库是否需要新建一个评审任务,还是追加到现有的评审任务
./fmanager diff --create # 提交代码评审,并强制要求新建一个评审任务
./fmanager diff --update # 提交代码评审,并强制要求追加到现有的评审任务中
$ ./fmanager list 查看所有提交过的评审任务的状态
$ ./fmanager browse 评审id 打开评审id对应的评审页面
举例:
./fmanager browse D3 # 打开 id 为 D3 的评审任务的评审页面
$ ./fmanager push push 代码到 Gitlab 。将自动根据当前是否引入代码评审选择适合的方式
举例:
./fmanager push # push 代码
./fmanager push --revision D3 # push 评审 id 为 D3 的代码
$ ./fmanager amend [--revision revision_id] [--show] 修改某个评审任务的信息(只对接入评审的仓库有效)
举例:
./fmanager amend --revision D3 # 修改 id 为 D3 的评审任务信息
./fmanager amend --revision D3 --show # 打印 id 为 D3 的评审任务信息(不修改)
$ ./fmanager land push 命令的别名,与 push 命令完全等同
$ ./fmanager faq <类型> 查看各种 faq
举例:
./fmanager faq # 打开 faq 菜单
./fmanager faq fmanager # 同上,打开 fmanager 的 faq
./fmanager faq git # 打开 git 的 faq
./fmanager faq rn # 打开 RN 红屏的 faq
./fmanager faq react-native # 同上,打开 RN 红屏的 faq
./fmanager faq jenkins # 打开构建站的 faq
./fmanager faq anydoor-android # 打开任意门 Android SDK 的 faq
./fmanager faq anydoor-ios # 打开任意门 iOS SDK 的 faq
$ ./fmanager tips 显示每日一贴。
$ ./fmanager say <要说的话> 用 fmanager 的吉祥物说一句话。
$ ./fmanager commits-since <时间> 查看当前主工程和所有子模块自某个时间点开始的所有提交。(缩写:cs)
举例:
./fmanager commits-since yesterday # 统计从昨天开始到现在的所有commit
./fmanager commits-since 16:00 # 统计从今天下午4点到现在的所有commit
./fmanager commits-since 4 o clock pm # 和上面示例完全等效
./fmanager commits-since 2016-08-18 # 统计从2016年8月18日到现在的所有commit
./fmanager commits-since 2016-08-18-16:00 # 统计从2016年8月18日下午4点到到现在的所有commit
$ ./fmanager switch-sdk <(Android)要切换到的framework分支|(iOS)要切换的模式,SDKMode、SourceMode> 把当前工程的framework切换到SDK模式/源码模式
举例:
./fmanager switch-sdk Source # 把当前工程的framework切换到源码模式,
# 如果当前已是源码分支,则切回SDK分支拉取最新modules.json配置后再切回源码,保证源码是最新
./fmanager switch-sdk SDK # 把当前工程的framework切换到SDK模式
./fmanager switch-sdk Source --update # 把当前工程的framework切换到源码模式
# 如果当前已是源码分支,则直接按照当前framework/modules.json更新源码所有基础模块代码
./fmanager switch-sdk Source --current # 按照当前工程的framework的分支切换到源码模式,默认会先切回外层modules.json配置的framework分支
$ ./fmanager lfs # 使用 lfs 管理二进制文件
举例:
./fmanager lfs install # 安装 lfs ,必须执行一遍才能使用 lfs
./fmanager lfs track "*.zip" # 把所有 zip 类型文件都用 lfs 来管理
./fmanager lfs ls-files # 列举所有使用 lfs 管理的文件

这些功能使它成为了团队日常开发中必备的工具,也因此获得了一个季度优秀工具奖以及公司创新奖。

除了技术上的问题,迁移 Git 也给团队的学习接受能力提出了挑战。由于我对 Git 比较熟悉,所以我自然承担了在团队中推广 Git 的角色。一开始的时候,找我解决代码拉取问题的人需要排一条很长的队伍。我也忙得几乎顾不上干其他事情。为了减轻负担,我先是进行了几场 Git 的使用技术分享。之后我在 Gitlab 上写了份 FAQ ,然后把团队遇到的各种常见问题都放到 FAQ 上。一旦帮同事解决了新的问题,我都会要求他去负责把解决办法添加到 FAQ 上,久而久之 FAQ 上的问题集就非常完善了。除此之外,先前为团队搭建的技术论坛也成了我用来推广 Git 技术的平台,我在上面写了几篇 Git 的教程,也对团队成员的学习起到了一定的帮助作用。我甚至把一些常用的 Git 技巧和 fmanager 做了整合,于是每次使用 fmanager 后,都会随机出现类似下面这样的卖萌 ASCII 图和贴士:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
  _____________________________________________________________________________________
/ 你知道吗:要了解某个site用了哪些模块,每个模块使用什么tag
,可以访问模块可视化平台。
\ http://xx.xx.xx.xx:8081/modulevis/
-------------------------------------------------------------------------------------
\
\
.--.
|o_o |
|:_/ |
// \\ \\
(| | )
/'\\_ _/`\\
\\___)=(___/

tux

__________________________________________________________________________________
/ 你知道吗:传说多看看 Gitlab 上的 FAQ 可以成为
Git 高手:
\ http://xx.xx.xx.xx/FFProject/FFWiki/wikis/git-faq
----------------------------------------------------------------------------------
\ / \ //\
\ |\___/| / \// \\
/0 0 \__ / // | \ \
/ / \/_/ // | \ \
@_^_@'/ \/_ // | \ \
//_^_/ \/_ // | \ \
( //) | \/// | \ \
( / /) _|_ / ) // | \ _\
( // /) '/,_ _ _/ ( ; -. | _ _\.-~ .-~~~^-.
(( / / )) ,-{ _ `-.|.-~-. .~ `.
(( // / )) '/\ / ~-. _ .-~ .-~^-. \
(( /// )) `. { } / \ \
(( / )) .----~-.\ \-' .~ \ `. \^-.
///.----..> \ _ -~ `. ^-` ^-_
///-._ _ _ _ _ _ _}^ - - - - ~ ~-- ,.-~
/.-~
dragon

__________________________________________________
/ 你知道吗:如果只需要更新 React Native
的代码,而不需要更新 native 的代码,可以使用
./fmanager pullrn 命令。
\
--------------------------------------------------
(####)
(#######)
(#########)
(#########)
(#########)
(#########)
(#########)
(#########)
(########)
_____ (#########)
/ \ (#########) |\/\/\/| /\ /\ /\ /\
\/\/ | (#########) | | | V \/ \---. .----/ \----.
| (o)(o) (o)(o)(##) | | \_ / \ /
C .---_) ,_C (##) | (o)(o) (o)(o) <__. .--\ (o)(o) /__.
| |.___| /____, (##) C _) _C / \ () /
| \__/ \ (#) | ,___| /____, ) \ > (C_) <
/_____\ | | | / \ /----' /___\____/___\
/_____/ \ OOOOOO /____\ ooooo /| |\
/ \ / \ / \ / \ / \

HOMER MARGE BART LISA MAGGIE
 由于 Git 的分享做的比较好,教师节的时候我被评上了公司的“十佳讲师”。收获小卡片一张。附图 3 由于 Git 的分享做的比较好,教师节的时候我被评上了公司的“十佳讲师”。收获小卡片一张。

这些技术推广策略对团队普及 Git 起到了非常有效的推动作用。过了一个月后,找我的人就变得很少了。多数人也从 Git 的新手慢慢变成了老手,也能够帮忙解决新人们的 Git 相关的问题了。

还有一个避不开的问题就是分支管理方案。因为我们的业务模型比较复杂,Git-Flow 和 Github-Flow 都无法满足我们的需求。而没有一个统一的分支方案,就好比没有一个成文的交通规则,老司机也开不了车。于是有很长一段时间,我们都聚在会议室里头激烈地讨论分支管理方案,见我的另一篇博文 《化繁为简的企业级 Git 管理实战(三):分支管理策略》。这个过程虽然很痛苦,但是却很能够锻炼你的思维,以及考虑问题的完整程度。而且,你会感觉到你真正成为了团队里头一个领域的游戏规则的制定者,更加能感受到你对于这个团队的价值。

到了年中,我们使用 Git 来管理代码已经非常稳定可靠,Git 的迁移可以说非常成功。我们也因此获得了团队的优秀项目奖。

三合一更新器

在下半年,我终于有时间回归到客户端开发了。我们遇到一个问题是几种更新的弹窗抢占问题。我们的 APP 已经包含了 Native 热修复、RN 热更新和 H5 离线缓存三个层级的更新能力。但这三种更新各自为政,可能发生弹窗抢占。此外,几种依赖可能存在依赖关系:比如 RN 层的某个接口可能依赖 Native 层相应的改动,这时候如果 RN 先更新成功了,而 Native 端又修复失败,就会导致应用崩溃。所以我承担设计了三合一更新器,将三层的更新交由一个更新器来统一调度,三层更新都允许设置静默、建议、强制三种更新优先级,并在必要时刻允许对优先级进行提升。这个三合一更新器优化了用户的更新体验,也消除了三层更新没有同步完成导致应用崩溃的潜在隐患。

Code Review 推动

除了三合一更新之外,下半年我们开始思考如何更好地引入 Code Review 来规范化我们的工作。

在这之前,我已经通过 pre-commit 客户端钩子和 pre-receive 远程钩子来自动禁止了不规范的代码提交进我们的仓库。但代码风格只是最低层次的要求。有些代码本身的风格并没有问题,但是实现的思路与需求存在偏差,或者实现时对一些特殊情况欠考虑,这些 bad case 都无法通过静态分析的手段禁止提交,只能靠人工审核。所以,一个 Code Review 平台是很有必要的。

我先尝试了 ReviewBoard ,这个平台的优点是易于搭建,而且也有相应的 CLI 工具配套。但我发现这个平台似乎只支持 post review ,即 push 到仓库之后才发起 review 。而我们更希望做到 pre review,在 push 前就必须通过 code review ,否则禁止 push 。所以我决定弃用 ReviewBoard 。

之后我尝试了 Phabricator ,据说是 Facebook 内部使用的 Code Review 平台。我在搭建试用了几分钟后就觉得这个就是我们要的平台:

  1. 基于 LAMP 架构,部署简单;
  2. 无需像 Gerrit 那样强绑定代码仓库;
  3. 多次 commit 自动合并成一个;
  4. 提供了配套的 CLI 工具 Arcanist;
  5. 平台开放 API (尽管不怎么完善);
  6. 类似 Facebook 的 UI,颜值够高,容易上手。

Phabricator
Phabricator

还是那句话,技术实现不是难点,难的是推广。为了在团队中推广 Code Review,我做了几个工作:

  1. 把 Arcanist 集成进了 fmanager ,把常用的 diff、land 命令也变成了 fmanager 的子命令,降低了工具的学习成本;
  2. 写了个 pre-push 钩子,根据当前模块是否存在 .arcanist 文件判断是否接入了 Code Review,然后在 commit 前根据 commit message 判断当前 commit 是否已经过 review ,从而禁用未经 review 的代码提交;
  3. 对 Code Review 记录做了跟踪和分析,评选出了有价值的 Review 记录并进行奖励。调动了大家参与 Review 的热情。

为了方便查询和自己相关的 Code Review 的进展,我还写了个 Alfred 的 Workflow,输入自己的账号即可查询所有相关的 Review 任务:

Phabricator Workflow
Phabricator Workflow

这个 Code Review 平台内测不到两周,我们的 Code Review 数量就破百了,两个月后,团队的 Code Review 数量就突破了一千。这个平台也拿到了该季度的优秀项目奖。

支线

加班统计平台

年初我发现团队里的一个痛点:每个月月初我们都需要到内网导出打卡记录,然后从中手动筛选出加班的那些天,再整理成一份报销表格给行政的同事申请加班补贴。手动筛选加班的打卡记录不仅耗时,而且很容易出错,尤其是当天是特殊节假日的时候。每次整理加班记录,可能要花费二十分钟的时间。而这些工作完全可以交给机器自动完成。

所以我写了一个内部使用的加班统计平台,从内网导出的打卡记录只需要交给平台进行一下分析,马上就可以生成一份符合要求的加班统计表格,并且还考虑了特殊节假日以及漏打卡的情况。原先要花费二十分钟完成的事情,只需要交给这个平台,一分钟左右即可导出需要的表格。

加班统计平台
加班统计平台

为了吸引同事们使用,我又加了加班数据排名以及可视化的功能,可以很直观的看出近期加班的情况。

加班统计平台-排名系统
加班统计平台-排名系统

加班统计平台-可视化
加班统计平台-可视化

这个平台也是我首次尝试收费的一个服务,可选择一年授权和长期授权两种激活码,并提供首次免费试用。有 100 多位同事在试用成功后就购买了我的服务。

模块配置可视化平台

由于我们同时开发维护多个 APP ,数量一多,要查出每个 APP 的某个模块在使用什么版本就变得很繁琐了。

所以我写了一个模块配置可视化平台,支持多条件的检索模块的配置信息。

模块配置可视化平台
模块配置可视化平台

为了保证数据是最新的,我给主工程的 Git 仓库加了 post-receive 远程钩子:每次检测到主工程的 modules.json 发生修改,在服务端的 post-receive 钩子就会把新的 modules.json 配置情况同步到可视化平台中。

代码同步工具

在我们成功迁移到 Git 管理代码后,公司也开始普及 Git,搭了一个公司的 Gitlab 。在正式发布前,科技的工程管理团队甚至邮件向我咨询了 Git 管理的一些经验心得。

尽管公司的 Gitlab 有更大的团队来维护和支持,我们并不愿意把我们的代码迁移到公司的服务上。最主要的原因是我们自己的 Gitlab 可以更方便的定制,迁移到公司的 Gitlab 就没有这么高的权限了。 年会电子琴弹唱附图 4 年会电子琴弹唱

虽然自己的 Gitlab 定制起来很爽,但稳定性和单点问题也成了隐患。我们甚至遇到过 Gitlab 所在的服务器发生维护而导致我们一整天无法正常拉取和推送代码的情况,简直是一场噩梦。为了避免单点问题带来的灾难,我写了一个全自动代码同步工具,能够在每天凌晨把我们的 Gitlab 的组织、代码、成员、分支保护等东西都同步到公司的 Gitlab 中,而且无需管理员权限。见 《化繁为简的企业级 Git 管理实战(四):多 Gitlab 数据同步》。这个工具给我们的代码安全提供了一个强有力的保障,也因此获得了该季度的优秀工具奖。

年中和年底的两次绩效沟通,leader 又继续给我评级了 A 档。同时我被评为了公司的年度优秀员工。

生活

有了去年的 Ukulele 弹奏的基础,今年的年会我决定玩点其他的乐器。于是我在九月份买了一台卡西欧入门电子琴,开始学习电子琴弹唱。

电子琴弹唱和 Ukulele 弹唱完全是不同级别的难度。Ukulele 的弹唱基本就是弹和弦搞定,而电子琴弹唱可以说是吉他指弹 + 演唱的难度了,要在几个月内达到拿得出手的水平非常困难。好在现在的电子琴的教学软件都做的非常人性化,我只需学好指法,然后照着软件给出的演示进行肌肉记忆,整首歌硬记下来也是可能的。年会上,我弹唱了偶像王力宏的经典歌曲《你不知道的事》( 练习视频:你不知道的事),算是把大伙糊弄过去了。

2017年

主线:人工智能

到了 2017 年,我们的框架变得稳定,不像上一年有诸多需求。于是我们进入了舒适区,过上了朝九晚六的生活。但这种情况对于我而言并不见得是好事:需求变少了,人也会跟着变懒,久而久之就没有成长了。既然业余时间变多了,我决定把时间用来学点 AI 的相关的知识。 顺手给课程提了几个建议和bug,收到了 Udacity 寄来的小礼物。附图 5 顺手给课程提了几个建议和bug,收到了 Udacity 寄来的小礼物。

机器学习对数学的要求比较高,所以我先花了半个月的时间看了可汗学院的《线性代数》课程,写了几篇学习笔记。然后又看完了 Udacity 上的《机器学习入门》,并做完了全部练习。这门课讲的非常生动,极大地帮助我加深了对机器学习如何解决实际问题的理解。

在锋哥的鼓励下,我带头成立了一个人工智能兴趣小组。我们一共开展了 6 次分享,涵盖了 TensorFlow、贝叶斯分类、线性回归、K-means 聚类、自然语言处理、对话机器人实现等多个 topic 。另外我申请购买了一台高性能的深度学习机,自己搭建了 Jupyter Notebook 和支持 GPU 加速的 TensorFlow 环境,可以供小组成员在 Notebook 上做感兴趣的实验。

我们的notebook
我们的notebook

之后我想尝试将分类的技术用在我们内部的 issue tracker 来判断是否重复提 bug 。由于我们的框架同时对接了多款 App ,一旦框架出现了一个 bug,多个 App 上都会报相同的问题。如果能通过机器学习的技术来分辨出是否重复提 bug ,那么就能减少开发在 follow issue 上面花费的时间。判断是否重复提 bug 和判断文本内容相似度类似,可以对文本统计 TFIDF 词频信息,然后利用余弦相似度来判断相似程度。所以,我写了个爬虫,把我们的 Redmine 平台上的数据按照格式爬了下来,总共爬了接近两万条 bug 数据。但过了几天公司开始推 Jira 作为新的 issue tracker,我们的 Redmine 就停止使用了。这个项目就只能搁浅了。

虽然我们并没有真正用机器学习算法做出什么成果,但这个过程中积累的一些机器学习的知识也将为我日后的工作方向带来收益。

支线

图灵

我为团队做的一个比较好玩的项目是微信聊天机器人“图灵”。这是基于 wxbot图灵机器人 API 做的一个微信群聊天机器人。

起初它是为了取代金科大白而做的一个新的管家项目,因此它可以实现诸如询问内网站点地址、恶劣天气通知、群成员生日提醒、新人入群欢迎、活动报名征集、考勤异常提醒等功能。

群成员生日提醒
群成员生日提醒

大伙对图灵非常稀奇,刚推出的短短两天,它就被群里的同事 @ 了 800 多次。

火爆的图灵
火爆的图灵

后面我发现把图灵和项目管理结合可以产生更多意想不到的效果。例如,我们的框架的源码和 SDK 是分开分支进行版本控制的。以往要构建 SDK ,需要先切到源码分支,执行构建脚本,然后再切到 SDK 分支上更新 SDK 。过程比较繁琐。我给图灵加上了构建框架 SDK 的功能。要构建某个分支的 SDK,只需要跟图灵说一声即可:

使用图灵构建SDK
使用图灵构建SDK

交给机器人构建 SDK 是如此方便,于是 @图灵 构建 SDK 成了我们日常构建框架 SDK 的主要方式。

李开复说,“5秒以下的工作将会被人工智能替代”。我也在工作中尽量找出 5 秒内可以用微信机器人取代的活,并交由它去完成。于是我给图灵实现了诸如苹果APP审核状态跟踪、项目日报查询、Jenkins 构建站任务跟踪、个人bug通知查询、线上 App 下载地址查询等功能,这些功能为项目的日常管理带来了很大便利。

开源项目

由于今年加班时间变少了,我也有了更多时间投入在开源项目中。我的问题开始由“这个团队需要什么”转变为“外面的世界需要什么”。

在 5 月份的时候我决定自己动手做一个智能音箱,一方面来自于自己生活中的需求,另一方面也是因为找不到一个比较成熟可用的中文的开源智能音箱项目。于是我买来了硬件设备,花了三个星期的时间完成了叮当的第一个版本。由于树莓派+智能音箱毕竟是小众项目,所以我一开始并不期望这个项目能得到多少关注。然而,随着 QQ 用户群人数的不断壮大,越来越多的朋友安装了叮当,并且真正将它投入在了日常的使用上。发布不到 10 天,star 数量就突破了 100 个,有两天登上了当天 Python 的 Github Trending。截至本文发布,叮当的 QQ 用户群已经达到了 117 人, Github Star 数量为 211 个。

叮当登上Github Trending
叮当登上Github Trending

叮当也给我的职业生涯带来了新的变化。在发布叮当后的第三天,腾讯团队找上了我问我是否考虑他们的工作机会。考虑到在这边已经处于舒适区,我决定去了解一下情况。

来自腾讯的工作机会
来自腾讯的工作机会

经过了三轮技术面试和一轮通道面试,我拿到了 offer 。拿到 offer 的时候,我非常纠结。最大的原因来自对现在团队的不舍。但考虑到工作方向和个人的职业规划,我最终还是提了离职。

在项目交接期间,我又写了一个基于 Github issue 的评论脚本 comment.js ,用来取代 Disqus、网易云跟贴这类社会化评论系统。有趣的是,在我发布 comment.js 一周后,网易云跟贴宣布停止服务。有种自己在船沉了之前造出了飞机的感觉 :-D

网易云跟贴停止维护
网易云跟贴停止维护

总结

白驹过隙,在平安的两年时光已走到了尾声。除了不舍,更多的是对团队的感激。谢谢锋哥的知遇之恩,谢谢定一的悉心栽培,谢谢技术大神鑫哥的指点,谢谢所有合作过的同事,谢谢团队对我的信任。

在平安的两年时间里,我一共获得了一次年度优秀员工奖项,一次年度创新员工奖项,一个杰出贡献奖,两个工具奖,两个项目奖,四个创新奖,参与申请了八项专利,三次绩效考核都保持了 A 档。如果要我总结点经验:

  1. 关于KPI:KPI 只是对你的基本期望。不要把自己的工作局限在 KPI 所设立的范围里头。而应该多从工作中发现问题,然后带头去解决问题。当然,你所发现和解决的问题应该对整个团队带来收益,否则只能被定性成“边边角角”的活。另外,拿到差的绩效也不要难过,更重要的是你在工作中学到了什么。
  2. 关于择业:在百度的老大说过一句让我受益匪浅的话:“找工作,一看方向,二看团队,三看公司,四看钱”。方向和团队对你的个人成长有非常重要的影响。我的两次跳槽,都是按照这个标准做出的慎重选择。
  3. 关于沟通:少吐槽,多做事情。经常吐槽会让人觉得你融入不进这个团队。与其花时间吐槽,不如想想能不能做点什么东西改善现状。
  4. 关于技术:把握好深度和广度之间的度。知识面广,学习能力好的人在工作前几年会比较混得开,因为工作上手得快,工作难度也不会很高,产出就会比别人多。然而随着工作年限的增长,外界对你的知识深度的要求也会越来越高。我去腾讯面试的时候,有不少深一点的技术问题没有答上来。虽然最终我还是拿到了 offer ,但我知道很大原因是开源项目加的分。所以,工作年限越大,更应该注重对自身技术领域的深耕,以免再次择业时面临被动。

雄关漫道真如铁,而今迈步从头越。在即将奔赴新的单位前,我也以此文告诫自己:

  1. 一切从头开始,多跟周围的朋友学习;
  2. 在未来至少要专心投入两年,把自己的技术学得更精深。

Comments