Web站点的响应速度 雅虎在2006年就发布了一份提升Web站点响应速度的最佳实践指南。该指南包含了35条规则,分为7个类别。这些规则已经被广泛使用,并指导人们在新的站点设计时更有针对性的考虑问题。这份指南已经成了Web前端性能度量的一个事实标准了。
YSlow是一个基于这份指南的测试工具,它可以测试一个站点是否“慢”,以及为什么“慢”?你可以通过很多方式来使用YSlow,比如Firefox,Chrome的插件,命令行工具,甚至PhantomJS这样的无头(Headless)浏览器。YSlow会检测你的站点中的资源是否没有压缩,是否缺失了超时设置,更进一步,它还会检测你的JS/CSS是否已经压缩/精简化,图片的尺寸,是否使用了CDN等等很多的维度。它还可以生成很多格式的报告,比如打分信息,TAP协议的输出,以及junit测试报告的格式。
我们这里讨论如何在持续集成服务器上设置一个YSlow任务,这个任务会在每次构建之后,测试你应用的性能指标,以帮助你更快的发现和定位问题。当然,我推荐你在staging环境,很多开发者在测试环境,本地开发环境都会启动很多对Debug友好的设置,比如未压缩的JS/CSS,没有超时设置的响应等,这会导致该构建任务的打分不够准确。
搭建CI环境 按照传统方式,如果要搭建一个这样的CI任务,我们需要至少做这样一些事情:
安装JDK 安装Jenkins 安装PhantomJS 安装YSlow.js脚本 然后设置环境变量,在Jenkins上创建任务,并运行YSlow.js脚本。这个任务很简单,只需要设置好参数,然后将结果输出为Jenkins上的报告即可。比如:
$ phantomjs /var/share/yslow.js -i grade -threshold "B" -f junit \ http://bookmarks-frontend.s3-website-us-west-2.amazonaws.com/ > yslow.xml -i grade 展示打分(grade)信息(还可以是basic/stats/all)等 -threshold "B" 指定失败的阈值为B -f junit 输出为junit识别的XML格式 这里的阈值可以是数字(0-100分),字母(A-F级别)或者一个JSON字符串(混合使用)
-threshold '{"overall": "B", "ycdn": "F", "yexpires": 85}' 上面的命令会测试站点http://bookmarks-frontend.s3-website-us-west-2.amazonaws.com/的各项指标,并应用雅虎的那35条规则,并最终生成一个junit测试报告格式的文件:yslow.xml。
但是维护CI环境是一个比较麻烦的事情,而且既然每个项目都可能会用到这样的基础设施,一种好的做法就是将其做成一个镜像保存起来,以方便其他项目的复用!这里我们使用docker来安装和配置我们的CI环境,配置完成之后,我们可以将docker的镜像分享给其他团队,也可以供我们在下一个项目中使用。
基于docker/docker-compose的环境搭建 在docker出现之前,我们要搭建一个测试或者staging环境,往往需要很多个不同角色的机器:有专门的数据库服务器,文件服务器,缓存服务器,Web服务器,反向代理等等。这样在成本上显然是个不小的开销,如果将所有不同的组件部署在同一台机器上,则又可能互相干扰,只需要一个小小的失误,整个系统就需要重新配置。更可怕的是,这个环境和生产系统不一致,那么很可能真实的错误要等到系统上线之后才会被发现。
比如在2012年,我所在的一个项目中,客户的系统采用传统的J2EE架构。本地开发中,我们使用了Jetty作为容器,而测试和Staging环境使用了Tomcat。由于Tomcat对空格的处理和Jetty有所不同,我们在本地测试通过,并且运行良好的代码,在Staging变得完全不能工作。这个问题花费了团队很多时间来排查错误。
在docker出现之后,我们可以在一台物理机器上运行多个互不干涉的容器,每个容器可以是一个组件(比如运行数据库的容器,Web服务器容器等等)。这样不但缩减了成本,而且可以让我们的环境尽可能和生产环境一致(有的项目甚至直接将CI出来的镜像应用到生产中)。不过对多个容器的管理是一个很麻烦的事情,好在docker提供了docker-compose工具来解决这个问题。使用docker-compose可以定义一组互相独立,但是又可以协作在一起的容器,这样我们可以很容易的搭建一个仿生产环境。
比如我们可以定义个docker-compse.yml
app:build:.links:- db:postgresports:- 8000:8000volumes:- .:/appworking_dir:/appentrypoint:/app/start.shenvironment:JDBC_DATABASE_URL:jdbc:postgresql://postgres:5432/bookmarksDATABASE_USER:bookmarks-userDATABASE_PASS:bookmarks-passworddb:image:postgres:9.3ports:- 5432:5432environment:POSTGRES_DB:bookmarksPOSTGRES_USER:bookmarks-userPOSTGRES_PASSWORD:bookmarks-password这个docker-compose定义了两个组件,app和db。db使用了postgres:9.3镜像,并设置了自己的环境变量。app则从当前目录.构建一个新的镜像,app与db通过links属性连接起来。
如果在当前目录执行docker-compose build命令,docker-compose会找到本地的Dockerfile,然后构建出一个docker的镜像,并启动该容器,同时,它还会启动postgres:9.3容器作为数据库组件。这样我们的环境就被完整的搭建好了。
搭建CI环境 app:build:.ports:- 8080:8080- 50000:50000volumes:- ./data:/var/jenkins_home这个配置,表明我们会根据当前目录的Dockerfile来构建一个镜像。
通过命令volumns,我们将本地目录./data映射为jenkins_home,这样我们定义的job信息,以及插件的安装都会放到本地的目录中,方便管理。配置完成之后,构建并启动该容器:
FROM jenkins:latest # Env ENV PHANTOMJS_VERSION 1....
成为咨询师 本文旨在帮助开发完成向咨询师的转变,内容不但涉及向UX学习,还包括思维方式的转变。我尽量采用一些亲历的例子来说明该如何做,也会适当的解释为什么需要这样做。不过在展开详细讨论之前,首先来澄清这里提到的三种角色。
开发(Developer)角色 开发是指那些喜欢写代码,享受写代码,喜欢纯粹,讨厌办公室政治,永远穿T恤的有些偏执的程序员。跟他们打交道,有这样一些注意事项:
不要让他们帮你盗QQ号 不要让他们帮你修电脑或者装Windows系统 不要跟他们讨论人文/政治类的问题 开发往往还单纯的可爱,除此之外,他们还有这样一些特点:
逻辑清晰 与人争辩时往往可以通过清晰的逻辑而获胜 单身 业界已经有很多关于开发的描述了,我这里也有一个描述开发的列表:
当然,要严格界定一个人是不是`开发`是非常困难的,大多数情况下,他们沉默寡言,遇到程序中的bug或者在调试某些库的问题时眼神呆滞,口中念念有词,他们不太喜欢和陌生人说话,在晚上精神充沛,白天则显得有些呆滞,喜欢喝咖啡,相信世界上有绝对的正确和错误,往往会带着非黑即白的二分法来看待事物,生活很难自理,喜欢机械键盘/电子设备,周末宁愿宅在家里写代码也不去做社交…… 用户体验设计师(UX) UX是指用户体验设计师,在本文的上下文中,更偏向与非视觉设计的那些设计师(产品设计师)。在项目中,他们会做用户调研,竞品分析,信息架构简历,交互设计(纸上原型,低保真)等活动,并负责开发纸上原型,验证这些原型等。
和UX打交道,也有一些应该注意的点,比如:
不要叫他们美工 不要对他们说诸如:“帮我美化一下这个页面”,“这个颜色得再亮一些”之类的话 不要跟他们讲关于程序员的笑话 事实上,人们对UX的误解很深。提到UX人们的第一反应是PhotoShop,P图/切图。这仅仅是他们日常工作中很小的一部分。大部分UX还要做很多用户研究,信息架构整理的事情。老实说,我在去年5月之前的对UX的认识和大部分开发的认识是一样的,但是在后来的项目上和多个UX合作过之后,我彻底改变了原先那种偏见,开始敬佩他们,并向他们学习。
设计工作可以细分为这样一些不同的方面(图片来源网络):
UX的一项特别的技能在于能从复杂的现实世界中抽象出清晰的信息(用户画像,体验地图甚至最后的用户故事)。这项技能不但重要,而且还很牛逼。
知识的诅咒 《反脆弱》里有个有意思的例子:人们仅仅创造了非常有限的词汇来描述颜色,比如蓝色,红色,而任何一个视觉正常的人都可以轻松的识别出数百种不同的颜色。也就是说,人们可以很轻松的理解相当复杂的事物,但是很难向别人描述该事物(想象一下向别人描述一只章鱼的颜色)。
人们对于现实世界中的事情(特别是复杂的业务场景)往往只能意会而很难言传,再加上知识的诅咒(我在《如何写一本书》里,详细讨论了这种常见的陷阱)的存在,当用户在描述A的时候,在没有上下文的人听来,很可能是B或者C。这种情况在软件开发中非常常见,也是很多项目之所以延期的原因(大量并无必要的返工,需求澄清等)。
在项目前期,UX需要和客户坐在一起,将客户的需求分析清晰。分析细节包括业务场景,用户画像生成,信息架构,体验地图等等,这些信息并不是天然就显现的,恰恰相反,它们需要UX经过很多轮的辛苦引导,从用户的脑海里提取出来的。
这里需要UX的核心能力是:
有目的的抛出问题,引导客户进行发散 有节奏的收敛,形成共识 不断修正过程中的错误 可视化能力(这可能是大部分人觉得唯一和UX相关的点) 咨询师 咨询师是指那些根据自己的丰富经验来帮助客户解决具体问题的人。这些问题并不一定局限在技术上 —— 比如架构的设计,具体前端/后端技术的选定,还包括一些流程的改善。比如引入新的工程实践来缩减项目的周期时间,帮助团队发现问题,建设团队的能力,作为各个团队间的润滑剂帮助项目成功等等。
咨询师工作中的一个常见的场景是:
列出目前遇到的问题 确定各个问题的优先级(和各个利益方) 制定方案 给方案加上时间,形成计划 细化计划中的条目,并促成它 引导/启发 我在印度的某一期TWU当教练的时候,发现了一个很有意思的现象,国外的同事在组织培训时更强调用引导/启发的方式,让学生们自己得出结论,并在课堂上进行讨论,以期教学相长。只有在过程中有启而不发的情况出现时,教练才会适当抛出自己的开发,并再次启动讨论。
与我一直的认识不同的是,这种方式效果很好。通过一些适当的启发,学生很容易自己讨论出一些有趣的看法,然后教练在这个基础上做一些总结,并帮助他们分析不同看法/想法之间的优劣。
我非常认同这种模式,后来自己组织的其他培训/workshop也都尽量采取这种方式。咨询师在客户现场,也应该采取这种引导的方式帮助团队来完成能力建设,而不是事必躬亲。
角色转化 从开发者视角切换到咨询师的第一要诀就是:让团队解决自己遇到的问题!乍听起来,咨询师好像变成一个多余的角色了:既然团队自己可以搞定,还要咨询师干什么呢?咨询师的职责是让团队意识到问题,理清思路,制定解决方案,并逐步实施。
使能/赋能 我们来看一个简单的例子:在客户现场,你发现团队往往在集成时会花费很多额外的时间和返工,开发过程中大家各自为政,没有人知道一次commit会给软件包造成什么影响。
如果你是一个咨询师,应该如何解决这个问题?一个常犯的错误是,直接上手帮助团队搭建持续集成(CI)环境,并设置CI纪律(比如build红了不许过夜,红的时候其他人都不许commit等)。
一种更好的做法是:做为咨询师,首先需要帮助团队认识到这个问题,你需要让所有人都知道,我们现在的问题是什么。在所有人都清楚了这一点之后,你需要提出(或者引导出)持续集成的概念(因为根据经验,这是一种可以很好的解决集成时额外的返工现象的好办法)。
但是对于不熟悉持续集成的团队来说,搭建一个持续集成环境是一个非常复杂的任务。因此你需要分解这个任务为一些更小的,可以被解决的问题。
申请虚拟机资源 安装jenkins(包括安装JVM,创建用户等) 配置本地构建脚本到jenkins(构建脚本,自动化测试等) 申请显示器资源(作为CI Monitor) 将结果显式在CI Monitor上 有了任务之后,你需要分别为这些子任务分配owner。对比搭建持续集成环境这样的大任务,这些小的任务已经非常具体,更重要的是,他可以被团队中任何人理解并解决。...
什么是引导(facilitation) 引导(facilitation)的词根来源与拉丁语“facil”,意思是“让……更容易”。而负责引导的引导师(facilitator)的核心职责是,通过一系列的活动、技巧,保证引导会议顺畅的进行,并解决整个过程中的问题,使得参与者就问题产生一个共识,达成一个结论。
其中可能涉及很多具体的问题,比如几乎在每个会议中都可能看到的:
如果有人尝试将会议变成一言堂,如何处理? 如果参与者不愿意分享自己的观点,如何处理? 过程中,两个参与者产生了争执,如何处理? 如何把握节奏,刺激与会者发散? 如何在收集到足够信息后,进行收敛? 显然,这是一个技术活儿。一次好的引导可以将与会者的众多想法,信息聚合起来,形成对团队下一步要做什么有极强指导意义的方案。
日常的引导活动 在平时的工作中,我们其实已经在频繁的使用引导活动,但是很少有人将其作为体系来关注,也很少有人能将这个能力应用在其他方面(比如在客户现场咨询,或者参加售前等)。引导是如此的常见,以至于我们对其视而不见。比如在interview完成之后,所有面试官和HR一起做的well/less well的列举;各种社区活动(Open Party,CDConf等)之后的回顾;每个项目在一个迭代结束后的Retro;对于某个问题的头脑风暴等等。
项目回顾会议 在开始前,引导师需要保证团队:
每个人都有开发的态度 整个过程需要在一个足够安全的环境中进行(Safe Check) 有时候,有Team Lead在场,新人可能不愿意对某事(比如最近加班有点过分)发表自己的看法等。这时候需要有Safe Check,比如分为1到5档,大家用不记名投票的方式来表述自己是否觉得安全。如果投票结果显示大部分人都觉得不安全,则需要与会的人中,职位最高的那个人离开会议,然后再做一次Safe Check,直到大家都觉得足够安全。不过,对于已经进行过多轮回顾的团队,我们往往会忽略掉这一步。
Retro过程是,团队坐在一起,回顾上一个迭代(通常是两个星期)做过的事情,有哪些做的比较好,哪些有待改进,有哪些疑惑等等。Retro可以有很多的形式,比如简单的Well/Less Well/Questions,更聚焦在产生Action的海星式等等。
通常的顺序是:
引导者请大家用纸笔将想法写在便签(stick)上 Time box这个过程(通常是5分钟) 大家将这些stick贴在墙上 引导者和团队一起过一遍所有的stick 归类相似的stick 引导者促进团队交流,讨论stick上的问题,并形成一些改进点(Action) Action一定要足够具体,并且需要一个所有者,所有者负责确保该Action一定会发生。比如团队发现上一个迭代中Code review做的不够好,一个Action就是每天下午5点有人来提醒大家来进行Code review。
如果这时候发现有太多的问题,团队可以用投票的方式选出本次Retro要讨论的数个stick。
引导会议 在日常工作中,我们几乎每天都有会议,而且越来越多的团队已经意识到冗长,无聊的会议有多大的杀伤力了。在很多会议上,与会者要么在刷新朋友圈,要么在对着笔记本电脑写代码或者读新闻,即使强制要求不许带电脑和手机的情况下,也无法限制参加者神游太虚。
根据《引导的秘诀》这本书里的定义,引导会议是
引导会议是一个高度结构化的会议,会议中的领导者(引导者)引导参会人通过预先设定好的步骤达成所有参会人产生,理解并接受的结果。 引导会议需要充分调用参与者的积极性,每个人都需要足够聚焦,这要求引导者可以有能力使得团队振奋(比如幽默的风格,或者具有挑战性的问题等)。另外,每个人的idea都需要被充分重视(一个细节就是不要随意篡改你听到的内容,这是没有经验的引导者常犯的错误之一)。一旦所有参与者都积极起来,引导者就可以稍微退后一些,将舞台交给团队。
而有时候,情景则相反,大家都不发言,也没有看到明显的发言的趋势,这时候需要一些方法来激励。如果是团队都比较茫然,引导者需要列出一些简单而容易理解的步骤,帮助团队按照预设的节奏来逐步前进。比如,在一开始的时候就将agenda板书在墙上,并通过头脑风暴的方式,鼓励参与者来将自己的idea可视化出来。
一个典型的误区是,引导会议的最后结论是本来就存在与引导者脑海中的想法。如果仅从结果来看,这种情况可能发生,但是只能说是碰巧而已。一个好的引导者需要帮助与会者自己产生,并得出一个可行的,被广泛认可的方案,而不是强加一个自己的给团队。
我们最为专业的引导活动是UX团队在客户现场的inception,inception由一系列相互关联,环环相扣的工作坊组成,这些工作坊基本上都需要采用很多引导的技巧,帮助客户团队将自己的问题描述清楚,并形成一个所有参与者都达成一直的可行方案。
如果你不知道如何开始一个引导会议,一个简单而通用的模式是:
我们的现状是 我们的目标是 我们如何到达目标 在行进中,如何度量 《引导的秘诀》里还提到了一种5P模式:目的(Purpose),产出(Product),与会人(Participant),可能的问题(Probable issues)以及流程(Process)。
5P提示你在准备会议之前,需要尝试回答这几个问题
为什么要开这次会议?主要目的是什么? 会议后的产出是什么? 谁需要参与会议? 在会议中,我们可能遇到什么问题? 遇到这些问题是,我们如何解决? 引导中的常用技巧 在引导活动中,有一些基本的规则,可以保证引导会议的顺畅性,比如...