作为JavaScript 开发者和架构师,必须承认并了解影响扩展性的因素。虽然不是所有JavaScript 应用都需要扩展,但总有一部分是需要的。比如,我们很难确认某个系统不需要扩展,不需要为它的可扩展性花费时间和精力。除非我们开发的系统不需要后期维护,否则总会有对增长和成功的预期。
从另一方面讲,JavaScript 应用并非天生成熟的可扩展应用,而是逐步积累、进化成的可扩展应用。对于JavaScript 开发人员来说, “可扩展性的影响因素”是一个有效的工具。我们不希望一开始就过度设计,更不希望被早期设计绑住手脚,限制了可扩展性。
对可扩展的需要
扩展软件是一种基于反应的活动。考虑可扩展性的影响因素可以帮助我们积极地做出准备。在应用后端等系统中,这种“扩展活动”通常是被自动处理的,可能是短暂的访问高峰。例如,激增的用户请求导致负载骤增,这时负载均衡器介入,将负载均匀地分派到后端服务器。在某些极端情况下,系统可能会在需要时自动准备新的后端资源来应对变化,当不再需要时将这些资源自动销毁。
但是前端不一样,前端的扩展活动通常发生的时间周期较长,而且更加复杂。JavaScript应用的独特一面在于,浏览器能获得的硬件资源就是它能使用的全部硬件资源,它从后端获取的数据可以很好地按比例增长,但这不是我们需要考虑的。随着软件的不断演进,我们要想成功做点什么,就必须关注“可扩展性的影响因素”。
上图自上而下地展示了可扩展性的影响因素。首先是用户提出软件需要实现的功能,接着功能尺寸、与其他功能的关系等因素会直接影响开发团队的构成,沿着箭头自上而下影响相应地增长。
不断增长的用户
如果构建的应用只服务于一个用户,就没有必要这么大费周章了。基于典型用户的需求来构建的应用将会为更多用户提供服务。所以在应用进化过程中,应该预见到用户的增长。尽管并没有确切的目标用户数量,不过,基于应用自身的特点,仍然可以使用http://www.alexa.com/这类工具作为基准,设定活跃用户数量的目标值。比如,如果我们的应用是任何人都可以访问的,就会希望有大量的注册用户;但如果仅针对个人安装,那么加入系统的用户数量的增长就会比较缓慢。但即使如此,我们还是希望部署数量不断增加,以提升软件的用户总量。
与前端界面交互的用户数量是扩展应用最大的影响因素。每增加一个用户都伴随着各种架构层面上指数级的增长。如果自上而下地看,用户决定一切。应用的存在终归是为了服务用户。JavaScript 代码越易于扩展,就越能取悦用户。
添加新功能
也许能够取悦用户的功能就是用户基数庞大的成功软件最显而易见的附带产物。软件的功能会随着用户数不断增长,尽管新功能显而易见,但还是经常被忽视。明明知道增加新功能不可避免,但我们还是很少思考如何合理地在代码中实现源源不断的新需求。正是缺少这样的思考,阻碍了我们继续发展。
这在软件交付初期非常棘手。软件开发商会竭尽全力吸引新的用户,但由于初期阶段能够吸引用户的功能有限,导致收效甚微。没有足够多的成熟特性,没有庞大的开发团队,也没有机会去打破用户习惯。当没有这些限制条件时,比较容易能够实现一些功能让已有或潜在用户感到眼花缭乱。但是我们如何才能在早期决策时迫使自己考虑周全?如何才能在提供更多功能的前提下确保没有限制我们扩展软件的能力?
你也许会发现,不管是开发新功能还是增强已有的功能,都是可扩展JavaScript 架构始终需要考虑的问题。我们需要考虑的不仅仅是软件推广文案中罗列的各种功能,还要考虑这些功能的复杂度、各个功能之间的共性以及各个功能有多少“移动部件(MovingParts)”。当自上而下审视JavaScript 架构时,如果用户是第一层级,那么各个功能就是下一个层级。从这个层级开始扩展变得纷繁复杂。
使功能变复杂的,并不是某一个单独用户,而是一群需要这个功能的用户。从这个角度讲,我们不得不思考使用软件的用户的特征或者角色,以及哪些功能提供给哪些角色。对这种组织结构的需求在一开始并不明显。直到后期,我们先前的决策使得引入基于角色的特性难以实施时,它才会显现出来。并且,这还取决于我们的软件是如何部署的,有时可能需要支持多种不同的用例。比如,可能几个大机构用户,都有各自的部署方案,并且很可能有各自独特的用户结构上的限制。这是十分具有挑战性的,如果希望做到可扩展,架构就需要支持这些组织结构迥然不同的需求。
雇佣更多的开发者
实现软件的各种功能需要可靠的JavaScript 开发人员,并且他们应该知道自己在做什么。能有一个这样的开发者团队是非常幸运的事情。团队组建不是自发的,在团队可以开发出优秀代码之前,需要在某种程度上建立起彼此之间的信任和尊重。一旦开始,我们就处于一个良好的状态。再看一下前面提到的自上而下的可扩展性影响因素,我们要开发的功能会直接影响团队的健康。这之间的平衡基本上是无法维持的,但是可以尽量接近。缺少人手但又有太多的功能要实现,这会让团队成员倍感压力。当如期交付毫无希望时,大家就不会努力尝试了。另一方面,如果开发人员过多,要开发的功能有限,就会带来更多的沟通负担,而定义职责又很困难,所以当大家对职责没有共识时,离失败就不远了。
相对于拥有太多的开发人员,开发人员不足反而更易于功能的开发。当面临巨大的功能开发压力时,是一个很好的时机来退后想一想:“如果我们有更多的开发者,会与现在有哪些不同呢?”这个问题经常被忽略掉,直接去招更多的开发者。而让大家惊讶的是,招聘到新人后功能的产出并没有立竿见影的效果。这就是为什么我们需要一个没有愚蠢问题、责任分配明确的研发文化。
团队组织结构和开发方法并没有定式,开发团队需要有针对性地处理开发中的情况,最大的问题无疑就是功能的数量、规模和复杂度。所以,这些才是我们在建立团队之初,以及团队成长过程用应该考虑的。后一点尤为重要,因为当功能大量增加后,初期的团队结构是无法适应的。
鉴于这些扩展影响因素会随着时间推移而改变,我们以架构的角度来调整设计或者修改产品,以应对扩展所面临的挑战。
若要进一步讨论这些影响扩展的各项因素,深入了解它们并准备一个核对清单,以帮助我们实现可扩展的JavaScript 应用来响应这些事件,可见《大型JavaScript应用最佳实践指南》一书。
本文选自《大型JavaScript应用最佳实践指南》,点此链接可在博文视点官网查看此书。
想及时获得更多精彩文章,可在微信中搜索“博文视点”或者扫描下方二维码并关注。