以一个开发的视角讲述一个阿里云产品从孵化到上线的都经历了些什么,哪些方面可以做得更好。

过程

需求讨论

一个项目的开始并不是一开始就有团队大部分角色和明确的需求,很可能连产品经理都没有,只有一个项目负责人带领两三个开发代表各个利益相关的团队把各自的需求搜集整理,通过一次又一次的会议沟通整理最终勾勒出产品的轮廓。

就像一个怀孕初期的婴儿连五官都还没有清晰,这个时候往往不知道谁是产品的 Stackholder,所有人包括项目负责人都有可能中途退出,有可能已经定下来的核心需求老板一句话就要作废,项目随时会因为预算、HC、其他更高优先级的需求流产;这个阶段最重要的是阐明产品的价值、找到愿意为项目埋单的老板,为产品争取争取尽可能多的资源,包括人力、时间,有了这些资源,项目不一定会成功,但是没有这些资源项目一定做不起来。

这个阶段最需要的几种能力:

  • 是向上管理,推动老板争取到资源
  • 很强的产品逻辑和领域知识,在不同团队利益冲突时你可以说话最大声,告诉他们这个领域的趋势是什么,头部产品是怎么做的,应该如何在需求中取舍
  • 过硬的技术能力和经验,和对已有架构的熟悉,往往是在考虑实现阶段体现出价值;熟悉已有架构可以从如何复用已有资源、如何集成的角度用投入产出比影响需求优先级

除了争取到资源,设计阶段还有一个重要产出,就是”产品抓手“,即围绕核心业务一个最常见最简单的 happy pass,为后续开发联调设置一个目标。

架构设计

有了大致的需求方向,和从老板那里争取到的人力,三两个开发就可以开始初始的架构设计了。这里的架构设计主要是:划分应用、表设计、接口设计。

在 DDD 语境下领域模型的识别是重要的一环,在阿里巴巴却只注重数据库表结构设计,对于这种现象的解释这里有一篇文章我认为说得很有道理:为啥国人偏爱Mybatis,而老外喜欢Hibernate/JPA呢?,我比较认同其中 Mybatis 更加拥抱变化的观点,但缺点是设计的时候太开放太灵活而失去了面向对象、领域驱动的优雅。不过事实证明,这样的开放的设计确实可以应对随后而来的各种意料之外的需求。产品是一边演进一边被定义的,一开始就想要做好领域设计并不容易,很可能一开始认为的核心业务也会随着项目变化而发生转移。不过这种模式也带来一个弊端,就是经过反复改造的应用生命周期不长,一个三年的应用一旦 Owner 离职或转岗则很有可能面临被重写的命运。也正是因为生命周期不长,代码质量和测试覆盖的受重视程度并不高。三年过后很有可能项目都不在了,有多少人会在意那个时候代码还是否能维护呢?

这个阶段需要的几种能力:

  • 对业务的透彻理解,有助于划分应用以及应用持久化数据的设计
  • 数据库基础知识,涉及到表之间的关联关系、索引如何建立

开发联调

我一直秉持一个观点,联调要越早越好,即使文档化的接口在不同团队不同开发实施出来也可能大相径庭,及早联调及早暴露问题也是风险管控的重要一环。

联调依赖的是需求讨论阶段的抓手的,大家都朝着快速实现这个目标前进才能确保方向没有偏差,实现小步快跑的第一步。

这个阶段最需要的是推动所有相关开发尽快跑通 happy pass,让联调尽可能早地发生,每次联调完之后确定待修复 bug 以及下一次联调需要验收的功能。后期的联调如果可能的话还得 involve 产品和设计,尽可能多地对上线前的产品做一些引导。

功能迭代

联调之后产品就可以上线第一个版本了,同步进行的是功能迭代。项目进行到这里 StackHolder 已经比较明确,也有了产品的介入,开发团队也比较稳定,大部分开发团队长期处于这个阶段。

这个阶段伴随着应用的演化,应用架构越来越完善,数据库结构变化越来越小,上线之后的应用由于一些数据不可能再改变而成为一个较为成熟的应用。

这个阶段需要的能力主要就是对框架、语言、以及设计模式比较熟悉,能够快速优雅的实现需求基于原有设计模式进行扩展,或者在实现新功能的时候预留出一些扩展。

这个阶段容易出现的问题是团队成员沟通不充分导致的重复造轮子,一条链路下原有的扩展点没有被利用而又新增扩展点;给应用应塞入一些不相干的功能,随着团队人员流动,这两种现象是应用生命缩短的杀手。最好的缓解方式就是做好新人 on boarding、文档沉淀,以及新方案落地前团队内部的技术方案对齐。

Bug 收敛,稳定性、容错性提升

功能完善之后有了运营的介入,这个时候会拉来一些种子用户进行”共创“,即公测前的试用,我们满足种子用户的大部分需求,用户帮我们发现大部分 bug。这个阶段发生在预期功能完成后的阶段,主要包含以下工作:

  • 监控预警:从业务层到应用层到基础设施逐级监控,目标在于在客户发现问题前发现问题:
    • 基础设施:服务器 CPU、内存、磁盘、负载均衡监控、
    • 应用层:RT (response time,主要为 nginx 日志)、端口心跳(最好由应用提供而不是 nginx)、Nginx 返回码监控、数据库连接池、JVM 监控、慢 SQL
    • 业务监控:失败成功率;可能涉及侵入代码的逻辑,再通过配置 logback 给 agent 搜集到业务监控服务。主要形式还是日志。
  • 日志追溯:主要是为了出现问题容易定位,有以下几个实践我认为是比较好的:
    • 一次不同应用之间由 traceId 串联,具体做法参考我的上一篇博客Spring boot 全局参数传递和追踪
    • 适当存储一些状态到数据库,配合日志查看帮助判断当前状态或是影响面
    • 还有一些更高级的基于 JVM 的追踪工具,比如 skywalking 用于查看调用链路和,极大提升了定位问题效率。
  • 安全性升级

这个阶段需要具备的能力偏向运维:

  • 从操作系统到网络层的基础知识
  • 安全方面的经验和知识
  • 各种DevOps工具

性能提升

有了完善的监控,我们对系统的整体表现有了一个比较好的掌握,就该针对性的做一些优化。以一个 RT issue 为例,当我们发现某接口长时间返回时间较长,首先通过 Skywalking 判断慢在哪个服务;第二部通过 Nginx 确定耗时任务是在应用内部(而不是DNS、网关或负载均衡器);第三步使用 Arthas(参见使用 Arthas 分析接口响应时间),定位到慢的地方(通常是 IO 操作,比如慢 SQL);第四步通过基础设施监控进一步判断问题,比如慢 SQL 是否真的存在,或者负载均衡配置需要优化;最后通过加索引或者优化配置的方式优化用户体验。再进行端到端验证问题是否已解决。

技术性的性能提升通常比较简单,比较复杂的往往是业务相关的性能提升,我们的服务是基于容器的,容器的调度、资源复用、配置成为了最有价值的优化点,这部分跟业务强依赖没太多共性,就不展开了。

感受

  • 稳定压倒一切。
  • 团队协作/资源协调,整个过程伴随着人员、利益相关团队流动。
  • 安全无处不在,每个环节都在体现安全。

反思

  • 每个阶段定好时间,定好验收内容

    无论是个人还是团队,如果只顾着忙于日常工作就容易走偏,或者陷入细节,最终达不到业绩目标。因此在开始一件事之前定好期望结果很重要,就跟TDD一样,团队开始规划前也要定好验收条件(也就是 KPI,通常是业务指标),一个好的节点验收计划可以帮助项目一步步达到理想的结果。

  • 文档真的很重要

    最近恰好看到一篇博客So You’re A New Grad Software Engineer,提到一个团队新人除了技术积累写好文档可能比写好代码更重要,非常赞同,从 commit message 到 README 到产品文档,写好文档除了帮助后来的人理解设计者意图也能帮助你成为某个知识领域的专家,从而让你被更多人找到问问题,不仅帮助了问问题的人也帮助了你自己。把软件开发想象成一个知识翻译的过程,文档是比代码更易于传递的知识中间态。

  • 贴近用户

    直面用户可能会很痛苦,因为你们不在同一个上下文,沟通起来会比较费劲。但耐心地去做这件事,并且坚持做,能更好地帮助你站在用户视角考虑问题,当一个用户带着问题通过工单,通过答疑,通过运营到达你面前,他面临的一定是一个对他而言痛到一定程度的痛点,这个痛点就是一次产品提升的机会。