Teaser Image

mindwind

十日画一水,五日画一石




「从粗放式的程序小子到精益化的编程大师。」

最近一直在思考,怎么算是有效地编程,个人该怎么做,一个开发团队又该如何? 这里的前提是假定需求的有效性,不考虑需求的无效导致的无谓的编程。 需求的有效性这是另外一个主题了,不在本文讨论。

我所在的开发团队,近三年前大部分都是刚毕业的学生,整个团队的开发风格就是粗放式的。 这里提到的 “粗放式” 是如何定义的,软件开发这个行业比较年轻,没有确切的定义,但在传统行业里提到的 “粗放经营” 可以用作类比,引用百度词条的定义如下:

粗放经营(Extensive Management),泛指技术和管理水平不高,生产要素利用效率低,产品粗制滥造, 物质和劳动消耗高的生产经营方式。

把上面这段话里面的经营二字改成编程,就很精确地表达我想说的粗放式编程的含义。

三年前大概就是上面说的粗放式编程的样子,那么三年后了现在是什么样呢?悲剧,还是粗放式的。 这不禁引发我思考粗放式编程的下一阶段是什么,路在何方? 然后就想到 “粗放” 的反义词 “精益”,然后又从传统行业里找到了关于 “精益生产” 的定义:

精益生产,简言之,就是一种以满足用户需求为目标、力求降低成本、提高产品的质量、不断创新的资源节约型的生产方式。

对的,把上面的生产换成编程就是我想要的 “精益编程”。感觉自己灵光一现找到了道路,看见了黎明的曙光。 再一查原来精益软件开发的思想早在 2003 年初就被 Mary Poppendieck 提出,并还写了本书专门来阐述 (Lean Software Development: An Agile Toolkit)。 好吧,2003 年我还在学校刚开始学习编程呢,自然很难切身体会精益编程的思想,不过一路走来最后发现英雄所见略同, 也就更有信心了,这真是黎明的曙光,不是海市蜃楼。

这下找到了方向,就是从粗放式向精益化的转变,那么这两者之间是否存在一个边界,就像历史上从东德到西德, 只要翻过了柏林墙世界就大变样了?我是否就是要找到这堵柏林墙把它推倒然后就从粗放式转变到了精益化? 仔细这么一思考,还真没这么明显的一堵墙。

拿着放大镜看看粗放式的编程是怎么的干活,一个需求到达开发后,开搞,搞完,上线,上完客户说 O 了,然后就没有然后了。 上面这个过程重复 500 遍,然后我们得到了一个提供 500 个不同业务服务的大型应用,然后问题来了。 加新功能开始变慢,再加第 501 个需求时,前面好几个功能突然就不正常了,代码量激增,维护困难。 这大概就是粗放式编程的第一阶段。

然后开始拆分业务功能,按小功能集打包成服务,服务独立部署,降低开发耦合付出的代价是提高了部署和运维的复杂度和难度。 新名词诞生了,面向服务架构(SOA)或者更新的微服务架构。 粗放式编程进入了第二阶段。

上面我们使用了最新最潮的服务架构,为什么还是粗放式的?因为我们还仅仅是在关注业务功能需求, 拆分服务也仅仅是降低了业务的耦合度,理顺业务服务之间的关系。 业务代码的开发是冰山露在海面上的部分,而海面下则是非功能性需求的支持,包括:可维护性、可扩展性、性能等。 今天受益于开源代码的流行,很多基础库代码的复用节省了大量的冰山下的开发维护成本, 但针对上面说的三个方面依然与业务代码开发息息相关。只是在粗放式的早期阶段我们能完成好功能已经不错, 回想下刚毕业时能让自己开发的系统顺利跑起来,完成客户的需求已经很开心了。

服务拆分是从整体上提升了系统的可维护性、扩展性和性能。但落在每个单个服务上,这三点取决于该服务的开发人员 的经验和水平,该服务的开发人员若还属于粗放式第一阶的话,可能就考虑不了那么多了。 精益编程的原则能帮助您通过迭代趋向完美:将软件开发看成一个不断探索的过程。 就整体系统而言,每轮迭代可能关注的重点不同,粗放阶段停留在完成功能,而进入精益阶段则关注非功能属性, 其目的是最低成本,最高质量的提供服务,最大化客户价值。

系统架构师关注系统服务整体拆分和部署的合理性,统观全局,这是关注整体系统的非功能属性。 架构师还需要关注每个核心服务的关键细节,以避免瓶颈效应影响整体系统的表现。 而每个服务的开发者则更需要具体关注服务的非功能属性,选择优化的实现方式。 这样算是迈入了精益化编程阶段。

具体开发服务时该如何关注非功能属性,应该没有统一方式,这里就我个人经验理解简单说说。

可维护性

  1. 单一服务提供的功能简单,职责唯一。
    简单更易维护。
  2. 服务之间功能正交。
    正交减少重复。
  3. 考虑新版本服务替换旧版服务时的并行性。
    意味着多考虑数据结构的稳定性,Linus 说的好程序员关注数据结构是有道理的。
  4. 提供不过时的文档说明。
    像维护代码一样维护文档,像写代码一样写文档,注重明确、清晰。
  5. 考虑可读性,命名,排版,一致性。
    像写文档一样写代码,注重可理解。

可扩展性

  1. 无状态更优。
    易于水平扩展,随时启停。
  2. 最小化共享资源的依赖。
    能不依赖更好,扩展到一定阶段的瓶颈可能就是共享资源。
  3. 基础服务多考虑提供 SPI 扩展接口,业务服务看情况。
    业务服务通常通过替换升级,而基础服务经常通过 SPI 扩展升级。

性能

  1. I/O 是英镑,内存是 RMB。
    访问 I/O 花的是英镑,访问内存花的是 RMB,能不花就不花,一次内存访问都是好几百个 CPU 时钟周期,要像花钱一样心痛。
  2. 碰到性能问题先从自己的代码上找原因。
    Java 程序员说起性能调优,总喜欢谈 JVM 调优,其实大部分性能问题都不是 JVM 的。
  3. 优化的前提是当前性能不能满足需求,提供性能数据证明。
    服务除了提供功能,还要性能指标说明,支持的 TPS,单次调用时长等,优化后也要提供数据对比。
  4. 性能回归自动测试,避免服务升级性能退化。
    其实现在能做好功能回归的都不多,性能花的功夫更少,继续努力。
  5. 更少的代码,更好地性能。