# 【NO.78】如何做一款面向企业客户的商用级 SDK

## 0.**导读**

我在 2008 年进入公司后，做的一直是面向 C 端用户的客户端产品—QQ，产品的可测性是很强的，虽然功能很多，但我们测试团队总是能成为产品质量的坚强后盾。2016 年我们团队加入腾讯云之后，依然在客户端方向，但所做的产品已经不再是一款软件，而是一套音视频通信领域的 PaaS SDK，即 [TRTC SDK](https://cloud.tencent.com/product/trtc) 和 [IM SDK](https://cloud.tencent.com/product/im)。

相比于 QQ 只需要做好一款 App，我们要面对的是服务好几千个客户的 App，而于此同时，测试资源又是有限的。在这种情况下，如何确保产品质量呢？

## **1.从一个小故事开始讲起**

在几年前我们刚开始做 ToB 的 SDK 的时候，曾经对接过一家做社交 App 的公司，对方的技术负责人很年轻也很务实。在商务大哥给力的努力下，客户成功完成了产品的接入，并进入线上灰度阶段。

然而，在开始灰度的那天晚上，线上用户出现了很多消息延迟大的投诉，用户的一条消息需要很长时间才能发出去。客户虽然对我们很失望，但依然很努力地在配合我们排查和修复问题。

在两天的时间里，我们给客户改进了多个版本，每次给版本的时候我们都说“已经找到问题了，这个版本肯定可以”，但每次效果都不理想。两天之后，客户的技术负责人很严肃地询问了我们一个问题：“从这两天的排障过程和修复过程来看，我想确认一下你们这是一款商用级的产品吗？”

在那个晚上，我们开始冷静地思考一个问题：**一款优秀的商用级 SDK 应该怎么做？**

## 2.**一年的努力功亏一篑**

最近教育行业被政策打压地非常厉害，但在两年前，这是个 PaaS 服务的兵家必争之地。我们有一家做在线英语教学的客户，一直在对接我们的 TRTC。这个客户对质量要求非常苛刻，他们很早便引入了赛马机制，将多家 PaaS 服务商拉入到自己的供应商集群，互为灾备，并进行质量评估，看谁的质量好就用谁的产品。

在最开始对接的时候，我们的产品质量还不是很优秀，几个关键指标跟竞品都有差距。这倒不是问题，优化总要有一个过程，于是我们一个迭代一个迭代地去跟进。因为客户的版本发布速度非常慢，所以我们需要在两个版本之间都做好问题分析和优化落地，稳抓稳打地慢慢降低工单率。就这样，经过了将近一年的时间，产品各项指标都已经很不错了，我们非常有信心在下一个版本超过友商获得质量上的领先地位。

但就在我们信心满满地等待客户上线的灰度反馈时，客户突然抛出一个问题：“你们的 SDK 有一个对音频模块的自动重启逻辑，这个逻辑会在切换线路时影响到其他供应商的音频模块， 这是绝对不能容忍的”。因为引入多家供应商赛马的意义就在于保证可以有灾备，如果一家供应商影响了其他供应商的稳定性，这个灾备便没有了意义，因此客户对我们异常失望，放量计划无限期搁置。

面对一年的努力功亏一篑，我们开始接受一个现实：**每位同事都可能会因为手滑引入缺陷，但对团队而言代价却是难以承受的，怎么办？**

回到正题，接下来我会介绍一下过去的这些日子里，我们怎么去应对这个问题。不过在此之前，我需要先介绍一下我们的产品：

## **3.我们是做什么的？**

我们团队所开发的是一套面向企业级客户的 SDK，包括用于实时音视频通讯的 [TRTC SDK](https://cloud.tencent.com/product/trtc)；用于消息通信的 [IM SDK](https://cloud.tencent.com/product/im)；用于直播推流和播放的 [LIVE SDK](https://cloud.tencent.com/product/mlvb) ；以及用于短视频录制和编辑的 [UGC SDK](https://cloud.tencent.com/product/ugsv)。
![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/202212151408332763584.png)

产品面向的客户群很多：有做泛互联网行业的，比如在社交领域长期霸榜苹果应用商店的某知名 App；也有在线教育领域的很多知名机构，教学模式包括 1V1、小班课、大班课等等；也有金融和保险领域的巨无霸，他们会使用我们的产品将现有的业务尽快地跟互联网融合；还有各行各业的中小型企业，他们虽然可能并不出名，但确实支撑我们国家互联网经济持续繁荣的基石；对了，还有做毕业设计的学生，虽然他们不会付费，但保不齐人家会在毕业后给自己的老板推荐我们的产品呢。

面对这么多行业领域的客户，有喜有忧，喜的是这是一桩很好的生意，忧的是这里有着车载斗量的压力：因为 SDK 这种形态的技术产品，如果要面向企业客户去服务，那真是**打从娘胎里一出来就注定了坎坷的一生**。

首先是**客户群体**：

- 客户所属行业分布广，教育、泛互、金融，不同的客户对产品的需求差异性大。
- 客户接入周期长，大客户在接入过程中会不断追加新需求和新特性，与此同时，客户对交付周期要求又很苛刻。

然后是**产品形态**：

- SDK 对专业性要求是比较高的，别人家的客户都只需要理解 http 的 get 和 post 就行了，俺们家的客户就得知道多线程安全、内存泄漏、前后台切换，苹果隐私合规要求，还有 android 的 gradle 配置方法和 windows 的 stl 兼容问题…
- 涉及平台众多，iOS、Android、Windows、Mac、Web，每个方向都需要很长时间的积累和沉淀。同时，在微信的强大的影响力面前，我们又增加了一个新的平台——微信小程序。

最后是**交付成本**：

- SDK 完成接入后，成不成要依赖客户的最终反馈，但往往客户的反馈周期很长，迭代周期也很长。
- SDK 版本多，平台多，这也就意味着测试工作是海量的。就说一个细节，这么多平台和版本，全量编译都需要两个小时，转测和发版就更不用说了。

面对这个问题，我们的友商做法是：**加人**。

![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/202212151408501987182.png)

当然，我们不能这么简单粗暴，毕竟粗放型经济是走不远的，我们还是得从研发体系上用集约的思想去解决问题，这就是接下来要说的重点：从研发、产品、数据和排障等四个方向去认认真真做好一款面向企业服务的 SDK 产品。
![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/202212151408583309952.png)

## **4.研发体系的优化**

在研发体系方面，我们依然遵循腾讯倡导的需求评审=>技术评审=>开发=>测试的流程。但每个环节，我们都结合自身的特点进行了改进。
![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/202212151409061184639.png)

### 4.1 怎么做需求评审？

首先是需求评审，我们团队经过这几年的打拼，总结出来最关键的一个点，就是看需求一定要看客户背后的意图。有时候客户会跟你说：“我想要你给我增加一个设置视频分辨率和码率的接口”。这个时候你要不要加呢？如果我们只是看客户的需求，那是要加的。但如果我们再问问，“您为什么需要我们加这个接口呢？” 那客户可能就会跟你说：“我觉得你们的画质不行，不够清楚，我要自己调，我要调清楚一点。” 这个时候我们就明白了，我们的需求不是“去增加一个可以设置视频分辨率和码率的接口”，而是去“提升我们的画质以满足客户的需求”。

这两者是不对等的，因为前者客户可能认为只要分辨率调到 4K 就是清晰的，但客户可能误以为“清晰度”就等同于“分辨率”，所以往往会指定一个 4K 的分辨率，却配置了一个 40Kbps 的视频码率。懂音视频的朋友都知道，这样的画面是模糊地没法看得。所以我们在简单版的 API 接口中，都不开分辨率设置接口，而仅仅是提供一个画质等级的接口，以避免客户的错误配置。但我们在得知客户的意图之后，会去了解客户为什么觉得我们画质不行，是跟哪款产品比有差距。进而分析是提升颜色矩阵转换的精度，还是在前处理的最后增加一道锐化，还是视频分辨率不匹配显示分辨率导致的问题，还是 OpenGL 的线性变换和就近变化的差异问题。

### 4.2 怎么做技术评审？

这个部分，我们一般会鼓励大家提供两个以上的方案，然后进入“左右互搏”的模式。因为很多可爱的同事本身也是可爱的急性子，只要能早点写代码，什么都是不重要的。毕竟咱们做研发的成就感，不就来自于把功能做出来看到自己的成果吗。

但我们也不断地告诫自己，我们究竟是做“一票子买卖”还是是“百年老店的生意”。如果是前者，那大可以想到哪里代码就写到哪里；但如果是后者，则需要我们综合考虑多个方案，选择更能可持续发展的方案。

要知道在 ToB 这个领域，我们一不注意就会把自己陷入到做定制需求的套路里。面对业务压力，一开始这样是很解渴的，但随后的维护成本就让自己彻底吃不消了，每天除了救火什么都干不了的团队，也就失去了创造新价值的能力。

### 4.3 怎么做代码合入？

在代码合入方面，我们团队在很早的时候就引入了一套非常严格的代码评审流程，即三级评审：

- CR 一级：模块的维护者来 review，这一级的目的是让模块的稳定性能够得到保障。毕竟在别人家的田间地头种自己的庄稼，你总不能背着这块地的主人搞小动作不是。
- CR 二级：自己的 leader 来 review，这是我们整个 CR 的核心基石。很幸运团队有像 taopu 一样负责和认真的 leader，会非常细致的 review 大家的每一行代码，并积极提出意见和建议，在 CR 中提升大家的技术水平。
- CR 三级：总监负责 review 和 代码合入，这一步更多是抽检，看看哪些同事是真正地爱这款产品，哪些同事则是不那么负责的架构破坏者。

![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/202212151409182850117.png)

### 4.4 怎么做功能测试？

最近半年在测试团队，尤其是 svein 和俊哥的大力支持下，我们的自动化测试进步极大。不管是 native sdk 还是 webrtc sdk，自动化测试都能覆盖掉很多刁钻和难以手工覆盖的部分。比如一次通话过程中几十次的“进进出出”，或者是频繁的切换某个状态，这些都是以往手工测试很容易把人逼疯的部分。益于测试团队的持续投入，目前我们的自动化测试系统已经小有成绩。要知道，构建一个面向音视频功能的自动化测试体系，那难度可是非常高的。仔细想想就知道这里面有多少破事儿要解决：

- 怎么确认画面出来了？
- 怎么确认声音是正常的?
- 怎么构造复杂的测试流程和测试序列?
- 怎么保证测试环境的稳定和不被干扰?
- 还有最艰苦的：怎么找到足够多又耐操的手机，尤其是水果牌的。

通过需求评审、技术评审、代码审查和自动化测试的多重保护，我们最近已经很久没有再发生前面说的第二个故事里的事情了。即使有些同事一时手滑引入了一些问题，也大都能在 SDK 交付前得到暴露，只是目前我们并不能将这个概率降低到 0% 而已。

![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/202212151409286401567.png)

## **5.产品体系的优化**

作为一款自身不带界面的 SDK，要做到产品体系的优化，就只能去优化技术本身，但这是枯燥且不好度量的。俗话说得好，“文无第一，武无第二”，说的就是评判标准的问题。这就好比你画一幅画，如果没有老师指点你怎么才算好，那就会很难度量自己这段时间是水平提高了还是退步了。不然人家丢勒一个德国人，干嘛两次跑到意大利的威尼斯去学画画；又不然怎么会出现很多画家都是在那啥之后才有人开始欣赏他们的作品的呢？

所以说，作为一款面向 ToB 客户的 SDK 产品，要提升产品质量，就得有一些手段和方法，我们是这么做的：

![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/202212151409386555801.png)

### 5.1 方法一：通过场景落地来验证产品质量

我们做 TRTC SDK，我们的客户拿 TRTC SDK 的能力去做合唱，去做 K歌，去做语音聊天室，去做视频直播。那我们就只是做好自己的一亩三分地吗？

当然不行，所以我们自己也实打实地开发了一些面向行业场景的 App（也可说是 Demo），比如合唱、语聊、教育、直播等等。并在这些场景的开发过程中，不停地寻找产品的问题和不足，并持续打磨，以确保在产品交付客户之前，在产品体验上就已经达到了一个很好的水平。

比如我们在开发在线合唱的场景时，就经常有人找我问：“rex，我跟你确认一下哈，咱们团队里的同学，究竟都是写代码的程序员，还是想要通过《我是歌手》来改变人生的麦霸？”

![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/202212151409558692327.png)

### 5.2 方法二：通过数据体系来评估产品质量

构建一套靠谱的数据体系很重要，这就是把“文无第一”的事情变成“武无第二”。通过数据体系，让所有的指标都变成可以比较的数字，并且依托数据分析系统，不断地提升产品质量。

![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/202212151410046803486.png)

虽然这个思路大家都很清楚，也都在各自的产品中有所落地，毕竟咱们腾讯的产品团队，谁家还没有一个负责数据运营的同事呢？当然有些比较大的业务，都是有自己的数据运营团队的。

但还是得说，这个事情在 toB 的方向上不好做，难在两点：

- **不同的客户关注的点是不一样**：比如教育客户关注的是稳定性，电商直播关注的是清晰度，秀场直播关注的则是音质。如果我们给一个在线教育客户去过度地优化画质，客户不仅可能不买账，还可能因为我们的优化影响了其他指标而弃用我们的产品。
- **音视频的表现不是简单地靠 DAU、成功率来衡量的**。比如“切课率”这个指标，影响因素非常多，比如网络波动呀，硬件发热呀，麦克风阻抗大呀，显卡驱动不匹配呀，还有可能是用户心情不好砸键盘呀。就说我们有个客户，发现上课的声音效果不好，结果客户很负责，亲自到了学生家去确认，最后发现是 iPad 的保护套把麦克风给遮住了，你说这找谁说理去？

## 6.**数据体系建设**

面对上述挑战，我们还是得从技术角度去解决问题，毕竟靠堆人是不行的，这生意得做出毛利率才能长久地坚持下去。

庆幸的是这方面我们还是做得不错的，尤其是我们团队一向比较在意数据，团队里还有一等一的聪明脑袋负责数据体系的建构。比如我们在自己的引擎内部的各个关键模块都做了数据“挂节点”。这些模块会每时每刻将近百个技术指标以一秒一次的频率反馈给统计模块，在统计模块进行汇总之后，再实时上传到服务器上。

![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/202212151410141341597.png)

基于这些海量的数据信息，仅仅靠 group by 和 count 、where 等 SQL 语句做简单的统计分析是肯定没用的，因为这样的分析得不出任何有价值的信息。

比如一次糟糕的通话体验，可能出现过一次 2s 的卡顿，但是这些数值如果仅仅是用来做大盘平均分析，那这次 2s 的卡顿就“淹没”在了海量的通话数据里，你拿到的最终的平均值甚至不会有小数点上的一个波动。

针对这个问题，团队中的 xuanyi 和 yuting 两位同事，基于对以往 badcase 的经验综合分析，构建了一套“根因分析系统”，并用了将近半年的时间，不断地打磨其准确性。到目前为止，这套系统对于 badcase 的分析已经接近人工挨个 case 分析的准确性，为团队节省了不知道多少人力。

![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/202212151410239684953.png)

## 7.**排障体系**

回到最初的小故事，客户之所以怀疑我们的产品不是一款商用级的产品，最大的问题就在排障体系上。因为客户也不是最终用户，客户在面对自己用户的反馈和投诉时，往往也是很难拿到第一手信息的。如果我们将排障过程演化成了：我们 <=> 客户<=>客户的用户，之间的复杂关系，这个事情就很容易引发矛盾和冲突。

所以我们在接受了早期的失败教训之后，就励精图治建设了一套商业级的排障系统。经过这几年的努力，这套系统已经越来越强大了，也承载了越来越多的能力。目前已经能够做到分析过去两周内任何一个用户的任何一次体验问题，并能够定位到技术层面的缺陷或者环境方面的问题。

![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/202212151410342950122.png)

于此同时，在线日志和离线日志系统的双重保障，也让排障的信息变得更加容易获取。以往比较困难的线上死锁问题和调用时序问题，也开始不再那么可怕和束手无措。

![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/202212151410438997892.png)
当然，面对这么多的客户，靠一个团队的人力是不可能搞定数千个客户的技术支持和售后服务的，靠两个也不行。不过作为一款腾讯云上的老产品，我们的 TRTC 和 IM 很早就接入了腾讯云的安灯系统。借助安灯的问题跟踪和信息流转能力，中小客户的问题也得到及时的处理和沉淀。

![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/202212151410539361168.png)

## 8.**总结**

从 2016 年加入腾讯云，团队到今天已经走过了五年，我们用了五年时间去学习如何做一款商用级的 SDK。虽然现在来说，我们做得还不够好，但至少可以回答几年前客户的质问，我们现在还是有信心并且有能力做好一款商用级的 SDK 产品的，而且不止一个。

原文作者：rexchang，腾讯 CSIG 客户端开发工程师

原文链接：https://mp.weixin.qq.com/s/DcDZad4UP4VUSWRnfVy9MQ