半年前我加入一个刚刚拿到 A 轮资金的创业团队负责 iOS 项目。早期的时候公司生死未卜,只追求快速迭代找到一个正确的方向。这种早期默默无闻的团队也没什么工程追求,就是写的快就好了。但是确定方向后要长期发展,就不能再野蛮生长了。 基于过去半年我在这个项目里的实践经验,和大家分享一下。
代码托管:自建 Gitlab
早期草根团队最省事的就是用 GitHub 了。但是团队人数增加后用 GitHub 的成本就很高了。普通的团队套餐每个月每人 9 刀。另外一个问题就是 GitHub 部署在国外,国内访问网络时常不稳定。听闻某跨国团队代码托管在 GitHub 上,某次重要会议期间 GitHub 无法正常访问。真是突如其来的父爱如山。 另外一个缺点就是服务端如果要自己配置 CI 服务不太方便。如果部署在自己的服务器上,其他一些服务脚本也部署在一起,会有很大的自主权。 综合之后选择了主流的 Gitlab。
工程师的时间比机器贵
很多短视的团队觉得配给工程师的设备太贵,挑个便宜点的就好了。一台好的电脑虽然贵点,可是长期下来节省下来的工程师的编译时间比机器贵多了。在设备上我跟公司建议那就配最新的 15 寸的 rmbp 呗,再来一个 dell 4K 显示器呗。后面发现键盘鼠标也重要啊,每个人又补贴了 500 块的键鼠额度。 看到很多工程师还在用 air 开发,还有 mac mini 的。真的为这种傻逼公司感到心痛。我曾经在的某团队还是 4 个终端用同一台电脑。每次编译的时候我就到南京路散个步。如果晚上要上线,可以去看个电影回来。
尽早招人
招人是团队发展过程中非常重要的一环。 很多早期团队都低估了招人的难度和周期。因为不太知名的团队招人有两个缺点:
不能给出太高的薪水
优秀的人才当然会比较市场上薪水。已经成名已久的 BAT 这种公司自然会有薪水的优势。创业公司虽然有期权,但是毕竟公司前途未卜。很多工程师也担心公司会不会过阵子就倒闭的问题。
团队事情多且杂
项目成熟后的迭代大多是按部就班,有稳定的节奏。每个环节的都有很细分的专职人员。早期的项目因为项目还是处于成长阶段,很可能半路做着看到直播火了,我们加个直播的需求。或者做着做着发现竞品有个功能,管不了那么多我们下个版本就上。或者 CEO 路过的时候突然有了个想法,上线的时间又推后一下。
招人除了技术的硬指标,在早期团队还有一个工程团队文化的问题。一个几十个人的项目,里面某个特定的人的积极性对于项目其实是不太重要的。他只要完成应该完成的工作。甚至和其他人不说话也影响不大。一个大的项目也不能因为任何一个人不在了就运行不下去。
但是早期团队,人就这么几个。有一个人对团队的使命认知不一致,日常行为里就会有很多摩擦。
我之前思考过团队文化是什么,怎么形容团队文化。后来看到一个说法感觉挺贴切。文化是空气,无处不在。公司没有规定下班后社交平台上看到用户反馈需要你去回应,也不会规定你发现其他部门的产品有问题是不当回事还是应该去和其他部门的人沟通,又或者看到一个更好的建议是不是要和公司提出来。这些行为背后的支撑就是团队文化。在团队里的人决定了价值观。
综合上面说的,招到一个匹配的技术人员,运气好的话几天就你能遇到,更常见的情况是可能要好几周的时间。当然如果情急之下招进来一个人,干了几个月后发现不合适就走了,对于团队的士气损害也挺大的。
所以考虑到项目未来的进展,要及时启动招人的计划。当你发现进度忙不过来的时候开始招人,这个时候你要抽时间去准备面试的事情,还要兼顾项目进度,会很焦头烂额。
转型 Swift
团队里的另外 3 个同事之前都没有写 Swift 的经验。但是考虑到未来的发展趋势,并且我们的业务类型对动态化的要求没那么强。我坚持在团队里推行使用 Swift 编程。
我经常被问到的一个问题是你想用 Swift 但是团队里其他人不会用,会不会给项目推进带来困难。其实如果团队里有人正确的引导,帮他们解决上手过程中的问题,再给一段时间过渡。很快他们就会退不回去。
下面介绍一下我把从 OC 迁移到 Swift 的过程。
先用 Swift 写好网络层的库。借着把常用的几个 OC Model 和 Swift 对象做好桥接。类似下面这样:
class SwiftUser {
init(ocUser: OCUser) {
}
func convertOCUser() {
}
}
这样改造之后如果一个新的模块就可以完全用 Swift 编写。一开始肯定是用 OC 的思维写 Swift 的代码。但是在熟悉了 Swift 语法后可以慢慢在 review 过程中提出可以用更 Swift 的写法。 有些功能需要 OC 和 Swift 互相调用确实挺麻烦。如果让一个没 Swift 经验的上手就解决这些问题一定很气馁。所以在项目过程中也要分配一定时间把老的 OC 代码重写了。好在原先的代码本来就很乱,需要重写。这样就随着业务推进中,Swift 比例越来越高。
这样经过一两个月后大家就慢慢熟悉了 Swift ,此时再去推进 RxSwift 等框架的使用。
理顺开发工作流
项目早期的时候需求千千万,一个迭代版本中应该开发多少功能呢?产品经理本能的就是靠拍脑袋。列了一页需求后表示这就是这个版本了。程序员都倾向于乐观估时间,做着做着半个月过去了。下个迭代的需求、UI 设计,交付前测试的工作都很混乱。
后来经过讨论确定了两周一个迭代周期。开发过程中发现某个需求这个迭代里无法完成就挪到了下个迭代中。每个周期阶段要做什么大家都很明确。两周左右发布参考用户反馈也是一个比较好的节奏。
这里要强调的是开发过程前期的准备工作。主要指需求和 UI 图。大多数的需求设计都是围绕某个需求展开,但是这个需求要融入到现有体系里是有很多周边的工作要做的。比如产品提出用户资料里应该可以打标签。于是画了一个草图里有标签。对于他可能这个需求描述的很明确了,但是真正落地的时候就有其他工作要做。比如标签怎么删除?标签有字数限制吗?标签重复了怎么办?这些问题如果前期设计的时候产品没有表达清楚,就只能在开发的过程中来回沟通。有一个经验丰富的开发者在前期就参与需求的讨论,和提出问题对于在后期开发有很大的帮助。
不用 Sketch 的设计师不是好设计师
我看到很多设计师沿用传统,一直使用 PS 。然而实际上业界使用矢量设计工具 Skecth 已经很普遍了。现在手机的屏幕尺寸更异,如果设计的时候不是矢量图,而是位图,做响应式的布局设计就会很不方便。实际上移动的 UI 设计如果用惯 Sketch ,绝对是生产力的极大提升。 但是和大多数人一样,很多设计师都会觉得 PS 用的也挺顺手的,Sketch 没用过。其实 leader 是有义务去推动一些人,让美好发生。
之前我在的团队我就一直不断暗示不厉害的设计师才用 PS ,后来刺激了几周后他说他现在也可以用 Sketch ,后来慢慢项目 symbol 都凑齐了 PS 他也退不回去了。
当然也有非常老派的设计师,这种只能给他压力让他去被动改变。当时我们团队有一个四十多高龄设计师,我们也很为难。我当时想那算了,下个月如果你不能用 Sketch 出图就自己准备换个工作吧。当然作为一个团队也不能给个指示就甩手不管了。中间已经熟练使用 Sketch 的设计师会特别关注他的学习状态,及时指导。最后也得到了一个好的结果,他在被迫改变后发现 Sketch 确实更好用。
这里还有安利一个很好用的输出设计图的软件:zeplin。设计图直接采用标注的方式会很死板。程序员在查看过程中可以自己查看到设计图的所有源信息效率会得到极大的提升。
接入 CI
很多团队改变代码里的宏来区别 app 里的环境,每次提交前改下宏。常在河边走,哪能不湿鞋。我还真遇到过提交 App Store 的时候,有人忘记改环境的宏,app 连接到的是测试环境。这里想说的不是发布前要仔细检查,而是这种情况就不应该发生。
实际上通过 Xcode 打包手动提交也是一个充满风险的过程。因为不时会出现本地改了几行代码,提交的时候把本地的代码也提交了。带来了未知的风险。
我通过配置 Xcode 里的 scheme、target 来区分环境。利用 fastlane 来完成自动打包上传的工作。结合 Gitlab 的 CI ,配置了 Gitlab runner,从此打包只需要点击一下按钮。降低了发布的人工操作风险。
我们的组件是用 cocoapods 管理依赖的。配置了 Gitlab runner 后,组件的版本更新也放在远端工作,不再基于本地。配置了 webhook 后,每次 job 完成后 slack 的 channel 里大家都会收到消息。
用好 Testflight,注重 beta 反馈
早期业务变化频繁,没有自动化测试,只能靠人工测试保证稳定。一开始团队选择了发布企业版的包来测试。当然企业版用户可以方便的下载安装,但是也有不少缺点。最大的缺点就是这个包和 App Store 的包是两个包,不一样的 bundle id 。会导致一些跟包绑定的功能无法正常测试,比如微信登录、支付后的跳转。
我们的业务里有聊天的功能,聊天记录是只存在本地的。而且我们认为一个账号只能在同一个平台上的一台设备登录。这就导致用户测试的时候账号会从 App Store 版本登出,这样聊天记录就没了。热心用户愿意试用我们的 beta 版,但是也承担了不该有的代价。基于这点考虑在我的主导下我们放弃了发布企业版的包测试的方式。而是改用利用 testflight 测试。
Testflight 有个较大的使用门槛,需要收集用户的邮箱,之后在 testflight 里输入苹果发出的邀请码才能开始测试。很多用户嫌麻烦就退出了,运营认为这样会给测试带来很大的不便。但是冷静了心态后其实事情并没有那么糟糕。真正对这个产品有兴趣的用户不会因为要填个邮箱就放弃了。那些流失的只是普通的用户。用户使用了 Testflight 后,后续的测试包的发布也会收到更新。不会像企业版那样,只能手动的告诉用户我们有新的测试包。当 beta 测试活跃用户超过 100 个会有一个质变。这些都是积极的重度用户,一群重度用户使用你的新版本几天,至少可以保证核心业务逻辑是没有纰漏的。
之前有人问过我们使用 Swift ,线上出严重 bug 时没法动态修复,会不会带来很多问题。实际上因为有这样一环 beta 测试环节,很少出现严重的事故了。有一次意外是我们的 Swift 版本升级到 4.0 的时候,一个枚举居然对 iOS 8 设备不兼容(Xcode 并没有提示我们,苹果的锅)。那个版本也恰好是支持 iOS 8 的最后一个版本。我们的测试用户里刚好没有使用 iOS 8 系统的。
Beta 测试的时候可以让用户及时的反馈问题也是很重要的。如果我跟你反馈一个问题,又要看 app 版本,又要说在哪个页面,还要说一下我的 userID。用户的脾气也是够好的。我们在 app 集成了摇一摇反馈 bug 的功能,操作步骤,网络请求,设备信息等这些有效的信息都会一起收集起来。在后台可以方便的看到。告诉用户碰到问题摇一摇,描述一下问题就可以了。用户反馈后我们会收到邮件,及时的反馈用户用户也很有参与感。
摇一摇的功能并没有对所有用户开放,只是针对某些特定我们能联系到的用户开放。毕竟每一个反馈工程师都需要跟进,如果面向所有用户开放,我们会收到太多无效信息。常看到工程师讨论这些开发者功能的入口要藏在哪里,有的说在某个文本框输入特定字符,有的说在某个角落里点几下什么的。开发者面板的入口我选择配置在 universal link 里。这样用户不会在 app 里任何一个地方误触到达,只能通过我们告诉他的链接通过跳转到达。
坚持 Code Review,增强技术交流
Code review 是一件神奇的事情。所有有素养的工程师都觉得 code review 好,据我们所知国外很多优秀的 IT 企业都很注重 code review,但是在国内却很少看到有团队执行 code review。或者中小团队里很少看到 code review。
但是我很看重 code review,从情怀的角度讲,这里面是工程师技艺的一种传承。一个方法名起的不好,从公司角度来看,这个项目一样会 work 。但是从工程师角度来说,如果有能力,为什么不帮助那些刚开始写代码的人一些指引呢?
作为一个 leader,在 review 的时候帮助成员成长,和只是看下代码是不是能完成功能最后会引向不同的结果。看过一句很有触动的话,现在很多 leader 知道自己的工作里需要管理其他人,但是却忽略了还需要 lead 。
老实说推进 code review 确实遇到很多阻力。有团队里的也有团队外的。团队外的看法是 code review 拖慢了项目进度。我作为一个核心的开发成员,每天超过 20% 的时间是没有可见的工作产出的。有时别人写的有问题被我打回去改,一个已经完成的功能又多花了几个小时。团队内遇到的问题是,很多成员不理解这项工作背后的价值。很容易就觉得我早上没有推进项目进度,只是在坐在那里不知道在看什么。觉得我 commit 的代码不多。最后我获得了团队“代码最少产出”奖。
对于我个人而言,其实不搞 review 我肯定更轻松。这个功能我肯定能把控所有细节,这样写只是不好而已,也不是不能用。我也大可以不对他们解释为什么这样写是不好的。只要让他们按照我的 comment 改就可以了。
但是吃力不讨好的坚持是为了什么?
我刚工作的时候,出去旅游路上遇到一个大学教授。闲聊起来我说我请教你一个问题,中国古代的鞋子,会把花绣在鞋底。鞋底其他人又看不到,这样做的意义是什么。他回答说,我们做事不是做给别人看的,最后还是要过自己心里这一关。花绣在鞋底,别人看不到,你自己知道。