线下越重,线上需要越轻,这个轻指的是轻便轻巧和简洁易用,通过前面几章小菜技术与产品历史介绍,我们了解到 B2B 生鲜领域在线下是如此之重,那么在交易场景线上化的过程中,端的移动化就势在必行,试想一下,让菜市场摊位老板人手一台笔记本点开网页选购支付,让采购销售抱着电脑去拜访客户,一边聊蔬菜行情,一边打开笔记本进行记录,有没有一种回到世纪初的感觉。
产品的移动化,这将是我们展开这篇文章的背景,我们会先了解小菜的产品托管在哪些端上,然后感受这些端带来的挑战,最后是我们面对这些挑战所采取的策略,以及整个小菜前端团队历练后的技术成长和沉淀,和我们对于自己的一个评估和对未来的展望,本文将采用最通俗易懂的方式陈述,会略有繁琐,但力求对技术新人也足够友好。
一、小菜大前端的端有哪些
小菜早期围绕着蔬菜销地以客户集单批发的模式摸爬滚打几年,从上游的蔬菜供应商到下游批发市场的摊位老板,在这个长长的链路中,我们诞生了这样几款线上产品来服务于不同的人群和场景,之前文章中也有介绍,这里再汇总一下,共 7 款 App:
- 宋小菜 服务于销地批发老板的下单工具
- 宋小福 服务于小菜内部销售团队的 CRM 销售管理与客户管理工具
- 宋小仓 连接司机-物流-采购-销售的蔬菜在途位置监控工具
- 采秘 服务于小菜内部采购团队的蔬菜品类采购工具
- 麦大蔬 服务于上游蔬菜供应商的大宗农产品交易平台
- 宋大仓 服务于上游囤货配资的进出库管理平台
- 行情宝 服务于产销两地的行情采集和预测工具
- ERP 后台管理系统
到目前为止,我们已经看到小菜的 7 个移动端 App,以及一个复杂的后台管理系统,这些都跟前端工程师息息相关,除了这些,还有 2 个重要的内部产品,就是:
- 大表哥 数据报表系统
- 大瓜子 市调模板配置系统
关于报表系统后文还有介绍,我们再为前端增加一个服务的产品场景,就是微信生态内产品,比如公众号或者小程序,它的技术栈和运行环境跟原生 App 和 PC 都不同,虽然小程序可以带来更多的业务可能性,也会对前端带来更大的挑战。
我们把这些端合并一下,小菜前端要服务的端或场景:
- 移动端(iOS/Android App/小程序)
- PC 端(ERP)
- 工具端(大表哥数据报表)
- 大伯伯(谐音打包包) 实现 App 选仓库选分支选环境配置的自主打包与推包系统
- 大表姐(来自饥饿游戏,寓意开工没有回头箭) 实现 6 款 App 解包差分后下发热更新包的发布系统
- 姑奶奶线上异常汇集分析与与 Bug 定级指派系统
- 大舅子 向下调用微服务接口向上提供 GraphQL 查询能力的数据聚合服务
- RGB 用户使用 App 的 PV/UV,以及业务数据监控相关的可视化平台
- 110 解决端异常收集与报警需求
- 堂哥工作台 团队记录资源分配与 redmine 同步的自动化周报系统
- ITms 解决内部 App 安装测试的配置生成和预装服务
- ...
先上小菜端上若干产品和工具的技术栈图,帮助大家理解我们的技术理念:
文章图片
二、多端带来的挑战
1. 【物理现状】移动端的碎片化 古典互联网时代,因为要兼容 IE678 而痛苦不堪,Hack 黑魔法经验基本代表前端水平,如今互联网早已移动化,我们理想中的移动端开发,看上去是可以大胆使用新语法特性,只需要做好尺寸兼容就好了,但事实并非如此,不仅在移动端的浏览器不是如此,在移动端开发 RN App 也是如此,这是我们某一款 App 一段时间内,所收集上来的手机厂商分布:
文章图片
可以发现 Android 的碎片化非常严重,每一个厂商下面有不同时期推出的不同型号的手机,这些手机有着不同版本的操作系统,不同的分辨率和用电策略,不同的后台进程管理方式和用户权限,要让一款 App 在哪怕头部 40% 的手机上兼容,都是一件艰难的事情,这个客观物理现状叠加下面的社区现状,App 质量保证这件事情会变得雪上加霜。
2. 【社区现状】技术框架的不稳定性 回到本文的开头,我们在长链路的 B2B 生鲜场景中,为了更快更轻,开发出 7 款 App,而且将来随着业务场景的拓展会诞生更多独立 App 甚至是集大成的 App,所以技术选型不太可能选择原生的 Java/Object-C 开发,尤其对于创业公司,6 款 App 得需要多少名原生开发工程师才能搞定,高频繁重的业务变化又怎样靠堆人来保证?
想清楚这些,一开始我们就调研 ReactNative,并最终全部从原生切换到了 RN,通过跑过来的这 3 年来看,使用 RN 为公司节约了大量的人力成本同时,也尽可能的满足到几乎所有的需要快速迭代的业务场景,又快又轻,成为宋小菜大前端团队做事的一个典型特征。
但换一个角度看,就是带来的问题,又快又轻的背后是 RN 版本的飞速迭代,截止到目前,也就是 2018 年 6 月份,RN 还没有推出一个官方的正式的长期维护的稳定版本,什么意思?就是 RN 目前依然处在不稳定的研发周期内,我们依然站在刀尖起舞,用不稳定的 RN 版本试图开发稳定的应用,三年走过来,我们在 RN 的框架里,多少次面对旧版本局限性和新版本不稳定性都进退不得,旧版本的 Bug 可能会在新版本中修复,新版本引进则会来新版本自己的问题。
除了 RN 自身版本,还有第二个问题,围绕着 RN 有很多业界优秀的组件,但这些社区组件甚至官方组件,都不一定能及时跟进最新的 RN 版本,同时还能兼容到较老的 RN 版本,所以 RN 升级导致的组件不兼容性,会引发你 Fork 修改组件的冲动,但这样会带来额外的开发成本和版本维护成本,取舍会成为版本升降的终极问题。
在国内开发,还有第三个问题,就是中文文档缺乏,社区资源匮乏,参考文献陈旧,可拿来主义的开源工程方案甚至社区线上线下会议分享都很缺乏,一个不小心就会踩坑,这就是 RN 社区的现状,我们在刀尖浪花上独步,App 选型背后的技术栈稳定性则成为悬在头上的一把铡刀,你不知道什么时候会咔嚓一声。
3. 【人才现状】人员能力的长短不齐 我们知道有一个词叫做主观能动性,表示没有条件创造条件也可以上,这个词的主体就是人,聊完移动端设备现状和社区现状后,我们来聊聊人的问题。RN 在国内真正开始普及使用,是从 2015 年开始,也就意味着,到 2018 年,一个 RN 工程师也就只有 3 年的工作经验,而 RN 的 “Learn once, write anywhere” 也刺激着一切 Care 人员开支, Care 产品研发投入性价比的公司纷纷跳水研究 RN,争抢 RN 人才,RN 是前端中的移动前端,前端有多抢手,那么 RN 工程师就比它还要抢手。
这导致基本上 RN 工程师,很难靠外部招聘,只能靠内部培养,这也是小菜前端的成长历程,我们有 2 名资深 RN 工程师,一个是从服务端 Java,一个是从原生 Android 开发转过来的。如果 RN 人手不足,产品支持的力度和速度就一定会遇到瓶颈,这就是我们曾经面临的问题,就是人才现状,外招数量不足,内培速度有限,RN 工程师的数量和能力就时不时成为公司业务扩张的瓶颈。
4. 【公司现状】高密集业务的交付质量 作为工程师,我们有很强的自尊心和不容挑战的代码洁癖,但在一个创业公司里面,甚至大公司的一个创业团队里面,我们需要对接一些关键的业务节点,冲刺一些特定的时间窗口,并且要及时响应多变的业务,和业务背后多变的产品形态,这都会带来非常密集的需求队列。
这些密集的需求队列对我们的代码实现质量有非常高的挑战,一个组件用 5 分钟思考如何抽象和用 50 分钟思考,实现后的稳定性、兼容性都是不同的,如何保证产品按期交付上线,会是摆在我们面前一个非常关键的命题,而这个难题之外,还有一个更难的命题等着我们,那就是如何保证交付不延期的同时,还能保证交付质量。
要知道,如果一个项目代码赶的太毛糙,后期维护起来的成本会是巨大的,甚至只能用更高的成本重构重写。本质上,再次重构就一定是公司在为早期的猛冲买单,为这些技术债买单,如何不去买单或者如何用最小的成本买单,这跟我们早期的业务密集程度,交付周期,质量把控有很大的关系。
综上,移动端碎片化所带来的兼容难度,RN 框架的局限性,版本间差异带来的不稳定性,技术社区资源的匮乏和前端团队技术能力掣肘,再叠加上高密度的业务排期,让前端开发这个本来很酷的事情,变得晴雨不定。
这些避不开的现实,是绕不过去的坎儿,是搭建团队必须搞定的基础,我们想要把 B2B 生鲜的线上线下场景通过端产品关联起来,想要通过前端团队的用户侧输出从而让这些产品落地,就必须面对这些现实挑战,而应对这些挑战,首先必须搞清楚有哪些挑战,搞清楚挑战以后,我们就会认识到,首当其冲的事情,是去搭建 B2B 生鲜公司的前端技术栈和人才梯队,现在我们进入到本文的重点。
三、如何应对井喷的挑战
1. 前端梯队如何搭建 创业公司的技术团队,本质上就是人和事,用合适的人搞定特定的事,人才的瓶颈就是这家公司产品落地速度和上线质量的瓶颈,因此人是第一位的,对于前端团队来说,如何一步步形成有综合战斗力的团队,取决于搭建什么层次的前端梯队,如果所有人一视同仁,培养同样的能力栈,发挥同样的兴趣向,跟进同样的业务线,那么这个梯队的扁平就会带来致命的团队瓶颈:能力可复制但不能互补,能力可递进但很难跨越,不能互补和很难跨越会导致团队内的技术路线过于单一,技术思维趋于固化,至于技术储备的丰富性和技术沟通带来的碰撞就更有限,最终导致人做事越来越机械化,甚至失去最初的技术初心。
那么小菜前端到底如何搭建,还是要从公司的人员、业务和技术现状出发,由于端的碎片化和技术框架的不稳定性,就必须在质量保障上投入巨大的人力保证产品可用,而人才能力局限性和数量的匮乏,就跟产品的质量保证成为了天然的矛盾,不可协调,代码撺太快,线上天天都是 Bug,代码撺太慢,产品节奏跟不上,至于跟工程师天天宣讲要小心小心再小心,能起到的作用也不大,因为工程师本身的能力也是参差不齐的,所以就必须把团队先拆成两部分,一部分做基建支持,一部分做业务支持,基建支持的同学研发整个团队的工具脚手架、抽象和打磨团队的基础通用组件、长期维护项目的通用架构,这些投入都会反哺到业务支持的同学,业务的同学可以放心大胆的基于基建的成果做上层业务开发,稳定的工程基础有了保障,上层的业务代码做质量保障难度就大大降低了。
除了分出来人做基建,做业务,还需要有核心的技术骨干,做技术前瞻性的研究,为团队 3 个月后,半年后,甚至 1 年后的技术方向,做必要的调研、测试和实验性开发,因为对于刀耕火种的早期技术团队,从原始人到迈向外太空跨空间作战,这中间还差着很多个关键的技术迭代节点,这些关键的技术迭代节点,一部分是靠外招技术专家和资深的工程师来输血发力,还有一大部分是需要靠团队内部长期的积累沉淀,也就是人才内部培养。
我们总结一下:
- 基建的同学负责输出工具系统、基础组件、流程规范,保证内部效率最大化和质量的有效保障
- 架构的同学负责攻克技术底层难点,调研先进技术,升级团队技术架构,沉淀技术方案,锁定和推进团队未来技术方向
- 业务的同学负责产品跟进,高频使用基建产品,并通过反馈来优化团队的技术基础设施,同时基于业务来抽象更多的基建需求
上面是人员的分工,还有三个重要的保障,这里不做引申,只列举一下:
- 团队人员的兴趣栈、能力栈和业务要尽量匹配
- 团队人员的阶段性目标、长期规划要跟进公司的职业晋升路线和能力模型
- 团队要有持续性的内部技术互动分享和对外的技术理念、方法方案分享
最后一点,就是资源流转,架构的同学,基建的同学和业务的同学的梯次关系是从下到上,越下越接近技术本质,越上越接近业务结果,越向下需要越好的技术实力,越向上需要越好的业务理解能力,这两个能力都是核心能力,需要让团队成员沿着梯队关系慢慢流动起来,业务中技术能力好的同学可以有机会沉下来做做基建,长期埋头基建的同学可以有机会上去做做业务,业务理解不错技术沉淀又好的同学可以继续沉下去参与架构,这样团队内部的同学都可以有多样性的技术场景和业务场景,一旦有同学请假、陷在别的业务不能抽调,马上就有同学可以补位进来开发,不会影响到产品上线节点。
关于团队如何搭建,目前小菜是走到了这个算是 v1.0 的阶段,未来还有更多挑战,也会带来更多的基于公司现状的新调整,无论如何变迁,方法论我们先沉下来:
- 人才梯队要有层次:基础架构、基建和业务上层等
- 人才成长要有规划:兴趣栈、能力栈和公司关系
- 人才能力要有扩展:单人能力和互补后的团队能力
2. 如何做技术选型
【前端|生鲜 B2B 技术平台的前端团队该如何搭建(B2B 技术共享第七篇)】技术选型是一个行业老话题了,方法论也有很多,在小菜我们遵循的是:技术方向性预研大踏步,业务基建型开发小碎步,前者尽可能激进,后者尽可能保守,比如 数据报表系统,我们激进的采用 GraphQL 来解决 SQL => 页面 Dom 的链路问题,在宋小福 App 上面,我们就求稳的采用 v0.48 的 ReactNative 版本,而不是用当时较新的 v50+ 版本。
在做技术选型之前,还有一些比较重要的基础性问题需要搞定,那就是团队技术动作的一致性,这个一致主要包含两点:
- 代码规范共用一套
- 仓库合作方式共用一套
再回到技术选型本身,抛开激进保守的大踏步和小碎步,我们需要回到技术本质和工程师的本质来看待如何选的命题,技术的本质是效率,工程师的本质是兴趣,如果这一套技术选型不能带来效率,如果工程师普遍不感兴趣,那么通常这一个选型我们不会采纳,我觉得这一个主观一些的标准,大家可以参考,但这里面也要权衡好历史包袱、维护成本,上手难度等这些客观现实,如果一个新技术会带来革命性的效率提升,那么即使有上手难度和维护成本,我们也会果断入坑,比如 GraphQL 对于数据报表对于解放前后端有大幅度的提升,我们会果断入坑大力推行,如果一个技术对于团队是锦上添花,那么我们会慎重选用,比如 TypeScript,可以给工程稳定性带来了较大的保障,但我们只选择在热更新这种 RN SDK 和 Server 端的去集成,而不是一下子推广到整个团队项目中铺开用,这里面就会考虑到实际得到的好处,以及历史包袱和上手难度,反复权衡后并没有带来更大的价值,所以这两类场景的推行和不大力推行,就又不会太依赖于工程师的喜好兴趣。
那么我们技术选型后的结果是如何呢?
文章最开始的那张图,里面就是我们的技术栈,这里再做一下总结:
- 工具类:强依赖 Node,多而杂的其他技术,如:MongoDB/Redis/MySQL/Shell/Python
- 业务类:强依赖 React/ReactNative,适度集成其他技术,如: Redux/GraphQL/Apollo
- 框架类:除了 React 全家桶会谨慎选择,Node 端框架则相对宽松:Koa/Thinkjs/Eggjs
3. RN 的 App 工程如何架构 小菜的主要产品类型,尤其是对外的产品,主要是 RN App,而且数量较多,那么 RN 项目的合理架构就变得尤其重要,我们这里探讨下小菜前端在 RN App 上面的沉淀,涉及到原生层面的技术细节太多,这里暂不做讨论。
首先,我们在构建 RN App 工程时需要关注这几个关键要素:
- 配置管理
- 静态文件管理
- 网络请求
- 组件管理
- 路由管理
- 数据缓存
- App 的热更新
- 数据搜集
- App 本身的一些配置
- 所使用三方插件的配置
import { pluginAConfig } from '../../../../../config'
这样就造成了阅读性很差且代码不美观,因此我们可以使用 Facebook 的
fbjs
模块提供的一个功能 providesModule
:
//config.js
/**
* config for all
* @providesModule config
* 使用 providesModule 将 config 暴露出去
**/
import pluginAConfig from './plugin_a_config'export default {
pluginAConfig
}// 然后在其他文件中调用
// A.js
import { pluginAConfig } from 'config'
这样就能很方便地在 App 的任意一处使用 config 了,但是我们要避免滥用
providesMoudle
,因为使用了 providesMoudle
进行声明的模块的源码,想要在编辑器中使用跳转到定义的方式去查看比较困难,不利于团队多人合作。__静态资源__泛指会被多次调用的图片或 icon,我们一般在 RN 使用图片时是直接引用的:
import { Image } from 'react-native'render(){
return (
)
}
当图片需要在多处使用时,我们可能会将这些可能会被反复使用的图片统一管理到
assets
文件夹中,统一管理和使用,但是当需要使用图片资源的文件嵌套较深时,引用图片就变得麻烦:
render(){
return (
)
}
这个问题与配置管理的问题一样,可以首先将图片资源按照类型进行分类,比如 assets 文件夹下有 button/icon/img/splash/svg 等,每一个类型的结构如下:
- icon/
- asset/
- index.js
其中
asset
文件夹保存我们的图片资源,在index.js
中对图片进行引用并暴露为模块: // index.js
export default {
IconAlarmClockOrange: require('./asset/icon_alarm_clock_orange.png'),
IconAvatarBlue: require('./asset/icon_avatar_blue.png'),
IconArrowLeftBlue: require('./asset/icon_arrow_left_blue.png'),
IconArrowUpGreen: require('./asset/icon_arrow_up_green.png')
}
然后再在
assets
文件夹下编辑index.js
,将所有的图片资源作为assets
模块暴露出去,为了避免和其他模块冲突你可以修改模块名为xxAssets
// assets/index.js
/**
* @providesModule myAssets
**/
import Splash from './splash'
import Icon from './icon'
import Img from './img'
import Btn from './button'
import Svg from './svg' export {
Splash,
Icon,
Img,
Btn,
Svg
}// A.js
import { Icon } from 'myAssets'render(){
return (
)
}
这样,我们就能很方便地将分散在项目各处的图片资源统一到一个地方进行管理了,使用起来也非常方便。
__网络请求__这块,react-native 使用 whatwg-fetch,我们也可以选在其他的三方包如 axios 来做网络请求,但有我们在开发中遇到过一个问题,那就是我们明明已经在代码里已经修改了 cookie, 但是每次请求可能还是会带上之前的 cookie 从而造成一些困扰,所以这里推荐一个实用的组件
Networking
:import { NativeModules } from 'react-native'
const { Networking } = NativeModules// 手动清除已缓存 Cookie,这样就能解决上述的问题了
Networking.clearCookies(callBack)
当然,
Networking
的功能不止于此,还有很多其他有趣的功能可以发掘,可以直接用它来包装自己的网络请求工具,还支持 abort
,可以参考 源码 来具体把玩。使用 RN 开发 App 本身效率就比较高,如果想要继续进阶就要考虑组件化开发,一旦涉及到组件化开发,就不可避免地会涉及到组件管理的问题,这里的__组件管理__比较宽泛,它实际上应该指的是:
- 组件规范
- 组件类型划分
- 组件开发标准
// mystyle
import { PixelRatio, Dimensions } from 'react-native'
import EStyleSheet from 'react-native-extended-stylesheet'const { width, height } = Dimensions.get('window')const globals = {
/** build color **/
$Primary: '#aa66ff',
$Secondary: '#77aa33',
$slimLine: 1 / PixelRatio.get(),
/** dimensions **/
$windowWidth: width,
$windowHeight: height
}EStyleSheet.build(globals)module.exports = {
...EStyleSheet,
create: styleObject => EStyleSheet.create(styleObject),
build: (obj) => {
if (!obj) {
return
}
EStyleSheet.build(_.assign(obj, globals))
}
}// view.js
import MyStyleSheet from 'mystyle'const s = MyStyleSheet.create({
container: {
backgroundColor: '$Secondary',
width: '$windowWidth'
}
})render....
这样,我们就能在开发的任意插件或者 App 中直接使用这些基础属性,当某些属性需要修改时只需要更新
mystyle
组件即可,还可以衍生出主题切换等功能,使得开发更加灵活。关于组件类型我们会抛开三方组件以及原生组件,因为一旦涉及到这两者,需要写的东西就太多了,我们将组件按使用范围分为通用组件和业务组件两大类。
首先什么是业务组件?即我们在开发某个业务产品常用到的组件,这个组件绑定了与业务相关的一些特殊属性,除了这个业务开发以外,其他地方都不适用,但是在开发这个业务时多个页面会频繁地使用到,所以我们有必要将其抽象出来,方便使用。
什么是通用组件?即可以在 App 范围内使用甚至于跨 App 使用的组件,这里可以对这个类别进行细分,我们将能跨 App 使用的组件上传到了自己的搭建的私有 npm 仓库,方便我们的 App 开发者使用,同时,具有 App 自己特色的组件则放到工程中统一管理,同样适用
providesModules
暴露出去。制定一整套组件开发标准的是很重要的,因为很多组件开发可能是多人维护的,有一套既定的规范就可以降低维护成本,组件使用的说明文档的完善也同样重要。
开发 App 就不可避免地会遇到如何管理页面以及处理页面跳转等问题,也就是__路由管理__问题,自从 Facebook 取消了 RN 本身自带的 Navigator 以后,许多依赖于这个组件的开发者不得不将目光投向百花齐放的社区三方组件,FB 随后推荐大家使用的是 react-community 推出的 react-navigation ,现在这个路由组件已经独立出来了。我们在开发时就是使用的这个组件作为路由管理组件,只不过是在其基础上做了一些定制 ,使得使用更加简单,部分跳转动作更加符合我们的产品场景,推荐大家使用这个组件。当然,除去这个组件还有很多其他的组件可供选择:
- 基于
react-navigation
进行深度定制的 react-native-router-flux - 基于原生解决方案的 react-native-navigation
- airbnb 的 native-navigation
一般情况下需要缓存的数据基本上就可能是我们会在 App 很多地方都会使用到的全局数据,如用户信息,App 设置(非应用层面的设置)等,RN 提供一个 AsyncStorage 存储引擎,通常的使用方式是对这个数据引擎进行包装后暴露出符合我们要求的读写接口。这里推荐另外一种使用方式:
既然需要缓存的数据可能是会在 App 很多地方使用到的全局数据,那么我们可以将这些全局数据使用 redux 来进行管理,而利器 redux-persist 则能让我们很优雅地读写我们的缓存数据。
同时,如果对
react-navigation
进行合理的定制,接管其路由管理,那么我们还能实现保存用户退出 App 之前最后浏览的页面的状态,用户在下次打开 App 依然可以从之前浏览的地方继续使用 App,当然,这个功能要谨慎使用!App 的版本更新,RN 除了传统的 App 更新外还有一个热更新的可选项(传统 App 更新也有热更新,其原理就不太一样了),社区大多数人都推荐使用 codepush 来进行热更新,至于其后端解决方案 貌似已经有了一个 code-push-server ,我们是使用自己的热更新方案,其原理就是在不更新原生代码的基础上更新 JS 代码和静态资源文件。
搜集的 App 使用数据(包括异常数据)并对此分析,根据分析来定位问题是保证 App 质量的有效手段之一。你可以选择自己搭建一套数据搜集服务,包括客户端 SDK 和服务端搜集服务,或者选择市场上已有的工具,目前较为成熟的收据搜集工具比较多,如友盟,mixpanel, countly 等等,在此不作赘述。
总结一下,一个 RN App 架构应该要保证 App 的运行稳定以及开发的便捷。运行稳定这一方面,除了从 JS 层面(如单元测试,JS 错误上报等)保证之外,很大程度上还要依赖于原生层面的处理,所以团队里面要有同学的精力可以投在原生研究上面,至于开发便捷,我们尽量将复杂重要或者简单繁琐的操作在构建工程时就做掉,这样也可以大幅度提高我们的开发效率,降低开发者之间的合作沟通成本。
4. 效率协同工具如何打造 效率协同往往不分家,效率宽泛一点,就是又快又好,协同宽泛一点,就是顺滑无内耗,而且效率协同在不同的场景下,一定有不同的表现,所以效率协同一定要具体到某一个场景才有意义,比如:
我们要发布 6 款 RN App 中的若干款,在一周内的若干天发布,由若干人自行打包测试自行发布,那么这里面就有巨大的协同问题,同时还有一些效率问题,如果一个同学进来改了 3 行接口调用代码,他至少要有这几个阶段:
- 开发阶段本机切新分支调试
- 测试阶段打一个连接测试环境的包测试有效性
- 测试完再打一个连接正式环境的本地包测正确性
- 最后再打一个连接正式环境的用来热更新的包进行发布
文章图片
这里面的一个圆点,就代表一个编译后的包,比如 A 打出来的不需要 Debug 的连接正式环境的需要热更新的 iOS 的 ipa 包,那么 A 的这个包,跟 B 打出来的不需要 Debug 的连接正式环境的需要热更新的 iOS 的 ipa 包,即便是在同一个仓库的同一个分支,也不能保证 100% 一模一样的包,原因在于,这些本地打的包,还会受到 Node/NPM 版本(语义化),XCode 版本,原生热更新版本控制等等因素影响,导致这个包自身很容易出问题,甚至是一些人肉引发的分支和人工上传等等的影响,也会导致这个包发布出问题,举一个我们真实发生过的故障,A 打完包,把包文件钉钉传给 B,B 在发布的时候,选择本地文件时候选错了一个老版本直接发布上线,导致线上部分用户直接版本回退,我们后来不得已采用紧急回滚,才把影响范围控制住。
上面大篇幅的介绍打包的这个场景,是小菜前端早期非常痛苦的一个场景,协同方式和规范无论我们如何三令五申总是避免不了人肉的问题,一旦出问题,就是大问题大故障,那么这时候,就必须投入基建的力量来打造一款或者一套流程工具,通过工具一劳永逸的解决这些主要的协同问题,把琐碎人肉的事情交给机器去做,机器比人做的快,也比人做的好。
我们来总结一下,团队跑一段时间,一定会挤压一些问题,这些问题不可以视而不见,也不可以拿业务支持第一这样的借口来无限期推迟解决问题,而是时不时评估一下,有没有可能通过系统和工具,来约束一些行为,来取代一些人肉工作,进而可以一劳永逸的解决掉一些问题,一旦决定去解决了,那么如何打造协同工具就变得顺水推舟了,因为工程师最擅长的干的事情之一,就是造轮子造工具。
小菜前端造了哪些轮子哪些工具呢,文章最开始就已经列出来了,这里再陈述和解释一下:
- 大伯伯(谐音打包包) 解决人肉打包带来的协同问题
- 大表姐(代号 Laurence - 来自饥饿游戏) 解决人肉发布、人工维护版本,问题追溯定位的效率问题
- 姑奶奶 解决去多个三方平台查看异常日志和 Bug 反馈指派的人肉效率问题
- 大舅子 解决前后端耦合在 Restful/Mock/冗余的接口 的合作效率问题
- RGB 解决纯数字报表分析页面访问数据和用户行为的效率问题
- 堂哥工作台 解决组员与 Leader 日报周报难回忆带来的书写效率和后期回顾问题
- 小菜图书馆 解决小菜书架上面借书还书靠人肉记录维护的效率问题
- ...
- 前端比较依赖于后端的接口定义,后端为前端 Mock 做完后,前端才方便的进行页面中的数据替换和逻辑判断,有等待成本
- 前端复杂多变的页面会影响到后端的接口复杂度和体积,页面上的字段增减,都会反映到接口的字段增减,接口本身变得不稳定,会带来很多隐患点,比如接口体积越来越臃肿,或者接口有多个版本,一旦接口文档更新没跟上,会导致后期的同一个接口的不同版本之间,增加调用出错概率等等
文章图片
大舅子目前的架构是放到网关下面,网关层做一些鉴权和安全的处理,向下把一个 GraphQL 的请求转发给大舅子,大舅子上面根据这个 Query Type 对应的 Resolver 去调用下层的服务接口,下层可能是另外一个 GraphQL 服务,也可能是微服务,也可能是数据库,兼容度很高,无论是哪一种,大舅子的角色扮演就是配置和聚合:配置客户端上页面对应的数据类型,嵌套关系和数据结构,向下连接和聚合不同的数据源。
内部的开发正式环境关系图如下:
文章图片
这个事情并不新鲜,多年前,Nodejs 就在扮演数据聚合层的角色,把多个 API 聚合成一个 API,或者打散一些 API,聚合成新的 API,但本质上依然是向客户端提供 API,这种 API 依然是面向页面,可以看做是页面驱动的 API,大舅子因为整个建模基础是 GraphQL,所以页面和数据结合的权利,交给了客户端自己去做,它需要什么数据,就在客户端声明什么数据结构,带来的好处很多,这里列举两条我认为有价值的:
- 前端可以不再局限于接口评审阶段,可以继续往前提到数据库/表结构评审阶段进入开发流程,在表结构评审期就能拿到字段定义与含义,从而再大舅子上提前定义前端自己的 Type 和 Resolver
- 后端不再耽误自己的时间为前端提供 Mock,也不再受页面 API 的约束,可以下沉精力专心做下层业务领域能努力的建设,下沉的领域能力多大,那么端上可使用和组合的能力就有多大
小菜前端已经从工具基建中受益,因为工具带来了协同和效率的优化只是结果之一,最重要的收获还有两个:
- 解放了小菜前端团队以及技术团队的人肉时间,可以有精力做其他事情
- 通过基建工具研发培养了小菜前端分析问题和解决问题的能力,同时沉淀了一些不错的技术方案
四、技术成长和沉淀
技术成长就是工程师的能力变化,我在 4 月份给大家做了一个 10 个月前后的能力评估,这 10 个月,是小菜前端 3 年来基建密度和团队内调整最大的几个月,也是团队整体战斗力提升最大的几个月,本文的所有分析、策略和实际的解决办法,也都是在这几个月里面进行实施的,挑了几个同学,挑了几个主要的能力维度,我们感受下他们的技术成长,白色的 * 代表 10 个月之前的能力值,2 颗星代表可以熟练的开发,三颗星代表基本精通或擅长,四颗星是比较精通。
文章图片
可以看到每个人都有不同程度不同层面的成长,有的全面开花,有的某些领域内快速积累,也要同学技术成长不多,但是协作能力工程能力有很大提升,其实还少了一个维度,就是参与业务拿到的结果或者说业务能力,图上放不下了,稍后会做分析和补充,我们再来看下这些同学做的事情:
文章图片
如果仔细比照一下,我们很容易得出三个结论:
- 整个技术团队综合技术能力有大幅度的提升
- 承担职责越多的同学能力成长越多越快(如组员 A)
- 承担繁重基建和工具开发的同学比承担业务开发的同学技术成长更多(如组员 E)
- 承担业务越多的同学,项目管理能力/沟通能力/业务理解能力也越好
- 通用的工具技术解决方案
- 通用的技术模块和业务组件库
- 团队整体的问题解决套路(分析解决问题的思维方式)
通用的技术模块和业务组件库,则是我们的组件三步走策略,首先是某个业务产品线下面的组件模板,比如 筛选组件或者列表组件,能在这个业务场景下的产品形态中通用,如果它可以跨产品线,那么就会跃升为 App 内通用组件,如果它还能继续抽象具有可重用性,那么就可以跃升为跨 App 的通用业务组件,比如热更新组件,地理位置定位组件,登录组件,异常提示弹窗组件等等。
团队整体的问题解决套路,这个是我们最大的收获,再直白一点,就是如何更快更好更有创造性的做事,这种思维方式,解决问题的套路,本质上是可以在团队内不断传承的,无论我们后面遇到什么样的业务和团队问题,我们用这一套场景-技术-长短期投入产出比评估的路子,都能用较轻的方式把问题解决掉,这个对于我们培养新人有很大的帮助。
五、对未来的展望
小菜三年走过来,前端团队从早期的技术和人员不稳定,到现在趋于稳定,站在公司的角度,最大的收获就是培养和磨炼了一批有创业热情,有担当勇气,有技术底蕴的一群人,这一群人抱团在一起,可以在所谓大前端这个框框内玩出更多的花样,支持到更多的业务场景。
站在今天看明天,虽然有很多东西对我们来说依然是未知的,但我们不再像过去一样临场发怯,手忙脚乱,取而代之的是无论多大多难的业务类型,我们都可以坐下来利用这帮人的智慧汇聚出一个最优选择,胸有成竹的去做技术探索和工程尝试,在跟公司一起成长变大的过程中,小菜前端也一定会沉淀出来更有实践价值,更有效率的技术方案,而这些就是我们将来可推广复用的宝贵技术资产,当然除了宝贵的技术资产,最最最重要的还有我们这群人,可以开心有趣有挑战性的 Coding,想进一步了解我们团队的可以 移步这里。 关于如何搭建高效率的生鲜B2B平台,因为包含的内容较多,也很复杂,无法再一篇文章中给大家讲清楚,本篇文章只是抛砖引玉,下面将分为多篇文章从行业现状、业务现状、产品概述、技术团队搭建、服务端技术平台搭建、前端开发等多个维度来讲述,我们将三年多在B2B领域沉淀的核心产品和技术平台公开,希望更多行业的人能深入了解,少走一些弯路,希望对大家有帮助,本系列文章分布如下(会继续更新):
1、《如何搭建高效率的生鲜 B2B 平台(B2B 技术共享第一篇)》
2、《宋小菜如何切入生鲜 B2B 市场(B2B 技术共享第二篇)》
3、《生鲜 B2B 平台的产品体系如何迭代(B2B 技术共享第三篇)》
4、《生鲜 B2B 如何搭建高效的技术团队(B2B 技术共享第四篇)》
5、《如何从 0 到 1 搭建生鲜 B2B 的技术体系(B2B 技术共享第五篇)》
6、《宋小菜技术如何应对生鲜 B2B 业务的快速变化(B2B 技术共享第六篇)》
7、《生鲜 B2B 技术平台的前端团队该如何搭建(B2B 技术共享第七篇)》
8、《宋小菜有关“能力”的设计和思考(B2B 技术共享第八篇)》
9、《服务拆分的设计和思考(B2B 技术共享第九篇)》
原文发布时间为:2018年06月09日
原文作者:宋小菜技术
本文来源:掘金 如需转载请联系原作者
推荐阅读
- 原力计划|云原生之 Docker Swarm服务编排介绍及使用入门
- 如何在Symfony 4.3中实现自己的用户身份验证系统(第2部分(创建用户注册表单))
- 如何在Symfony 4.3中实现自己的用户身份验证系统(第3部分(创建登录表单和注销路径))
- 智能助手(6个最旅行者需要的应用程序)
- 2020年最佳IOS应用开发工具的终极指南
- 如果你是初创公司,为什么要进行WordPress开发()
- 让我们在Plesk中加密(来自https://acme-v02.api.letsencrypt.org的无效响应)
- 如何在带有Darcula主题的黑暗模式下使用NetBeans
- 如何在Windows 10中使用CSV拆分器将庞大的CSV数据集拆分为较小的文件