极客时间已完结课程限时免费阅读

08 | 代码与分类:工业级编程的代码分类与特征

08 | 代码与分类:工业级编程的代码分类与特征-极客时间

08 | 代码与分类:工业级编程的代码分类与特征

讲述:刘飞

时长08:32大小3.91M

编程,就是写代码,那么在真实的行业项目中你编写的这些代码可以如何分类呢?回顾我曾经写过的各种系统代码,按代码的作用,大概都可以分为如下三类:
功能
控制
运维
如果你想提高编程水平,写出优雅的代码,那么就必须要清晰地认识清楚这三类代码。

一、功能

功能代码,是实现需求的业务逻辑代码,反映真实业务场景,包含大量领域知识。
一个程序软件系统,拥有完备的功能性代码仅是基本要求。因为业务逻辑的复杂度决定了功能性代码的复杂度,所以要把功能代码写好,最难的不是编码本身,而是搞清楚功能背后的需求并得到正确的理解。之后的编码活动,就仅是一个“翻译”工作了:把需求“翻译”为代码。
当然,“翻译” 也有自己独有的技术和积累,并不简单。而且 “翻译” 的第一步要求是 “忠于原文”,也即真正地理解并满足用户的原始需求。可这个第一步的要求实现起来就很困难。
为什么搞清楚用户需求很困难?因为从用户心里想要的,到他最后得到的之间有一条长长的链条,如下所示:
用户心理诉求 -> 用户表达需求 -> 产品定义需求 -> 开发实现 -> 测试验证 -> 上线发布 -> 用户验收
需求信息源自用户的内心,然后通过表达显性地在这个链条上传递,最终固化成了代码,以程序系统的形态反馈给了用户。
但信息在这个链条中的每个环节都可能会出现偏差与丢失,即使最终整个链条上的各个角色都貌似达成了一致,完成了系统开发、测试和发布,但最终也可能发现用户的心理诉求要么表达错了,要么被理解错了。
因为我近些年一直在做即时通讯产品(IM),所以在这儿我就以微信这样一个国民级的大家都熟悉的即时通讯产品为样本,举个例子。
微信里有个功能叫:消息删除。你该如何理解这个功能背后的用户心理诉求呢?用户进行删除操作的期待和反馈又是什么呢?从用户发消息的角度,我理解其删除消息可能的诉求有如下几种:
消息发错了,不想对方收到。
消息发了后,不想留下发过的痕迹,但期望对方收到。
消息已发了,对于已经收到的用户就算了,未收到的最好就别收到了,控制其传播范围。
对于第一点,微信提供了两分钟内撤回的功能;而第二点,微信提供的删除功能正好满足;第三点,微信并没有满足。我觉着第三点其实是一个伪需求,它其实是第一点不能被满足情况下用户的一种妥协。
用户经常会把他们的需要,表达成对你的行为的要求,也就是说不真正告诉你要什么,而是告诉你要做什么。所以你才需要对被要求开发的功能进行更深入的思考。有时,即使是日常高频使用的产品背后的需求,你也未必能很好地理解清楚,而更多的业务系统其实离你的生活更远,努力去理解业务及其背后用户的真实需求,才是写好功能代码的基本能力。
程序存在的意义就在于实现功能,满足需求。而一直以来我们习惯于把完成客户需求作为程序开发的主要任务,当功能实现了便感觉已经完成了开发,但这仅仅是第一步。

二、控制

控制代码,是控制业务功能逻辑代码执行的代码,即业务逻辑的执行策略。
编程领域熟悉的各类设计模式,都是在讲关于控制代码的逻辑。而如今,很多这些常用的设计模式基本都被各类开源框架固化了进去。比如,在 Java 中,Spring 框架提供的控制反转(IoC)、依赖注入(DI)就固化了工厂模式。
通用控制型代码由各种开源框架来提供,程序员就被解放出来专注写好功能业务逻辑。而现今分布式领域流行的微服务架构,各种架构模式和最佳实践也开始出现在各类开源组件中。比如微服务架构模式下关注的控制领域,包括:通信、负载、限流、隔离、熔断、异步、并行、重试、降级。
以上每个领域都有相应的开源组件代码解决方案,而进一步将控制和功能分离的 “服务网格(Service Mesh)” 架构模式则做到了极致,控制和功能代码甚至运行在了不同的进程中。
控制代码,都是与业务功能逻辑不直接相关的,但它们和程序运行的性能、稳定性、可用性直接相关。提供一项服务,功能代码满足了服务的功能需求,而控制代码则保障了服务的稳定可靠。
有了控制和功能代码,程序系统终于能正常且稳定可靠地运行了,但难保不出现异常,这时最后一类 “运维” 型代码便要登场了。

三、运维

运维代码,就是方便程序检测、诊断和运行时处理的代码。它们的存在,才让系统具备了真正工业级的可运维性。
最常见的检测诊断性代码,应该就是日志了,打日志太过简单,因此我们通常也就疏于考虑。其实即使是打日志也需要有意识的设计,评估到底应该输出多少日志,在什么位置输出日志,以及输出什么级别的日志。
检测诊断代码有一个终极目标,就是让程序系统完成运行时的自检诊断。这是完美的理想状态,却很难在现实中完全做到。
因为它不仅仅受限于技术实现水平,也与实现的成本和效益比有关。所以,我们可以退而求其次,至少在系统异常时可以具备主动运行状态汇报能力,由开发和运维人员来完成诊断分析,这也是我们常见的各类系统或终端软件提供的机制。
在现实中,检测诊断类代码经常不是一开始就主动设计的。但生产环境上的程序系统可能会偶然出现异常或故障,而因为一开始缺乏检测诊断代码输出,所以很难找到真实的故障原因。现实就这样一步一步逼着你去找到真实原因,于是检测诊断代码就这么被一次又一次地追问为什么而逐渐完善起来了。
但如果一开始你就进行有意识地检测诊断设计,后面就会得到更优雅的实现。有一种编程模式:面向切面编程(AOP),通过早期的有意设计,可以把相当范围的检测诊断代码放入切面之中,和功能、控制代码分离,保持优雅的边界与距离。
而对于特定的编程语言平台,比如 Java 平台,有字节码增强相关的技术,可以完全干净地把这类检测诊断代码和功能、控制代码彻底分离。
运维类代码的另一种类,是方便在运行时,对系统行为进行改变的代码。通常这一类代码提供方便运维操作的 API 服务,甚至还会有专门针对运维提供的服务和应用,例如:备份与恢复数据、实时流量调度等。
功能、控制、运维,三类代码,在现实的开发场景中优先级这样依次排序。有时你可能仅仅完成了第一类功能代码就迫于各种压力上线发布了,但你要在内心谨记,少了后两类代码,将来都会是负债,甚至是灾难。而一个满足工业级强度的程序系统,这三类代码,一个也不能少。
而对三类代码的设计和实现,越是优雅的程序,这三类代码在程序实现中就越是能看出明显的边界。为什么需要边界?因为,“码以类聚,人以群分”。功能代码易变化,控制代码固复杂,运维代码偏繁琐,这三类不同的代码,不仅特征不同,而且编写它们的人(程序员)也可能分属不同群组,有足够的边界与距离才能避免耦合与混乱。
而在程序这个理性世界中,优雅有时就是边界与距离。
分享给需要的人,Ta购买本课程,你将得20
生成海报并分享

赞 18

提建议

上一篇
07 | 多维与视图:系统设计的思考维度与展现视图
下一篇
09 | 粗放与精益:编程的两种思路与方式
unpreview
 写留言

精选留言(27)

  • WolvesLeader
    2018-08-23
    怎么判断一个代码的好坏,我总是不知道自己写的代码是不好还是好

    作者回复: 那就看看一些标准库或开源代码,找找好的感觉

    13
  • 艾尔欧唯伊
    2018-08-20
    感觉运维代码是最复杂,但是前期是最看不到收益的的。。。 但是却很重要,特别是项目规模上去之后。。。

    作者回复: 很多时候,运维类代码都成了技术债

    12
  • third
    2018-08-22
    突然想起了,福特曾经说过的一句话,如果我们去问顾客想要什么,他们会告诉我们想要一辆更好的马车,而不是汽车。

    作者回复: 😄,哈哈,经典的福特之问

    11
  • 2018-08-20
    恩,讲的很好!一门语言学会后,编程的困难在于对业务逻辑的理解,尤其对于业务背景和整体流程的理解,决定了业务抽象的层次的高低,和看问题的深浅程度! 大厂应该都有基础架构平台,各种维护性代码应该比较容易加,让业务工程师专注于业务。

    作者回复: 恩;业务大了后,都会工具化和平台化,才会有规模效应

    6
  • 阿信
    2019-03-10
    总结: 对于最终需要交付运行的程序代码, 从代码的用途(or使用场景)上将作者代码分为三种:功能代码、控制代码、运维代码。 功能代码,满足于用户需求,为实现特定的业务功能而开发。 控制代码,对代码执行流的控制,如并行、异步、限流、熔断、超时控制等。RPC、中间件、代理服务器等,应该是对代码执行流的控制。控制代码的需求,是从众多(已有的或预见的)功能开发中提炼而来,基于一些共性的特征抽取。源于功能而高于功能。 运维代码,用于解决运行过程中出现的问题,或者为解决问题提供必要的信息。 结合工作,我们做的防重组件,是控制代码。基于pinpoint实现链路跟踪,是对业界已有的运维代码成果的使用
    展开

    作者回复: 嗯,理解很对

    4
  • 2018-08-20
    项目时间太紧的一个后遗症就是deadline 只能保证功能代码调通。运维代码缺失,大量fixme:有空时再补日志,有空时再补错误码,有空时再补国际化,有空时……

    作者回复: 有空时,就忘了……😂

    4
  • 吴封斌
    2019-04-18
    第二次拜读这篇文章,但对于控制,和运维还不是很明白😭,作者可以在形象化点说明吗?😊

    作者回复: 那可能是你还没怎么写过类似的代码,所以觉得抽象,不着急,多做些事情,多写点代码,会恍然的😄

    2
  • 小伟
    2019-03-10
    控制不仅仅是业务逻辑代码的控制,还包括性能、容错等方面的控制。而且业务代码里也包含了逻辑步骤的控制,界定控制的边界是关键。赞同老师最后一句的观点,但落地需要一些好的方法论,后面继续学习。
    2
  • 予悠悠
    2019-01-26
    没有完全理解控制代码究竟是哪一类代码。胡老师可以举更多的例子解释吗?

    作者回复: 比如并发,互斥,流控,隔离等控制代码执行的代码

    3
  • liangjf
    2018-09-06
    刚出来工作的我只想尽快熟悉各个业务功能,然后串起来,理清整个框架

    作者回复: 恩,刚开始都是这样的

    2
  • like_jun
    2018-08-29
    一直纠结这个。终于看到了答案。

    作者回复: ^_^

    2
  • Michael
    2018-08-21
    于现阶段的我而言 我觉得更难的是针对业务逻辑的抽象和实现 把业务逻辑的实现和业务相对应 和PM脑子里的概念相对应 让后来的人能看代码就能知道业务的所有规则 清晰明确的掌握业务 这个是最难的

    作者回复: 所有的代码都需要一定程度的抽象思维

    2
  • Hank_Yan
    2020-12-18
    讲到心底里面了。去年就买了专栏,看了几章没什么感觉。经过一年的成长,再回过头看老师的文章,简直字字珠玑,不舍得跳过一段话,哈哈哈
    2
  • 今之人兮
    2019-12-04
    拐回来又看一遍,还是受益良多。 平常很多时候都是写完功能代码就急着被催上线,现在更会注意一些三码齐全。希望coding越来越好
    1
  • 杨城
    2018-09-11
    老师对代码分类的很清晰👍 接触到的大部分程序员都希望更多的编写高技术含量的控制代码,会觉得写业务代码比较枯燥没啥技术含量,而且每个公司业务并不通用,请问老师我们平时学习中应该怎么平衡在学习两者上投入的时间精力呢?

    作者回复: 不用太刻意去平衡,哪里的需求更强烈就投入到哪。业务和技术是价值链上的两个环节,两个环比一个环更有价值

    2
  • crysSuenny
    2018-09-05
    "因为从用户心里想要的,到他最后得到的之间有一条长长的链条 极客时间版权所有: https://time.geekbang.org/column/article/13626 ",看到这一点,您遇到过用户具备甲方攻城狮的情形么;是不是若产品经理不打算偷懒的话,甲方攻城狮那边其实没有技术职责?从技术上来讲,形同虚设是不是噢。。
    1
  • 登高
    2018-08-21
    最近的编程中这三个方面都体验到,如何分离不太懂
    1
  • monkeyking
    2018-08-20
    如何让开发正确理解产品的需求有哪些好的方式呢
    1
  • 米哈游牛浚亲
    2021-05-05
    总结: 程序代码大致上可以被分为三大类:功能,控制和运维代码。 功能代码: 功能代码是指实现基本需求的代码,是具体的业务逻辑;程序员在编写功能代码的时候更多的从事的,是一种翻译操作,将用户需求“翻译”成为代码和最终上线的功能,而在这个过程中,最难的不是编写代码的部分,而是理解用户真正的需求。用户的需求从最原始的心理需求到最后验收的功能模块之间存在一个长长的链路,这个链路中每一步都可能出现信息的偏差和缺失,这样导致最终交付的代码和用户最初心里期望的可能会大相径庭。 而对于业务功能模块代码,不应只着重于业务代码的编写,而是应该做到真正地理解并满足用户的原始需求。甚至后者对于程序员来说可能占有更高的比重。 控制代码: 功能模块是实现业务逻辑的代码,而控制代码则是决定了代码的执行策略,决定了程序的流向。在现代工业级代码中,控制的相关代码往往都被封装在了框架的底层,使得程序员被解放出来可以专注在业务功能代码上。控制模块在抽象的层面上具有一定的相似性,是因为各个框架都会最终偏向于当下时间节点对应模式框架的最佳实践方案。比如微服务架构模式下关注的控制领域,包括:通信、负载、限流、隔离、熔断、异步、并行、重试、降级。 控制代码都是与业务功能逻辑不直接相关的,但它们和程序运行的性能、稳定性、可用性直接相关。提供一项服务,功能代码满足了服务的功能需求,而控制代码则保障了服务的稳定可靠。 运维代码: 运维代码是实现了开发和运维人员对系统及模块后期维护的相关功能,让程序具备了工业级别上的可维护性。一个简单的例子就是日志系统的设计。 运维代码的一个种类是对于异常的处理,这类运维代码的最终目标是能够让系统实现运行时的自我诊断与修复。但这样的功能在实现上存在着一系列的困难。在现实中往往退而求其次,要求系统至少具有针对错误的准确定位与上报(工业级运维代码的最基本诉求)。 运维代码的另外一个种类则是对系统在运行时进行改变的种类,通常这一类代码提供方便运维操作的 API 服务,甚至还会有专门针对运维提供的服务和应用,例如:备份与恢复数据、实时流量调度等。 对于 功能、控制、运维 三种类型的代码,需要依次的实现,在现实中可能存在某些情况,在完成功能模块后就迫于压力上线。需要记住的是,缺少了后面两类的任意一种,都是技术负债(坑),甚至说可能是灾难。 对于整体的设计,秉承一个高内聚,低耦合的设计理念;对于不同类型的代码,需要有明确的界限;因为三种类型代码具有各自的特点:功能代码易变化,控制代码固复杂,运维代码偏繁琐,有足够的边界与距离才能避免耦合与混乱。 对于上诉思想,可以由AOP的编程模式来快速实现,通过早期的有意设计,可以把相当范围的检测诊断代码放入切面之中,和功能、控制代码分离,保持优雅的边界与距离。
    展开
  • Sch0ng
    2021-02-25
    此文的意义在于把我们日常接触的代码从功能的角度做出划分,而且划分符合MECE法则(相互独立,完全穷尽)。对于代码,在做任务分解的时候,你打开了一扇新世界的大门,知道可以分为功能代码、逻辑代码和运维代码。帮助你化整为零,分而治之,逐个击破。