关于工程化:前端工程化之包管理器

包管理器(1)pnpm(基于hard link + symlink) 基于硬链接机制(hard link)/内容寻址晋升包的装置速度 依赖包被存储在一个全局的store目录下,不同的我的项目依赖同一个包(版本雷同)时,会硬链接至此地位,无需重新安装, 即便一个包的不同版本,pnpm 也会极大水平地复用之前版本的代码应用软连贯(符号链接,symlink, 相似于windows快捷方式)的非扁平的node_modules构造 仅将我的项目的间接依赖项增加到 node_modules 的根目录下, 通过符号连贯查找虚构磁盘目录.pnpm下的依赖包node_modules/express/... // 符号连贯node_modules/.pnpm/express@4.17.1/node_modules/xxx // virtual store(2)npm package-lock.json锁定版本(3)yarn 并行装置缓存yarn.lock锁定版本性能比照:pnpm > yarn > npm 命令区别: install all: npm install / yarn / pnpm installinstall: npm install xx (-D) / yarn add xx (-D) / pnpm add xx (-D)uninstall: npm uninstall xx (-D) / yarn remove xx (-D) / pnpm remove add xx (-D)update: npm update xx / yarn upgrade xx / pnpm update xx相干文件:.lock文件该文件记录了package.json依赖的模块,以及依赖的依赖,每次装置都是雷同的后果,解决我的项目问题,此文件用来对整个依赖树(我的项目中援用的模块以及模块依赖的模块)进行版本固定cnpm install无奈辨认该文件 ...

May 30, 2022 · 1 min · jiezi

关于工程化:那一年我们在巴塞罗那找到的ONES-图腾

邻近2021年岁末,「圣诞之星」被悬挂到圣家族大教堂第二高塔「圣母塔」之上,这意味着大教堂进入了最初的施工阶段。 圣家族大教堂(简称「圣家堂」)被称为世界上最驰名的「烂尾楼」——从1882年开始建筑,至今仍然没有建成,是巴塞罗那的驰名地标,也是寰球惟一一座尚未完工就被列入世界文化遗产的修建。 2018年9月,研发治理计划公司 ONES 创始人兼 CEO 王颖奇率队返回巴塞罗那加入行业展会期间,受到了圣家堂及巴塞罗那当地多项建筑设计的灵感启发,组织团队从新设计出了 ONES 沿用至今的 LOGO,实现了一次品牌视觉上的降级。 忆往昔峥嵘岁月稠。以下是王颖奇在巴塞罗那寻找 「ONES 图腾」之旅的自述。 辨识度之惑踏入2018年,ONES 的业务有很大的停顿。客户逐渐开始认可咱们提供的研发管理工具产品性能,但与此同时,客户也会对咱们的品牌 LOGO 的辨识度示意「感到蛊惑」。 过后,ONES 的 LOGO 是简略的圆圈里有个尖角,黑红色,看起来跟其余一些不相干品牌 LOGO 太类似甚至雷同,以至于有客户说,ONES 的品牌基本没方法让人记住。 这时候,我意识到,在守业初期,咱们对 LOGO 设计的关注不够,没有从视觉上对品牌进行残缺自洽的梳理。 当然,这跟公司的倒退阶段无关。ONES 开办于2015年,但直到2017年,咱们还没有一个客户。而2018年是 ONES 的转折点,咱们有了系列的种子客户,在取得客户对于品牌方面的反馈后,我才发现,咱们对品牌流传的原始筹备并不足够,最外围的就是品牌视觉物料是不达标的。 对于公司本身而言,LOGO 是一种图腾,而图腾是一种能激发群体身份认同,跟别人造成区隔,从而取得安全感和鼓励的符号零碎。从商业上看,一个辨识度高的 LOGO,能够升高认知老本、决策老本和流传老本,因此有微小的商业价值。 于是,咱们开始了寻找更换 LOGO 方向的摸索,参考了国内互联网巨头的最新 LOGO 设计特色,推敲了几个月,同时做了很多版本的底稿计划,但感觉那些都不是我真正想要的,所以始终还没定下来换哪个 LOGO 。 无意栽花花不发,转折却产生在我的巴塞罗那旅程中的「无心插柳」。 先要换思维 2018年9月初,ONES 的 A+轮融资工作靠近序幕,恰逢有一个国内研发治理行业展会在巴塞罗那举办,于是我约上 ONES 的产品负责人和一名投资人,我们仨一起从深圳返回巴塞罗那,学习和理解行业前沿最新动向。 参加展会之余,偷得浮生半日闲。咱们一起品味当地的葡萄酒,一起吃当地各种的火腿——从最便宜的吃到最贵的。 微醺薄醉之际,我一览巴塞罗那多家艺术博物馆,感觉本人置身于亦幻亦真的完满梦幻之中。 下一站,咱们奔赴如雷贯耳的圣家堂。刚一下车,我往天上一望,几个大尖顶高耸入云;再往尖顶上看,那仍然是个大工地,吊车长长的臂膀在宏伟而神圣的教堂顶上有点不协调。但这已让我感到震撼。 通过了靠近140年的建造,这个大教堂仍然没有竣工,巴塞罗那的市民们预计,到2026-2028年它有心愿建完。不过,尽管这只是个未竣工的工程,门口却排着长长的队伍,大家在等待参观。 百闻不如一见。身临其境,我先是惊叹于整座教堂一门一柱上精密的雕刻与简约的细节,所有的人物、动物、鸟类和野兽栩栩如生,而岩石的质感又让它们体现得像沙画般柔和。 圣家堂外部的立柱犹如树干,向上延长出若干树枝,撑持起教堂微小的拱顶——这让我感觉这里的修建不是造出来的,而是像生物天然成长而成的,充斥了「向上成长的力量」。 我发现,整个教堂内外没有直角,也简直没有直线。因为圣家堂的代表设计师高迪认为,修建应该更像大自然,而大自然是没有直线的。 当我看到圣家堂外部的旋转楼梯,彼时彼刻,我开始想,直线的确是理工科思维,或者称为「简略性能思维」。 换 LOGO ,先要换思维。 工程的代表 带着好奇心,我持续参观高迪设计的其余修建作品。 咱们来到了巴特罗之家,这是高迪另一个神来之笔。整座建筑设计秉承高迪一贯的格调,没有棱角,全是柔和的波浪形态。 ...

February 25, 2022 · 1 min · jiezi

关于工程化:全栈工程化实战之一容器化和基础环境

一、容器技术(Container)在虚拟机(VMware)呈现以前,利用的部署因为依赖问题,往往新利用要部署在独立的服务器上,导致硬件资源利用率低下,VMware的呈现完满的解决了这个问题。 然而,VMware也存在有余,首先VMware实际上是虚构独立的OS,这须要额定的CPU、RAM和存储资源;其次,因为虚拟机是独立的OS,因而启动比较慢;最初,对于独立的OS,须要独自的补丁&监控,对于商业利用还须要零碎的受权许可。 基于以上起因,以google为代表的大型互联网公司始终在摸索以容器技术代替虚拟机模型的毛病。 在虚拟机模型中,Hypervisor是硬件虚拟化(Hardware Virtualization)—将硬件资源划分为虚构资源打包进VM的软件结构中,而容器模型则是操作系统虚拟化(OS Virtualization)。 容器共享宿主机OS,实现过程级别的隔离,绝对于虚拟机,容器启动快、资源耗费低、便于迁徙。 1.1 Linux容器古代容器技术起源于Linux,依赖的核心技术包含内核命名空间(Kernel Namespace)、控制组(Control Group)、联结文件系统(Union File System)。 Kernel Namespace 负责隔离 容器技术的复杂性使其难于推广,晓得Docker的呈现,Docker使得容器技术的应用变得简略。 目前还没有Mac容器,windows平台win10和win server2016曾经反对windows容器。 留神,因为容器是共享宿主机操作系统的,因而windows容器是不能运行在Linux零碎的,但Docker for Mac和Docker for Windows 通过启动一个轻量的Linux VM来反对Linux容器 在windows中,零碎必须是64位windows 10且开启Hyper-V和容器个性。 1.2 Docker装置略,Mac装置参考这里,windows装置参考这里。 1.3 基本概念1.3.1 docker引擎架构装置胜利后,执行docker version输入相似上面的内容: Docker daemon 镜像治理、镜像构建、API、身份验证、外围网络等。晚期的Docker引擎只蕴含LXC和daemon,daemon则耦合了客户端、API、运行时、镜像构建等性能,拆解后的daemon的外围性能聚焦在镜像构建治理和API。 containerd 封装容器的执行逻辑,负责容器的生命周期治理。shim 用于将运行中的容器和daemon解耦(daemon可独立降级)放弃所有的STDIN和STDOUT流开启状态将容器退出状态同步给daemonrunc(容器运行时) OCI容器运行时的标准参考实现(因而也称OCI层)轻量级的Lincontainer包装命令行交互工具惟一作用是创立容器1.3.2 镜像镜像是由多个层组成的,蕴含一个精简的OS和利用和依赖包的独立对象。镜像存储于镜像仓库官网镜像仓库服务。 能够通过命令docker image pull 镜像名拉取镜像,一个残缺的镜像名如下: <ImageRegistryDomain/><UserId/>:repository:<tag> 相干操作 拉取镜像docker image pull repository:tag查看本地镜像docker image ls查看镜像详细信息docker image inspect repository:tag删除镜像docker image rm repository:tag(留神,存在运行容器的镜像不能删除)构建镜像的形式 通过运行中的容器构建docker container commit通过Dockerfile文件1.3.3 容器容器是镜像的运行时实例。 运行容器 $ docker container run [OPIONS] <IMAGE> <APP>留神,对于linux容器,容器中默认只运行app对应的过程,如果app过程退出,容器也就退出了,即kill掉容器中的主过程,也就kill掉容器。 ...

February 8, 2022 · 2 min · jiezi

关于工程化:lerna-简单入门

Multirepo模式单体仓库,即每一个package都独自用一个仓库来进行治理。如果不同package之间相互依赖,会越来越难以保护。 Monorepo所有相干的package都放在一个仓库里进行治理。 lerna是什么?A tool for managing JavaScript projects with multiple packages. 一个用于治理,具备多个package,我的项目的工具。一个由lerna治理的我的项目,通常的构造如下: - lerna.json- package.json- packages - packageA - package.json - packageB - package.jsonlerna Fixed/Locked 模式 (默认模式)默认的模式,lerna init 创立默认模式的我的项目。固定模式应用 lerna.json 对所有的 package 进行对立的版本治理。多我的项目中任何一个 package 批改都会导致所有 package 的版本号变动。 lerna Independent 模式独立模式,lerna init --independent 创立独立模式的我的项目。独立模式容许每一个 package 独自批改版本号。在 lerna publish 时, 只会更新有变动的 package 的版本号。 lerna.json{ "version": "1.1.3", // 版本号,Independent模式下设置为independent "npmClient": "npm", // 指定运行命令的客户端 "command": { "publish": { "ignoreChanges": ["ignored-file", "*.md"], // 指定那些目录或者文件的变更不会被publish "message": "chore(release): publish", // 执行公布版本更新时的自定义提交音讯 "registry": "https://npm.pkg.github.com" // 设置npm包公布的注册地址 }, }, "packages": ["packages/*"] // 指定包所在的目录}应用lerna装置lernanpm install --global lerna初始化lerna (应用默认模式)lerna init我的项目目录构造如下: ...

August 23, 2021 · 2 min · jiezi

关于工程化:关于Code-Review

以下是转载的一篇文章,有几个点记忆比拟粗浅1: 少继承,多组合。理论我的项目中,各种职业级别不同的同学一起合作批改一个 server 的代码,就会呈现,职级低的同学改哪里都改不对,基本没能力进行批改,高级别的同学能批改对,也不违心大规模批改,整个我的项目变得愈发不合理。对整个继承树没有齐全意识的同学都没有资格进行任何一个对继承树有调整的批改,合作变得举步维艰。代码的批改,都变成了依赖一个高级架构师高强度监控继承体系的变动,低级别同学们束手束脚的后果。组合,就很好的解决了这个问题,把问题一直细分,每个同学都能够很好地攻克本人须要攻克的点,实现一个 package。产品逻辑代码,只须要去组合各个 package,就能达到成果。 2: 重视工程设计,思考本人面对的业务畛域,思考可能的模型边界,模型细节3: 大道至简。首先,简略不是面对一个问题,咱们印入眼帘第一映像的解法为简略。我说一句,感受一下。"把一个事件做进去容易,把事件用最简略无效的办法做进去,是一个很难的事件。"比方,做一个三方受权,oauth2.0 很简略,所有概念和细节都是紧凑、齐备、易用的。你感觉要设计到 oauth2.0 这个成果很容易么?要做到简略,就要对本人解决的问题有全面的理解,而后须要一直积攒思考,能力做到从各个角度和层级去意识这个问题,打磨出一个艰深、紧凑、齐备的设计,就像 ios 的交互设计。简略不是容易做到的,须要大家在一直的工夫和 code review 过程中去积攒思考,pk 中触发思考,交换中总结思考,能力做得愈发地好,靠近'大道至简'。4: 可显性。可显性的规范很简略,大家看一段代码,懂不懂,一下就明确了。然而,如何做好可显性?那就是要谋求正当的函数分组,正当的函数上下级档次,同一档次的代码才会呈现在同一个函数里,谋求通俗易懂的函数分组分层形式,是通往可显性的路线。5: 出现异常时,马上退出并给出足够错误信息。6: 对于日志。沉默是金。除了简略的 stat log,如果你的程序'发声'了,那么它抛出的信息就肯定要无效!打印一个 log('process fail')也是毫无价值,到底什么 fail 了?是哪个用户带着什么参数在哪个环节怎么 fail 了?如果发声,就要把必要信息给全。不然就是不发声,示意本人好好地 work 着呢。不发声就是最好的音讯,当初我的 work 一切正常! 对于代码格局标准,100%严格执行,重大容不得一点沙。 文件绝不能超过 800 行,超过,肯定要思考怎么拆文件。工程思维,就在于拆文件的时候积攒。 函数对决不能超过 80 行,超过,肯定要思考怎么拆函数,思考函数分组,档次。工程思维,就在于拆文件的时候积攒。 代码嵌套档次不能超过 4 层,超过了就得改。多想想能不能 early return。工程思维,就在于拆文件的时候积攒。 举荐的书籍:《unix 编程艺术》

May 21, 2021 · 1 min · jiezi

关于工程化:前端工程化Yeoman-和-Plop

前端工程化遵循肯定规范和标准,通过工具去提高效率,降低成本的一种伎俩 工程化是一种布局架构,工具是实现布局架构得伎俩,是工程化的集成 Powered by Node.js 工程化通过Node驱动 为什么要应用工程化 应用ES6存在兼容问题应用Less、Sass、PostCSS加强css编程性存在运行环境不反对应用模块化形式进步我的项目可维护性,运行环境不间接反对部署上线前"手动"压缩代码资源,部署过程"手动"上传代码到服务器多人合作开发,无奈硬性对立大家代码格调,从仓库pull回得代码品质无奈保障、一些成熟的工程化集成 脚手架工具(前端工程化的发起者)实质作用:创立我的项目根底构造,提供我的项目标准和约定 约定体现在 雷同的组织构造雷同的开发范式雷同的模块依赖雷同的工具配置雷同的根底代码有些IDE创立我的项目的过程能够看作是工程化,例如Android Studio 罕用脚手架 create-react-app , vue-cli , angular-cli 依据信息创立对应我的项目根底构造 实用于本身服务的框架Yeoman 通用型我的项目脚手架工具Plop 我的项目中创立特定类型文件Yeoman创立现代化web利用的脚手架工具 全局范畴装置yo yarn global add yo , npm i yo -g装置对应的generator 例如: yarn global add generator-node , npm i generator-node -g通过yo运行generator yo node* 填写脚手架内容Sub Generator在已有的我的项目根底上创立特定类型文件 如我的项目上增加README,ESlint,Bable配置文件,用生成器主动生成 例如:在generator-node创立的我的项目里增加node子集生成器cli,并不是都有子集生成器,依据官网查看 yo node:cli 创立cli利用所须要的文件my-module --helpYeoman应用步骤总结明确需要找到适合的Generator全局范畴装置找到的Generator通过yo 运行对应的Generator通过命令行交互填写选项生成你所须要的我的项目构造找到对应generator例如:创立一个网页利用webapp * yarn global add generator-webapp * yo webapp (相似于 yo node 装置generator-node)自定义Generator基于Yeoman搭建本人的脚手架,实质上Generator就是一个NPM模块 自定义的Generator满足构造 自定义的名称必须是 generator-name ...

May 18, 2021 · 1 min · jiezi

关于工程化:入门lerna

overviewlerna是一个“一库多包”的管理工具。一库多包:在一个仓库中包含了多个代码包。这样做能够解决若干相干包的关联/共享性能。还能够跟踪版本。 demooverview创立一个能够输入文本的包。 initmkdir lerna-demo1cd lerna-demo1运行后果如图 create增加一个lerna治理的包(calc)。 lerna create calclerna create pointer edit编辑calc/lib/calc.js为如下代码: 'use strict';module.exports = { add: (a, b) => a + b, subtract: (a, b) => a - b, multiply: (a, b) => a * b, divide: (a, b) => a / b,}编辑pointer/lib/calc.js为如下代码: 'use strict';let calc = require('calc')module.exports = { construct: (x, y) => { return {x, y} }, distance: (p0, p1) => { let x = calc.subtract(p0.x, p1.x) let y = calc.subtract(p0.y, p1.y) return Math.sqrt(calc.multiple(x, x), calc.multiple(y, y)) },}add在clac包中增加lodash依赖包。 ...

May 8, 2021 · 1 min · jiezi

关于工程化:前端工程化的实践过程

overview前端近来的有好多发现方向。如:微服务/工程化/自动化。我打算记录一下我在工程化方向做的摸索。分为“工程化各组成部分”/“实现过程”/“对将来的影响”。这篇文章只是一个结尾,后续还有好多内容。一时半会儿也写不完。明天先做了一个vue-cli的插件。 vue-cli-plugin-auto-frame它须要基于@vue/cli应用。以后反对装置罕用的vue插件/扩大脚本/创立我的项目构造。它的最终目标:应用一行命令创立一个领有相应构造的我的项目。这个插件先公布在了npm,就是为了占个坑。而后迭代欠缺。

May 1, 2021 · 1 min · jiezi

关于工程化:解密智联招聘的大前端架构Ada

Ada是智联招聘自主研发的演进式大前端架构。于2017年正式投入使用后,又通过三年继续演进,全面笼罩了从研发到运维的各个方面,具备跨技术栈工程化体系、交互式图形界面开发工具、自动化公布流程、Serverless运行时和欠缺的监控预警设施。目前曾经撑持团体内数百个工程,在线URL数量多达数千,每日承载申请量逾十亿次。 本文将摘取Ada的一些要害个性,向大家介绍Ada的演进成绩和设计思维。 可演进的工程化机制“可演进”是Ada最外围的设计思维。 Ada的最后版本实际上是它的内核,投入使用后便始终放弃每两至三周一个版本的演进速度,一直地坚固内核,欠缺周边设施,同时凋谢更多研发能力。咱们心愿所有工程都能享受到最新版本的个性,不违心看到工程版本随着时间推移变得碎片化。 思考到Webpack的灵活性和复杂性会不可避免地助长碎片化,咱们决定将其暗藏到Ada外部,由Ada来承当起对立工程化机制的责任。 Ada标准了工程的目录构造,将指定目录下的次级目录作为Webpack Entry解决,实现了对SPA和MPA的同时反对,更容易撑持巨量级的简单视图。 同时,Ada还对立解决了Webpack Loader及插件的应用形式、CDN地址、Code Split、SourceMap、代码压缩等构建细节,并且主动解决了不同部署环境之间的差别,标准化了工程的构建输入模式。 针对工程之间可能存在的正当的差异性配置,比方域名、根门路和语言处理器(Webpack Loader)等等,Ada还向业务团队提供了一个更加精简的工程配置文件。 通过工程标准和工程配置文件,咱们把Ada塑造成了一名“Webpack配置工程师”,它会解决好所有波及到Webpack的工作,业务团队无需关怀此类细节。咱们也因而对工程化机制有了更强的治理和演进能力,可能在不影响业务团队的状况下进行迭代(比方调整逻辑、修复问题、降级Webpack版本、甚至更换到其余打包工具等等)。 反对多框架为了更好地反对业务特有的技术诉求,以及应答不断涌现的新框架和新技术,Ada从一开始就将多框架反对能力当作了一个重要的设计指标。 依靠于对立的工程化机制,Ada能够依据各种框架的特点针对性地调整Webpack配置,造成新的脚手架。所有脚手架都延用了统一的工程标准和工程配置文件,最大水平上保障了统一的开发体验,缩小了框架的切换老本。 咱们抉择Vue.js作为公司的次要前端框架,并为其研发了专门的脚手架。Vue.js脚手架保留了Vue.js在研发效率方面的长处,容许开发者配置多种CSS处理器,并对服务器端渲染提供了良好的反对。 随后,Ada又提供了Weex脚手架来反对挪动端疾速开发,帮忙业务团队将一套代码同时运行在浏览器、iOS和Andriod中。 针对须要反对旧版IE浏览器的业务,咱们抉择了MVVM模式的鼻祖框架Knockout.js,并将Vue.js广受赞美的的单文件组件机制引入到Knockout.js脚手架中,为开发者带来了和Vue.js脚手架一样的开发体验。 此外,Ada还提供了用于开发Web API的Node.js脚手架,并逐渐为它减少了TypeScript反对和GraphQL研发能力。 “可演进”的Ada工程化机制为新框架预留了短缺的扩大空间,也让咱们更容易跟进框架的版本更迭,继续为业务团队凋谢框架的残缺能力。 服务器端研发能力Ada基于Koa研发了Web服务器,并凋谢了服务器端研发能力,赋予前端工程师更全面的掌控力。岂但能够在UI层面执行权限校验、重定向和服务器端渲染(SSR)等操作,还可能通过研发Web API来实现BFF层(Backend for Frontend)。残缺的服务器端研发能力能将前后端的接触面(或摩擦面)从简单的视图层面转移到绝对简略可控的BFF层面,实现真正意义上的前后端拆散,继而通过并行开发来最大水平进步开发效率。 为了进一步升高服务器端研发难度,Ada在脚手架目录构造标准的根底上,进一步标准了路由函数的申明形式,造成了从HTTP申请到函数的映射关系。申请函数是一个异步函数,Ada会向它传递一个上下文对象。这是一个通过了悉心封装的对象,它蕴含了以后Request的所有信息,提供了全面管制Response的能力,并且对立了Web API和SSR的API。 借助申请函数映射机制和自定义上下文对象,Ada向开发者提供了一种更加简略间接的、面向申请的开发方式,同时暗藏了Koa和Web服务器的技术细节。这种设计使得业务团队能够更加专一于产品迭代,架构团队也能在业务团队无感知的状况下进行日常保护和继续演进(比方调整逻辑、裁减能力、降级Node.js版本、甚至更换到其余Web服务器框架等等)。 Serverless架构在升高服务器端开发门槛的同时,咱们也心愿可能升高服务器的运维和治理难度,让前端工程师不用分心于诸如操作系统、根底服务、网络、性能、容量、可用性、稳定性、安全性等运维细节,从而将更多的精力投入到业务和专业技能上。基于这样的思考,咱们引入了Serverless架构。 咱们借助容器技术搭建了服务集群,将Ada演进成为一个更加通用的运行时,除了函数发现以及通过执行函数来响应URL申请之外,还对运行时本身提供了全方位的保障。Ada服务器有残缺的申请生命周期追踪机制和日志API,可能自动识别和阻断歹意申请,还能从常见的Node.js故障中主动复原。此外,服务集群也具备欠缺的平安进攻和性能监控设施,并实现了容量弹性伸缩,在节约老本的同时也能更好地应答流量稳定。 如此一来,服务便从工程中脱离进去,成为Serverless服务集群的一员,继而通过公布流程来将服务和工程连接起来。公布流程也运行在云端,分为部署和上线两个阶段。部署阶段仅仅执行文件构建、上传和注册,不会对线上版本产生任何影响。部署实现后,就能够在公布核心上线具体的URL版本,并且能够随时回滚至历史版本。无论公布还是回滚,都会即时失效。 URL粒度的公布形式更加符合前端业务的迭代习惯,更加灵便,与单体利用的整体公布形式相比也更加平安可控。工程作为一种代码组织模式,不再承当服务的责任,能够随时依据须要进行合并和拆分,也能更好地适应虚构团队这样的组织状态。 工作台和许多框架一样,Ada晚期也提供了一个命令行工具来辅助开发。命令行工具的局限性非常明显,出现模式和交互模式都过于繁多。随着Ada的逐渐采纳,日常开发过程中产生的信息和所波及的操作都愈发繁冗。咱们须要一个更具表现力的工具来进一步提高工作效率,便基于Electron研发了Ada工作台。 Ada工作台并不是命令行性能的简略复刻,而是对前端图形界面开发工具的大胆设想和从新定义。咱们为Ada工作台增加了丰盛的性能,全面笼罩了前端工作流程中的开发、调试、公布等环节,使它成为真正的一站式前端开发工具。 咱们在Ada工作台中引入了URL级别的按需构建。开发者抉择URL之后,Ada工作台就会主动启动多个构建器来执行构建,同时以图例的模式展示构建状况。构建中呈现的任何问题,比方未找到援用或者未通过开发标准查看,都能够直观地看到提醒,点击提醒则能浏览更具体的信息。按需构建既晋升了构建速度,也在肯定水平上无效地防止了Webpack在构建大型工程时可能呈现地各种问题。 除了手工启动构建之外,Ada工作台提供了一种更加便当的形式——“拜访即构建”,通过监听对URL的拜访,主动启动按需构建,并在构建实现后被动刷新页面。“拜访即构建”通过天然的本机调试行为来触发构建,免去了手工一一抉择URL的繁琐操作,很快就成为了开发者的首选构建形式。 尽管服务器端代码最终运行于Serverless环境,但并不意味着开发阶段只能近程调试,为了便于调试,Ada工作台内置了Ada服务器的一个开发版本,该版本仅对本机开发流程进行了适配和性能缩减,其余个性和Serverless版本放弃高度一致,诸如端口抵触、环境差别等等困扰开发者的效率阻碍在很大水平上都被打消了。 Ada工作台还提供了一个交互式的日志查看器,来帮忙开发者浏览本机开发时输入的日志。所有日志都会以十分简洁的模式出现,能够通过点击来浏览明细,同时也提供了关键字搜寻和日志级别过滤等性能,以便开发者能疾速找到所关怀的调试信息。 公布流程也被无缝嵌入到Ada工作台中,并且失去了进一步加强,可能不便地执行URL级别的按需公布。 目前,Ada工作台曾经成为公司前端技术体系的重要基础设施。前端技术畛域还在不断涌现出各种新的概念,而Ada工作台的设想空间仍旧很大,这也让咱们对它将来能施展的作用更加期待。 挪动端研发能力咱们抉择了Weex作为挪动端的疾速研发框架,帮忙业务团队应用相熟的Vue.js语法开发能够同时运行于浏览器、iOS和Andriod中的利用。 Weex脚手架遵循了Ada的工程化机制,能够享受Ada工作台提供的开发和调试便当。此外,Ada工作台还以插件的模式内置了Weex真机调试工具,以便在App内进行调试。 在开发模式上,咱们最大水平保留了Web的特色,为前端工程师带来更加相熟的开发体验,Web格调的URL路由形式也在Native内核中失去了反对。Native内核向Weex提供了全方位的反对,包含路由、缓存、视图组件、互操作API等等。针对历史遗留的Native平台差别问题,则通过咱们研发的mobile-js-bridge来将它们封装成统一的API。 此外,咱们为Weex也提供了URL粒度的公布能力,可能独立于App的版本进行公布,极大地提高了挪动端的迭代速度和问题响应速度。 Ada充分发挥了Weex在疾速迭代方面的劣势,宽泛地利用于公司的各个挪动端产品中,先后帮忙业务团队许可了多场疾速交付战斗。 能力裁减Ada除了反对开发Web页面,还反对开发一种非凡的视图——Widget。作为微前端架构的一种实现,Widget运行在宿主页面中,能够独立开发和公布。其设计指标是解耦代码、流程和团队,帮忙业务团队进行跨技术栈、跨产品以及跨团队的性能复用。比方公司所有产品线都须要应用对立的登陆注册Widget,后者由平台团队来保护,在保障兼容性的前提下就能够自行迭代演进,而不须要各产品线逐版本配合公布。Widget SDK负责保护Widget的生命周期,并提供了相似于Web Worker的通信机制,从而实现Widget和宿主页面在技术框架、代码逻辑和公布流程上的齐全独立。 Widget是一种在客户端复用能力的机制,在服务器端,Ada提供了申请上下文扩大来实现能力复用。申请上下文扩大是一组能够独立开发和公布的函数,公布之后的函数会附加到申请上下文,供特定范畴的申请函数调用。借助申请上下文扩大,业务团队能够更不便地复用诸如用户认证和受权之类的服务器端专用能力。 此外,Ada服务器还内置了一些罕用的第三方模块的多个版本,比方vue-server-renderer、axios和pg等等。开发者能够通过专门的公共模块API来援用这些公共模块的制订版本。由Ada服务器对立提供的公共模块一方面晋升了工程的构建速度,减小了输入体积,另一方面也躲避了Webpack无奈解决Node.js Native的问题。 在对GraphQL进行了大量调研和实际之后,咱们决定通过工具包的模式提供GraphQL开发能力。GraphQL工具包同时反对graphql-js和Apollo GraphQL两种实现,并且能够将Schema转化为Ada申请函数,从而在Ada服务器中执行。GraphQL工具包会辨认Schema中的异步Resolver,并将它们注册到Ada Server的性能监控和申请跟踪机制中,为业务团队在合并了多个操作的申请中定位问题提供便当。 得益于Ada的“可演进性”,咱们可能更加持重地响应业务诉求,继续一直地将技术洞察转换成新的能力,以更加“Ada”的模式提供给业务团队,上述能力扩大就是其中的典型示例。 品质保障咱们采取了多种技术手段来保障Ada外围代码的品质和Serverless服务集群的稳定性。 Ada外围代码遵循了相当严格的开发标准,并通过数千个单元测试用例100%笼罩了全副代码和执行门路。针对单元测试可能呈现的“非无意笼罩”状况,咱们特地设计了“混沌模式”,通过随机删除特定的代码来测验测试用例的全面性。 为了确保Ada服务器的变更不会毁坏API的向下兼容性,咱们在集成测试阶段将Ada的测试版本部署到一组测试容器中,并申请事后公布的测试URL来一一进行查看API的性能是否失常。Serverless服务集群也装备了欠缺的日志剖析、性能监控、弹性伸缩、故障复原和预警机制。 ...

November 30, 2020 · 1 min · jiezi

前端物料中台建设

我在上一篇的文章大BU级别的"前后端分离"实践中提出了我们当前的前端团队中存在一些问题以及解决思路,并且在其中详细地写出了统一视图服务的实现思路和收益。这篇文章主要写关于前端迭代及上下游协作效率我们是如何解决的。 背景我们的目的是提升前端迭代及上下游的协作效率,我们从四个出发点开始讲起: 团队负责的业务越来越读,如何提高资源、组件及代码的复用率,提升整体研发效率;提升项目的上下游协作效率,涉及到从UE到UI到FE再到QA的整个流程,这需要很强的推动力和足够的说服力;提升人效比,一人顶三人,提升项目中前端开发人员的人效比,让前端做的更快、更多、更好;最后是将整体方法进行梳理,把提效的这部分能力应用在web、wap、小程序中,实施中台化。 解决思路 首先,建设前端物料体系,重新定义和统一前端的开发标准,建设前端物料资源体系,其中包括设计和单元测试等资源;第二,实行工具化管理,完善工程体系,统一前端的开发流程和技术栈,保证资源复用率;最后也是最重要的就是渐进式开发,因为它决定了物料完成之后能不能达到最佳的复用率。 整体架构 问:什么是物料?答:可按照规范进行标准化构建,并且可以在不同项目,不同团队,不同成员之间复用的任意资源。我们把这类资源统称为物料。参与物料资源生产和消费的对象共有四个:开发者、使用者、设计者、产品/运营。为此,物料中台提供了标准化的物料资源,其中包括项目的基础框架、组件库、区块和模板。基础框架是项目的样板框架也就是俗称的boilerplate,组件库是基于部门内设计同学提出的Union Design设计规范开发前端组件库,它同时支持web和小程序两个宿主环境。区块可以简单理解为组件的拼装集合,它比组件库提供了更上层的业务封装能力,但是这也让它变得没有组件库那么地纯粹;模板就是各种站点的样式模板,其中整合了前面所提到的基础框架+组件库+区块等各种资源。让开发者可以最大限度地减少工作量。 图中的中间部分是可被支持的宿主环境,包括Web端、移动端及小程序。Web端可以支持TypeScript+Vue/React框架模式进行开发,移动端支持VW/REM布局方式,同时也支持Hybrid模式进行开发,具体文章请参考端动态化方案详细设计。另外由于业务原因,小程序目前支持微信小程序和智能小程序两种。 最下面是物料中台提供的工程化体系,提供了CLI工具和GUI工具。CLI工具用于在前端项目开发中,让FE可以便捷地对物料进行生成和消费;此外物料中台还提供了GUI工具,让物料的消费方可以通过拖控件的方式,对项目进行快速地原型落地和模板设计,里面具体的细节请等待后续的文章更新。 我们和设计同学一起推出了物料相关的设计资源,其中就包括Union Design设计规范。为了降低物料在项目中的落地阻力,物料中台还提供了完善学习资源,包括文档、教程,也会定期组织培训和分享。 最后,在整体上实现了物料中台的功能。 组件库设计我们在进行组件库设计是主要考虑了两个方面: 组件分级 基础UI组件复合组件业务组件组件边界 越界操作副作用侵入性循环依赖首先对组件进行分级,根据对业务的侵入性和纯粹性(复用程度)为分界点,拆分为三级,如下图: 如图中表示,越往下业务侵入性越高,抽象程度越低。越往上复用程度和纯粹性越高,同时抽象程度也越高。 组件库设计的关键就是组件的边界限制,每个组件都是非常独立的且不可再拆分的最小单元。首先是禁止组件的越界操作,只让它处理份内的事情,对外部的事情不能染指半分;第二是副作用,禁止(尽可能的少)有副作用,如果了解过函数式编程,就会知道副作用的各种优缺点;第三是侵入性,禁止纯粹的木偶组件(组件库中的每个组件都是纯粹的木偶组件)对业务的侵入性,因为侵入性越高复用率越低,组件库对于其他人也就越难用;最后是禁止组件内部的循环依赖。 最终实现 最终实现中,整体分为四层,最下面是技术实现,往上是依赖的设计资源,再往上就是组件库提供的五种基础组件,包括基本操作、表单、视图、导航和数据处理等。最上层则是接入的业务方。自下而上地完成了整个前端业务方向的组件库方案。 上下游协作对上下游协作效率优化的流程中最核心的就是效率提升和物料复用。 效率提升 交互设计单元测试回归成本物料复用 物料共享生成和消费业务迁移渐进式开发整体流程如下图: 在整体协作流程中主要包含两条操作流程,物料管理和物料使用。 物料管理的功能是给物料开发者使用的,负责物料对不同平台的支持,创建,生成和列表管理,可以在管理列表中对自己上传物料进行消费。 对于物料的消费,提供物料实时预览、下载、使用、和源代码查看功能。使用物料时会自动生成CLI命令,在终端中执行命令就可以在项目中使用此物料。 另外,也可以将项目中的某些功能抽离为物料,供其他项目使用。以此实现可持续迭代和渐进式开发。这样就完成物料资源在不同团队,不同项目,不同成员之间的共享和复用,提升前端的整体协作效率。 整体收益 内部接入6个项目;在公司内部NPM中开源了2个组件库;在BU内部共推出了4款产品,分别是Union Design设计规范、@baidu/union-design、@baidu/union-design-wechat和marble-cli/GUI工具; 在代码复用率方面,项目基础框架为46.1%,业务逻辑为21.87%;全新开发一个项目迭代周期缩短23.8%,迭代的项目缩短21.4%; 最后有任何疑问请联系作者

August 20, 2019 · 1 min · jiezi

软件工程

软件的定义软件是计算机系统中与硬件相互依存的一部分,软件包括程序、数据与相关文档的完整集合。其中程序是按事先设计的功能和性能要求执行的指令序列,数据是使程序能正常操纵信息的数据结构,文档是与程序开发、维护和使用有关的图文材料。 在结构化程序设计时代,程序的最小单位是函数及子程序,程序与数据是分离的;在面向对象程序设计时代,程序的最小单位是类,在类中封装了相关的数据及指令代码。 软件生命周期如同其他事物一样,软件也有一个孕育、诞生、成熟和衰亡的过程,我们称之为软件生命周期。软件生命周期由软件定义、软件开发、和运行维护3个时期组成,每个时期又可划分为若干个阶段。 软件定义时期的主要任务是解决“做什么”的问题,即明确工程的总目标和可行性;导出实现工程目标应使用的策略及系统必须完成的功能;估计完成工程需要的资源和成本;制定工程进度表。该时期的工作也就是常说的系统分析,它通常又分为3个阶段:问题定义、可行性研究和需求分析。 软件开发时期的主要任务是解决“如何做”的问题,即具体设计和实现在前一个时期定义的软件,通常由概要设计、详细设计、编码和测试4个阶段完成。 软件运行维护时期的主要任务是使软件持久地满足用户的需要,通常由4类维护活动:改正性维护,也就是诊断和改正正在使用过程中发现的软件错误;适应性维护,即修改软件以适应环境的变化;完善性维护,即根据用户的要求改进或扩充软件,使它更完善;预防性维护,即修改软件为将来的维护活动预先做准备。 软件危机软件开发周期长、成本高、质量差、维护困难原因: 1、缺乏软件开发的经验和有关软件开发数据的积累,使得开发工作的计划很难制定。主观盲目的制定计划,往往与实际情况相差太远,致使常常突破经费预算,工期一拖再拖。而且对于软件开发工作,给已经拖延了的项目临时增加人力只会使项目更加拖延。 2、软件人员与用户的交流存在障碍,除了知识背景的差异,缺少合适的交流方法及需求描述工具也是一个重要原因,这使得获取的需求不充分或存在错误,在开发的初期难以发现,存在的问题往往在开发的后期才暴露出来,使得开发周期延长,成本增高。 3、软件开发过程不规范,缺少方法论和规范的指导,开发人员各自为战,缺少整体的规划和配合,不重视文字资料工作,软件难以维护。 4、随着软件规模的增大,其复杂性往往会呈指数型增长。 5、缺少有效的软件测评手段,提交用户的软件质量差,在运行中暴露出大量问题,轻者影响系统的正常使用,重者发生事故,甚至造成生命财产的重大损失 软件工程1968年10月在北大西洋公约组织(NATO)召开的计算机科学会议上,Fritz Bauer首次提出“软件工程”的概念,试图将工程化方法应用于软件开发。许多计算机和软件科学家尝试,把其他工程领域中行之有效的工程学知识运用到软件开发工作中来,经过不断实践和总结,得出这样的结论:按工程化的原则和方法组织软件开发工作是有效的,是摆脱软件危机的一个主要出路。 软件工程是指导计算机软件开发和维护的工程学科。采用工程的概念、原理、技术和方法来开发和维护软件,把经过时间考验而证明正确的管理技术和当前能够得到的最好的技术方法结合起来,以经济地开发出高质量的软件并有效的维护它,这就是软件工程。 软件工程的目标是运用先进的软件开发技术和管理方法来提高软件的质量和生产率,也就是要以较短的周期、较低的成本生产出高质量的软件产品,并最终实现软件的工业化生产 自从“软件工程”诞生,提出用工程学的基本原理和方法来组织和实施软件生产后,又发展了与软件有关的心理学、生理学、经济学等方面的学科。在此期间,研究软件工程学的科学家们陆续提出了100多条有关软件工程的准则,1983年美国TRW公司B.W.Boehm将它们概括为著名的软件工程7条基本原理: 1、按软件生存周期分阶段制定计划并认真实施软件从定义、开发、运行和维护,直到最终被废弃,要经历一个很长的时间,通常称这样一个时期为软件生存周期。在软件生存周期中需要完成许多不同性质的工作,所以应把软件生存周期划分为若干阶段,为每一阶段规定若干任务,制定出可行的计划,并按照计划对软件的开发和维护啊活动进行管理。 2、坚持进行阶段评审软件的质量控制工作不能等到编码结束后再进行,因为大部分错误是在编码之前造成的,而且错误发现的越晚,为改正它所需付出的代价就越大,因此,在每个阶段都要进行严格的评审,以尽早发现在软件开发过程中产生的错误 3、坚持严格的产品控制在软件开发过程中要及时识别所有影响产品质量的因素,并在开发过程中严格加以控制,如对于需求变更的控制。由于外界环境变化或软件工作范围的变化,在软件开发过程中需求的变更是在所难免的,所以必须依靠科学的产品变更控制机制来顺应需求的变更。就是说,当需求变更时,为了保持软件各个配置成分的一致性,必须实施严格的产品变更审查和基线配置管理,确保变更的正确性和有效性。 4、使用现代程序设计技术随着软件产品规模和复杂性的不断增加,对于软件开发的方法和工具提出了更高的要求。实践表明,采用先进的程序设计技术可提高软件开发的生产率,还可提高软件产品的可维护性。 5、明确责任为了提高软件开发过程的可见性并有效地进行管理,应当根据软件开发项目的总目标、任务分解结构和完成期限,规定开发组织的责任和产品标准,使得工作结果能够得到清楚的审查。 6、用人少而精合理安排软件开发小组人员的原则是参与人员应当少而精,即小组的成员应当具有较高的素质,且人数不应过多。人员素质高能大大提高软件开发的效率,明显减少软件中的错误。开发人数减少,可以降低因交流开发进展情况和讨论遇到的问题而造成的通信开销。 7、不断改进开发过程软件开发过程定义了软件工程方法使用的顺序、要求交付的文档资料、为保证质量和协调变化所需要的管理以及软件开发各个阶段应采取的活动和必要的评审。为保证软件开发的过程能够跟上技术的进步,必须不断地灵活改进软件工程过程。为了达到这个要求,应当积极主动地采取新的软件开发技术,注意不断总结经验。此外,需要注意收集和积累出错类型、问题报告等数据,用以评估软件技术的效果和软件人员的能力,确定必须着重开发的软件工具和应当优先研究的技术。 软件工程方法论软件工程方法论包含3个要素:方法、工具和过程。 软件工程方法为建造软件提供技术上的解决方案(“如何做”)。方法覆盖面很广,包括沟通、需求分析建模、设计建模、编程、测试和维护支持。 工具为方法的运用提供自动的或半自动的软件支撑环境。 过程是为了获得高质量的软件所需要完成的一系列任务的框架,它规定了完成各项任务的工作步骤。

July 11, 2019 · 1 min · jiezi

探讨奇技淫巧

探讨奇技淫巧起源在工程实践中,我们常常会遇到一些奇技淫巧。所谓奇技淫巧,就是官方在设计或者实践中并未想象出的代码风格或者使用场景。其实也就是类似于 react 的 hoc,本来源自于社区,但是该方案却成为了官方肯定的方案。那么究竟应不应在平时学习呢?究竟应不应该在工程中使用呢,或者使用怎么样的奇技淫巧。 两年前。我还没有毕业,在大学的最后一个学期中选择了进入前端,同时,被吸引到前端阵营中一个不得不说的原因就是 js 的奇技淫巧,同时个人是一个比较猎奇的人,所以就学了很多关于 js 的奇技淫巧。 现在这些奇技淫巧要么变成了这门语言不可或缺的一部分,要么随着时间的推移而消失,还有一些在不知不觉中却忘记了,既然这次的文章是介绍这方面的知识,也就多介绍一下之前学习的一些例子。 ~ 运算符 + indexOf在 es6 includes 尚未推行之前,我们判断判断字符串或者数组包含只能使用 indexOf 这个方法,但是 indexOf 返回的确实元素的索引,如果不存在则返回 -1。因为在之前写 c 语言的时候,我们往往使用 0 代表成功,1 2 3代表着不同的错误。因为0是独一无二的。在类c的语言中是具有 truthy falsy 这个概念。并不指代bool的 true 与 false。 下表代表了js 的 truthy 以及 falsy。 变量类型falsytruthy布尔falsetrue字符串" "非空字符串数值0 NaN任何不为falsy的数值null是否undefined是否对象(数组), {} 以及 []否是对于数值而言,我们知道 0 对于数值是唯一的,而 -1不是。那么我们可以通过 ~ 运算符来把-1 变为 0. ~-1// 0~1//-2解释下 对每一个比特位执行非(NOT)操作。NOT a 结果为 a 的反转(即反码)。 9 (base 10) = 00000000000000000000000000001001 (base 2) ~9 (base 10) = 11111111111111111111111111110110 (base 2) = -10 (base 10)因为在计算机中第一位代表着 符号位置。 ...

June 5, 2019 · 3 min · jiezi

观远AI实战 | 机器学习系统的工程实践

图片描述「观远AI实战」栏目文章由观远数据算法天团倾力打造,观小编整理编辑。这里将不定期推送关于机器学习,数据挖掘,特征重要性等干货分享。本文8千多字,约需要16分钟阅读时间。机器学习作为时下最为火热的技术之一受到了广泛的关注。我们每天打开公众号都能收到各种前沿进展、论文解读、最新教程的推送。这些文章中绝大多数内容都跟酷炫的新模型、高大上的数学推导有关。但是Peter Norvig说过,“We don’t have better algorithms. We just have more data.”。在实际机器学习应用中,对最终结果起到决定性作用的往往是精心收集处理的高质量数据。从表面看好像也不难,但略微深究就会发现机器学习系统与传统的软件工程项目有着非常大的差异。除了广受瞩目的模型算法,良好的工程化思考与实现是最终达到机器学习项目成功的另一大关键因素。谷歌在2015年发表的论文《Hidden Technical Debt in Machine Learning Systems》中就很好的总结了机器学习工程中的各种不佳实践导致的技术债问题。主要有以下几种:系统边界模糊在传统的软件工程中,一般会进行细致的设计和抽象,对于系统的各个组成部分进行良好的模块划分,这样整个系统的演进和维护都会处于一个比较可控的状态。但机器学习系统天然就与数据存在一定程度的耦合,加上天然的交互式、实验性开发方式,很容易就会把数据清洗、特征工程、模型训练等模块耦合在一起,牵一发而动全身,导致后续添加新特征,做不同的实验验证都会变得越来越慢,越来越困难。数据依赖难以管理传统的软件工程开发中,可以很方便的通过编译器,静态分析等手段获取到代码中的各种依赖关系,快速发现不合理的耦合设计,然后借助于单元测试等手段快速重构改进。在机器学习系统中这类代码耦合分析同样不可或缺。除此之外还多了数据依赖问题。比如销售预测系统可能会对接终端POS系统数据,也会引入市场部门的营销数据,还有仓储、运输等等多种数据来源。在大多数情况下这些数据源都是不同部门维护的,不受数据算法团队的控制,指不定哪天就悄悄做了一个变更。如果变更很大,可能在做数据处理或者模型训练时会直接抛出错误,但大多数情况下你的系统还是能正常运行,而得到的训练预测结果很可能就有问题了。在一些复杂业务系统中,这些数据本身还会被加工成各种中间数据集,同时被几个数据分析预测任务共享,形成复杂的依赖关系网,进一步加大了数据管理的难度。机器学习系统的反模式胶水代码:随着各种开源项目的百花齐放,很多机器学习项目都会调用各种开源库来做数据处理、模型训练、参数调优等环节。于是自然而然在整个项目中大量的代码都是为了把这些不同的开源库粘合在一起的胶水代码,同样会导致之前提到过的边界模糊,与特定的库紧耦合,难以替换模块快速演进等问题。流水线丛林:在数据处理特征工程与模型调优的迭代演进过程中,稍不注意你的整个系统流水线就会变得无比冗长,各种中间结果的写入和前后依赖极其复杂。这时候想添加一个新特征,或是调试某个执行失败都变得如此困难,逐渐迷失在这混乱的丛林中……如果只具备机器学习知识而缺少工程经验,用这种做实验的方式来开发系统显然是不靠谱的,必须有良好的工程化思维,从总体上把控代码模块结构,才能更好的平衡实验的灵活性与系统开发效率,保证整体的高效运作。 失效的实验性代码路径:这一点也是承接前面,很多时候如果以跑实验的心态来给系统“添砖加瓦”,很可能到后面各种小径交叉的代码库就这么出现了,谁都搞不清楚哪些代码有用哪些是不会执行到的。如何复现别人的实验结果,要用哪些数据集和特征,设置哪些变量、做哪些微调都会成为难解之谜。缺乏好的系统抽象:个人觉得sklearn的各种API设计还算蛮好的,现在很多其它库的高层API都参考了这个准业界标准来实现。文中主要提到在分布式训练中缺乏一个很好的业界标准,比如MapReduce显然是不行的,Parameter Server看起来还算靠谱但也还未成为标准。没有好的抽象标准也就导致了各种库在功能、接口设计时不一致,从而有了以上提到的一系列边界模糊,胶水代码等问题。配置项技术债相对于传统软件系统,机器学习系统的配置项往往会更多更复杂。比如要使用哪些特征、各种数据选择的规则、复杂的预处理和后置处理、模型本身的各种参数设置等等。因此除了工程代码外,配置项的精心设计、评审也成了一个不容忽视的点。否则很容易造成系统在实际运行中频繁出错,难以使用。变化无常的外部世界机器学习系统很多时候都是直接与外部世界的数据做交互,而外部世界总是变化无常。而且机器学习系统本身的输出也会影响到外部世界,从而进一步回馈到机器学习系统的输入中来。比如推荐系统给用户展示的内容会影响用户的点击行为,而这些点击浏览行为又会成为训练数据输入到推荐系统来。如何获取到外部世界变化的信息,进而及时改进甚至自动更新算法模型就成了一个非常重要的问题。在谷歌的这篇原始论文中对各种坑都给了一些解决的建议,归纳总结一下,总体上来说就是要转变团队整体的文化氛围,强调良好的工程思维和实践。一个设计良好的机器学习项目系统中往往真正跟机器学习相关的代码只占了很小的一部分。图片描述新模型固然酷炫,但是机器学习工程实践的总结与推广与它在整个项目中扮演的角色的重要性显然是不成正比的。所以今天我们要着重来讲一下这个方面:机器学习系统的最佳工程实践是什么样的?这时候就要请出谷歌的另外一篇论文《The ML Test Score》了。前一篇论文在具体实践落地方面缺乏细节,在这篇论文里,谷歌总结了28个非常具体的机器学习系统相关工程实践准则,可谓是干货满满,十分接地气。文中给出的28个建议都是针对机器学习系统的,没有包含通用软件工程里那些单元测试,发布流程等内容,在实践中这些传统最佳实践也同样非常重要。这些实践在谷歌内部团队广泛使用,但没有一个团队执行的覆盖率超过80%,因此这些测试点都是非常值得关注并有一定的实践难度的。特征与数据测试特征期望值编写到schema中:很多特征的分布情况或数值期望是有一些先验知识可以去校验的。比如一般人身高都在0-3米的范围内、英语中最常见的词是”the”、整体的词频一般服从幂律分布等。我们可以把这些先验领域知识,或是从训练集中计算出的数学期望值编写在数据schema文件中,后续对于新的输入数据,构建完特征后的模型训练数据以及最终上线使用模型时都能进行自动化的检查,避免因为数据不符合预期而导致的错误预测情况。确保所有的特征都是有用的:在之前的机器学习技术债论文中也有提到研发人员总是倾向于不断往系统中添加新的特征,尤其在上线时间比较紧迫的情况下,缺少细致的特征选择和有效性验证工作。这会导致特征数量越来越多,构建训练集需要花费的时间也越来越长,后续的维护成本也会更高。所以跟业务代码一样,没有帮助的特征也要及时清理,轻装前行。文中给出的方法基本是常见的特征选择法,比如计算特征相关度,使用单独或小批量特征来跑模型看是否有预测能力等。去除性价比低的特征:计算添加任何一个特征都需要消耗资源,包括生成和训练模型开销,模型预测开销,甚至还要考虑到对上游数据的依赖,额外的库函数引入,特征本身的不稳定性等等。对于任何一个特征的添加,都要综合考虑这些开销与它能带来的性能提升来决定是否引入。如果只是非常有限的效果提升,我们应该果断放弃那些过于复杂的特征。特征必须遵循业务规范需求:不同的项目对机器学习系统可以使用的数据可能有不同的规范需求,比如可能有些业务禁止我们使用从用户数据中推演出来的特征。所以我们也需要从代码工程层面把这些规范需求进行实现,以避免训练与线上特征出现不一致或违反了业务规范等问题。数据流水线必须有完善的隐私控制:与上一条类似,机器学习系统从数据源获取用户相关隐私数据时已经通过了相应的控制校验,后续在系统内部流水线做处理时我们也要时刻注意对隐私数据的访问控制。比如各种中间数据集,数据字典的存放与访问控制,上游系统的用户数据删除能够级联更新到机器学习系统的整个链路中,诸如此类需要特别注意的问题。能够快速开发新特征:一个新特征从提出到实现,测试,上线的整个流程所需要花费的时间决定了整个机器系统迭代演进,响应外部变化的速度。要实现这一点,良好的工程结构、不同模块的抽象设计都是非常重要的。文中没有给具体的例子,不过我们可以借鉴sklearn中pipeline模块设计的思想,以及类似FeatureHub这样的开源系统的实现来不断优化完善特征工程实践。为特征工程代码写相应的测试:在实验探索阶段,我们经常会写完一个特征之后,粗略地取样一些数据,大致验证通过后就认为这个特征基本没有问题了。但这其中可能就隐藏了不少bug,而且不像业务代码中的错误,要发现这些bug极其困难。所以必须养成良好的习惯,在特征开发阶段就写好相应的测试代码,确保特征的正确性,后续应对各种系统变更也都能很快通过测试来进行快速验证。图片描述模型开发测试模型说明必须通过review并记录在案:随着机器学习模型技术的发展,各种复杂模型,大量的参数配置往往让模型训练和执行变得无比复杂。加上在多人协同的项目中,很多时候需要使用不同的数据,或者做一些特定的调整来重新评估模型效果,这时候有详细的模型说明记录就显得尤为重要了。除了简单的文本记录,市面上也有不少开源项目(比如ModelDB,MLflow等)专注于帮助开发者管理模型,提高实验的可复现性。模型优化指标与业务指标一致:很多机器学习的应用业务中,实际的业务指标并不能直接拿来作为目标函数优化,比如业务营收,用户满意度等等。因此大多数模型在优化时都会选择一个“代理指标”,比如用户点击率的logloss之类。因此在建模,评估过程中必须要考虑到这个代理指标与真实业务指标是否有比较强的正相关性。我们可以通过各种A/B测试来进行评估,如果代理指标的改进无法提升真正的业务指标,就需要及时进行调整。调优模型超参数:这点相信大家都会做,毕竟各种机器学习教程中都会有很大篇幅讲解如何进行调参来提升模型效果。值得注意的是除了暴力的网格搜索,随机搜索同样简单而效果往往更好。另外还有许多更高级的算法例如贝叶斯优化,SMAC等也可以尝试使用。对于同一个数据集,在使用不同的特征组合,数据抽样手段的时候理论上来说都应该进行参数调优以达到最佳效果。这部分的工作也是比较容易通过自动化工具来实现的。对模型时效性有感知:对于很多输入数据快速变化的业务例如推荐系统,金融应用等,模型的时效性就显得极其重要了。如果没有及时训练更新模型的机制,整个系统的运行效果可能会快速下降。我们可以通过保留多个版本的旧模型,使用A/B测试等手段来推演模型效果与时间推移的关系,并以此来制定整体模型的更新策略。模型应该优于基准测试:对于我们开发的复杂模型,我们应该时常拿它与一些简单的基准模型进行测试比较。如果需要花费大量精力调优的模型效果相比简单的线性模型甚至统计预测都没有明显提升的话,我们就要仔细权衡一下使用复杂模型或做进一步研发改进的必要性了。模型效果在不同数据切片上表现都应达标:在验证模型效果时,我们不能仅依赖于一个总体的验证集或是交叉验证指标。应该在不同维度下对数据进行切分后分别验证,便于我们对模型效果有更细粒度上的理解。否则一些细粒度上的问题很容易被总体统计指标所掩盖,同时这些更细粒度的模型问题也能指导我们做进一步细化模型的调优工作。例如将预测效果根据不同国家的用户,不同使用频率的用户,或者各种类别的电影等方式来分组分析。具体划分方式可以根据业务特点来制定,并同时考察多个重要的维度。我们可以把这些测试固化到发布流程中,确保每次模型更新不会在某些数据子集中有明显的效果下降。将模型的包容性列入测试范围:近些年来也有不少关于模型包容性,公平性相关问题的研究。例如我们在做NLP相关问题时通常会使用预训练的word embedding表达,如果这些预训练时使用的语料与真实应用的场景有偏差,就会导致类似种族,性别歧视的公平性问题出现。我们可以检查输入特征是否与某些用户类别有强关联性,还可以通过模型输出的切分分析,去判断是否对某些用户组别有明显的差异对待。当发现存在这个问题时,我们可以通过特征预处理(例如文中提到的embedding映射转换来消除例如性别维度的差异),模型开发中的各种后置处理,收集更多数据以确保模型能学习到少数群体的特性等方式来解决这个问题。图片描述机器学习基础设施测试模型训练是可复现的:在理想情况下,我们对同一份数据以同样的参数进行模型训练应该能获取到相同的模型结果。这样对于我们做特征工程重构验证等都会非常有帮助。但在实际项目中做到稳定复现却非常困难。例如模型中用到的随机数种子,模型各部分的初始化顺序,多线程/分布式训练导致的训练数据使用顺序的不确定性等都会导致无法稳定复现的问题。因此我们在实际工程中对于这些点都要额外注意。比如凡是用到了随机数的地方都应该暴露接口方便调用时进行随机数种子的设置。除了尽量写能确定性运行的代码外,模型融合也能在一定程度上减轻这个问题。模型说明代码需要相应测试:虽然模型说明代码看起来很像“配置文件”,但其中也可能存在bug,导致模型训练没有按预期方式执行。而且要测试这种模型说明代码也非常困难,因为模型训练往往牵涉到非常多的外部输入数据,而且通常耗时较长。文中提到谷歌将会开源一些相关的框架来帮助做相关的测试,一些具体的测试方法如下:用工具去生成一些随机数据,在模型中执行一个训练步骤,检验梯度更新是否符合预期。模型需要支持从任意checkpoint中恢复,继续执行训练。针对算法特性做简单的检验,比如RNN在运行过程中每次应该会接受输入序列中的一个元素来进行处理。执行少量训练步骤,观察training loss变化,确定loss是按预期呈现不断下降的趋势。用简单数据集让模型去过拟合,迅速达到在训练集上100%的正确率,证明模型的学习能力。尽量避免传统的”golden tests”,就是把之前一个模型跑的结果存下来,以此为基准去测试后面新模型是否达到了预期的效果。从长期来看由于输入数据的变化,训练本身的不稳定性都会导致这个方法的维护成本很高。即使发现了性能下降,也难以提供有用的洞察。机器学习pipeline的集成测试:一个完整的机器学习pipeline一般会包括训练数据的收集,特征生成,模型训练,模型验证,部署和服务发布等环节。这些环节前后都会有一定交互影响,因此需要集成测试来验证整个流程的正确性。这些测试应该包括在持续集成和发布上线环节。为了节约执行时间,对于需要频繁执行的持续集成测试,我们可以选择少量数据进行验证,或者是使用较为简单的模型以便于开发人员快速验证其它环节的修改不会引起问题。而在发布流程中还是需要使用与生产环境尽可能一致的配置来执行整体的集成测试。 模型上线前必须验证其效果:这点看起来应该是大家都会遵守的原则。唯一要注意的是需要同时验证模型的长期效果趋势(是否有缓慢性能下降),以及与最近一个版本对比是否有明显的性能下降。模型能够对单个样本做debug:这个就有点厉害了,当你发现一个奇怪的模型输出时,你怎么去寻找问题的原因?这时候如果有一个方便的工具能让你把这个样本输入到模型中去,单步执行去看模型训练/预测的过程,那将对排查问题带来极大的便利和效率提升。文中提到TensorFlow自带了一个debugger,在实际工作中我们也会使用eli5,LIME之类的工具来做黑盒模型解释,但从方便程度和效果上来说肯定还是比不上框架自带的排查工具。模型上线前的金丝雀测试:这点在传统软件工程中基本也是标配,尽管我们有测试环境,预发布环境的各种离线测试,模型在正式上线时还是需要在生产环境中跑一下简单的验证测试,确保部署系统能够成功加载模型并产出符合预期的预测结果。在此基础上还可以进一步使用灰度发布的方式,逐渐把流量从旧模型迁移到新模型上来,增加一层保障。模型能够快速回滚:与其它传统软件服务一样,模型上线后如果发现有问题应该能够快速,安全回滚。要做到这点必须在开发时就确保各种上下游依赖的兼容性。并且回滚演练本身也应该成为常规发布测试的一环,而不是到出了问题才去手毛脚乱的操作,引出更多意料之外的问题。图片描述监控测试依赖变更推送:机器学习系统一般都会大量依赖于各种外部系统提供的数据,如果这些外部系统的数据格式,字段含义等发生了变化而我们没有感知,很容易就会导致模型训练和预测变得不符合预期。因此我们必须订阅这些依赖系统的变更推送,并确保其它系统的维护者知晓我们在使用他们提供的数据。 训练与线上输入数据分布的一致性:虽然模型内部运作过程极为复杂,难以直接监控其运行时正确性,但是模型的输入数据这块还是比较容易监控的。我们可以用之前定义的特征数据schema文件来对线上输入数据进行检测,当出现较大偏差时自动触发告警,以便及时发现外部数据的变化情况。训练与线上服务时生成的特征值一致:在机器学习项目中经常会出现同一个特征在训练时和在线上使用时所采用的计算生成方式是不一样的。比如在训练时特征是通过处理之前的历史日志计算出来的,而到了线上的时候同样的特征可能来自于用户实时的反馈。或者是训练时采用的特征生成函数是非常灵活,易于做各种实验尝试的,而到了线上则改用固化的优化计算性能版本以降低服务响应时间。在理想情况下,虽然采用了不同的计算方式,但生成的特征值应该是相同的,否则就会出现训练和预测使用的数据有偏移,导致模型效果变差等问题。所以我们需要通过各种手段来监控线上线下数据的一致性。例如可以通过对线上数据进行采样打标记录,来与训练集中的对应条目进行直接比较,计算出有偏差的特征数量,及这些特征中相应的有偏差的样本占比数量。另外也可以通过计算线上线下各个特征的统计分布的差别来衡量是否有这类问题的产生。模型的时效性:这一点与之前的模型测试中的时效性类似,我们可以直接使用其中获取到的模型效果与时间推移关系来推断理想情况下模型训练更新的频率,并以此来对模型持续运行时间进行监控和告警。要注意过长的更新周期会提升模型的维护难度。另外哪怕模型本身更新比较可控,但是模型依赖的数据源也有类似的时效性问题,我们同样需要对其进行监控以免出现数据过期的问题。数值稳定性:在模型训练中可能会在没有任何报错的情况下出现奇怪的NaN,inf值,导致非预期的参数更新甚至最终得到不可用的模型。因此我们需要在训练中监控任何训练数据中包含NaN或者inf的情况进行适当的处理。同时对于各模型参数的取值范围,ReLU层后输出为0的单元数量占比等指标进行检测,一旦超出预期范围就进行告警,便于及时定位排查相关问题。模型性能相关监控:机器学习模型的训练速度,预测响应时间,系统吞吐量,以及内存占用等性能指标对于整体系统的可扩展性来说都是至关重要的。尤其是随着数据量的越来越大,越来越复杂模型的引入,更加剧了各种性能问题的出现。在模型演进,数据变化以及基础架构/计算库的更迭中,需要我们谨慎的评估模型性能变化,进行快速响应。在做性能监控时不但要注意不同代码组件,版本的表现,也要关注数据和模型版本的影响。而且除了容易检测到的性能突变,长期,缓慢的性能下降也需要引起足够的重视。模型预测质量的回归问题:总体的目标是希望新部署的模型相对于过去的模型在预测性能上没有下降。但验证集相对于线上的真实情况来说总是有所区别的,只能作为一个性能评估的参考。文中列举了几种手段来做监控:衡量预测的统计偏差,正常情况下模型的预测偏差应该为0,如果不是则说明其中有一定问题。对于能在作出预测后很快得到正确与否反馈的任务类型,我们可以以此进行实时监控,迅速判断模型效果相比之前是否有下降。对于在模型提供服务时无法快速获取到正确标签类型的任务类型,我们可以使用人工标记的验证数据来比较模型效果的变化。最后总结一下前面提到的各种监控,基本上还有两个共通点,一是各种告警的阈值要做精心选择,过低的阈值容易出现警报泛滥导致根本没人去管的情况,而过高的阈值又会掩盖系统中已经存在的各种问题。二是除了短时间内明显的指标急剧下降外,同时也要关注长期的缓慢的下降,后者更难以发现,应该引起足够的重视。图片描述文章后续还给出了这28个测试指标的具体评分标准,帮助大家在实践中更好的对照,应用这些最佳实践。还有很多在谷歌内部使用这套评分系统的各种反馈,以及他们各个团队的平均得分情况等。图片描述对于这个平均得分情况,作者强力安利了一把谷歌自家的TFX。 图片描述比如基础架构的集成测试,谷歌内部的得分也很低(满分为1的情况下平均为0.2分)。TFX可以方便的实现整个工程的pipeline,自然也很容易在此基础上完成相应的集成测试了。除了TFX,像Uber的Michelangelo,Facebook的FBLearner Flow也都是类似的机器学习平台,虽然没有开源,但都有比较详细的介绍文章可以学习参考。![图片描述][9]这些框架基本可以看作各家公司做机器学习工程的最佳实践总结,总体架构基本都遵循“数据管理->模型训练->模型测试验证->部署上线”这样的流程。不过他们在具体设计实现上各有千秋,例如文中作者认为“线下线上训练数据分布偏差监控”是最为重要但却鲜有实现的一个测试点,在很多项目组都曾经因为缺少这个监控而出现过多次线上故障。因此TFX提供了数据验证模块来专门解决这个问题。而像Uber的Michelangelo则侧重快速开发特征这个点引入了Feature Store模块。在实际应用中我们可以根据不同的业务特点灵活的选择工程方案来进行实现。参考资料:https://papers.nips.cc/paper/…https://ai.google/research/pu…本文作者:观远数据算法天团-zijie关注机器学习与分布式系统方向知乎号:zijie0 | Blog:zijie0.github.io观远数据合伙人 / 知乎5000赞文章博主观远算法天团又称余杭区五常街道数据F4,成员多来自于帝国理工大学、纽约大学、清华大学、浙江大学、上海交大等国内外知名学府,并有阿里大数据产品与技术研发的从业背景。2018年凭借先进的算法能力,在微软大中华区智慧零售(Smart Retail)AI挑战大赛中,两度斩获冠军,并从1500多家创新公司中脱颖而出,全票入选腾讯AI加速器第二期。作为本专栏特邀产出者,观远算法天团将在未来持续为大家分享更多精彩干货,敬请期待!

January 17, 2019 · 1 min · jiezi