架构师与SQALE

意面式代码

SQALE质量模型

Software Quality Assessment based on Lifecycle Expectations 目标管理软件中源代码的质量

如下对于过程质量和结果质量的分类并没有绝对的官方出处,更多是一种直觉的分类

过程质量

  • Maintainability
  • Changeability
  • Testability
  • Reusability

结果质量

  • Reliability
  • Effciency
  • Security

互联网中架构更喜欢讨论的是结果质量,的确有其挑战,但过程控制是结果控制的非取巧不二法门

SQALE度量内在质量

整合了ISO-25010标准与代码规范,目标是客观,准确,可复制和自动化的方式为评估软件应用程序的源代码提供支持,为管理技术负债提供一种有效的方法

Sonar code quality testing essential,戏称开发人员7宗罪

  • 编码规范:名字是最短的咒,各位法师常乱施法
  • 潜在的Bug:可能在最坏的情况下出问题的代码,及存在安全漏洞的代码
  • 文档和注释:过少,过多(亦对信息量也是冲销),过时
  • 重复代码:违反了Don’t Repeat Yourself 原则
  • 复杂度:结构复杂(如圈复杂度高),难以理解,测试,维护
  • 测试覆盖率:单元测试,尤其是针对复杂代码的覆盖是否足够
  • 设计与架构:是否高内聚,低耦合,依赖最少

从可测性,可读性,可理解性,容变性 等代码可维护的质量属性来衡量代码质量,代码质量不好最直接的受害者是开发者或组织本身

同样是代码质量问题,重复代码和注释过多危害肯定不一样,圈复杂度为10和20的方法相比,危害和修改难度差别也大,不能直接用问题的数量来衡量质量,需要更精细合理的量化度量方法,这时你考虑解决问题的人力投入耗时这个维度,根据不同的项目组是可以根据自己的生产力自定义配置(开发语言不同,默认值也不尽相同)

SonarQube,CoderGears,SQURE等商用代码扫描工具都将SQALE作为参照标准,但同时也在做自己的扬弃或简化

落地好架构的要求

  • 方向清晰:具有一定的前瞻性,了解技术的发展方向拥抱应用之;理解业务,最好领先业务半年(因为架构升级-- 朝向解决复杂问题–往往带来业务阵痛),这也要求架构师对业务理解和预判有更高的要求

  • 方案细致:除了宏观上层设计,还要关注关键细节,给多种方案选型予以综合评估

  • 方法实用:以解决问题为目标,不搞大而空

  • 多方利益均衡:康威定律(1967):软件架构无法超然跳脱组织架构

架构合理性目标

以高效、稳定、安全 三大目标衡量架构的合理性。

追求的是面向维护的设计;

KISS,不仅仅是逻辑简单,只有简单的体系,才能经得起进化

需求角度

  • 优雅可复用地解决当下业务问题
  • 前瞻设计:能在未来一段时间都优雅可复用满足业务

非需求角度

  • 稳定指标:可用性,包括各种测试覆盖(单元测试、自动化测试、黑百盒、故障注入等),都是为了这个目标
  • 高效
    • 文档化
    • 可扩展:软件设计秉承低耦合理念,在合理的地方抽象,方便功能更改,新增和运用技术的迭代
    • 高复用
  • 安全:数据安全

架构和架构师的价值

  • 通过保障系统质量属性来降本增效

  • 排首第一武器是抽象能力,接着还有分层,分治和演化

    • 《模型思考者》中提及模型是对现实世界问题的抽象,模型思考着需针对问题,看穿客观事务本质,选取或构建合适模型,推导出问题最优解
    • 架构师的职责和模型思考者类似,定义问题并识别方向,创建/选择/调整架构,找到最合适的方法,推进团队解决问题
  • 架构质量的优劣

    • 取决于系统在应对变化时所需投入的研发成本,隔离变化
    • 用Martin Fowler的话来说,重要任务之一:消除不可逆的决策
  • 高质量低成本直观看违背常理,但高质量=低成本

    通过业内在增量功能迭代和研发投入时间的增长曲线分析上发现,基于低质量的架构,在数周内就显现出研发效率的降低,而高质量架构要好得多

程序员最难任务排位

pie showdata
    title 程序员最难任务
    "命名" : 49
    "解释我的工作" : 15
    "工作量评估" : 10
    "团队协作" : 8
    "接手别人的代码" : 8
    "实现不认可的功能" : 3
    "写文档" : 2
    "写测试" : 2
    "设计方案" : 2

着眼长期成本

架构规范的价值是降低长期成本,涉及组织惯例和公共建设,文档,流程,文化

如何消解程序员的头疼问题?

  • 命名: 下定义是一种概念能力,抽象能力的磨炼,锻炼存在平时之中。如小说《阴阳师》中所言,名字是最短的咒。

  • 工作量评估:WBS,掌握工具

  • 别人的代码:代码规范

  • 写文档:常规环节,代码即文档

不同周期的发力方向

短期

代码开发,联调测试,部署

长期

页面变更,业务流程/规则变更,基础实施变更,依赖包升级,etc

榜样

Amazon的领域API标准化

  1. 所有团队的模块都要通过Service Interface方式将其数据和功能开放出来
  2. 团队间程序模块的信息通信,都需要通过这些接口
  3. 除开Service Interface的通信形式一概不允许:不能直接链结别的程序(如外部团队的动态库),不能直接读取其它团队的数据库,不能使用共享内存模式,不能使用别人模块的后门
  4. 所有的Service Interface,毫无例外,都必须从骨到皮设计成能对外开放的。即:团队必须做好规划,以便未来把接口开放给全世界的程序员
  5. 不这样做的人会被炒鱿鱼!

如此这般的的好处之一是:领域限界清晰,没有意外,你的团队控制着源代码和数据,在单一的限界上下文中工作

how amazon became a platform

工程师文化

质量

  • 它是一个高度概括的词汇,是一个结果。质量是工程师的reputation,不仅是在职场。
  • 换个土话,可以解释为“靠谱”
    • 事情做成离不开靠谱的人,聪明的人只能聊聊天
  • 工程师一直以做事为己任,视出品为生命,是我们对成事的渴望,对自己孜孜向上的推进

务实

  • 不消多说,工程师一直在解决问题的路上,眉眼为问题而动,养成的自然是以解决问题为目标,不搞假大空的作风
  • 但在评估需求时,存在普遍乐观主义的倾向。本质是因为需求信息不够详尽。
    • 慢思考模式是惰性的,通过经验可训练强化自己的评估模式

效率

  • 效率就是生命,减少中断进入心流状态,充分将人脑效能提速起来。
  • 抓重点:做好数据和用户分析
  • 自动化:更多的自动化,更少的多人协作
  • 降成本:去除支持性工作,提高组件质量和重用,持续改善组织文化
  • 良好的分层分治设计,也是我们应对效率怪兽的解药

革新

在小革新的积累下,也在涌动大的革新,以解决结构性问题/难题,旧的世界的打破需要技术突破,技术突破有些时候是跳跃性的,但离不开原来渐近式的铺垫

  • 跳跃性解决结构性难题
  • 渐进性解决日常问题

角色划分

现在架构师划分的角色也很细,顾名思义,数据架构师,(运维)云架构师,安全架构师这些都比较容易让人好理解其定位。还有几个不那么容易概念区分的架构师角色,特在此记录说明一下。

  • 企业架构师:企业架构师的职责包括协助创建和执行信息技术架构路线图,创建支持业务目标的企业基础设施。
    • 分析技术架构领域的当前趋势并提供建议
    • 评估应用程序是否符合企业标准和业务标准
    • 确定与组织变更相关的架构的生存能力
    • 就治理模型和框架等领域的最佳实践对技术人员进行培训
  • 业务架构师(解决方案架构师):专门评估业务需求,并将它们转换为解决方案、产品或服务。通常花费大部分时间来协调正在进行的活动,从概念定义到需求的分析和实现,最后转移到IT操作。
    • 在设计和构建阶段管理应用程序开发团队
    • 为初级员工提供培训和指导
    • 与应用程序开发人员协作以实现业务目标
  • 领域架构师–应用架构师:领域架构师是在其专业领域具有深入知识的专家。数据架构师,安全架构师也属于领域架构师

何为工作能力?

不局限于架构师角色

工作能力即为 将大目标分解为可执行的小任务的能力,任务是实现结果的过程

知无涯和能力发展

各种岗位都是要不断构建自己的知识体系,将经验沉淀总结为方法,这方面贯穿着宏观思维能力,抽象思维能力和钻研能力

  • 宏观思维就是考虑整体,站在更高层次上看问题,需要有意的跳出细节看整体,找到关键重点再深入细节
  • 软件项目永远不变的就是变化,抽象是应对变化的主要手段,要求看得更远,将现有和未来可能的业务场景综合起来进行分析,做出相对稳定的抽象,预留架构的扩展性
  • 钻研能力要求具备深入细节的能力,攻坚克难

专业的方向可以有选择性,并不是所有技术都要深入到细节层面

  • 用源码学习很多时候并不是一个特别高效的学习过程,API学习就是一种挺高效的途径
  • AWS的API,J2EE的API,这些吵架吵出来的API设计很漂亮,通过API学习你可以把它的领域想透了,不信你自己写一个,一比较很容易是这也忘了(不对),那个也忘了(不对)

还有一个误区

都喜欢钻研计算机科学知识,唯“底层”为王,深入钻研细节要权衡好ROI,对于业务部门技术人员而言,在软件设计方法软件工程的专业知识实际上也是应花力气钻研的,这些基础知识同样是该领域的底层

技术债(TechnicalDebts)

概念最早出现在1992年,本义指开发人员为加速软件开发,在应采用最佳方案时做了妥协,改用短期能加速开发的方案,从而在未来给自己带来额外开发负担。

本金 : 定义为修复代码质量所需消耗的人力资源估值

例如针对java而言,修复一个圈复杂度为15的方法需一个开发人员15分钟的时间

通常借钱是有利息的,技术债也一样,存在利滚利的情况。有些违规项马上修复要10分钟,若放一段时间后,也许就需要20分钟甚至更多时间来修复(代码细节的知识随时间流逝,及破窗效应造成代码问题加速恶化等原因),因为利息的存在,不及时偿还的话,会在未来呈现非线性增长,造成始料不及的损失

负债率:偿还债务所需耗费的资源,除以重写所有代码预估耗费的资源。在扫描工具的实现中,分母是通过代码量和开发生产力水平计算得出,其中生产力是一个配置项。

技术债的排序

技术债务对每个问题设置了规则等级,面对那么多技术债务,如何按优先级排序你的工作呢?

且看技术债务金字塔

  • Reusability
  • Portability
  • Maintainability(非结构化语句goto,switch外的break,文件注释率<35%,循环语句中使用continue,文件行数>1000, 不一致的缩进,变量大小写风格)
  • Security
  • Effciency(残留吴用的变量,类继承太深>7)
  • Changeability(滥用public,类复杂性>100)
  • Reliability(未初始化变量使用,条件循环语句中使用赋值符号)
  • Testability(方法参数列表庞大>6, 需独立测试路径过多 > 11)

这个金字塔仅是一个比喻,自底向上的看,每个特性建立在它下面的基础上,可测试性是底部,因为它是最重要的,首先要确保你的应用程序是可测试的,接着确保它的可靠性,可变性,有效性等等

  • maintainability及以上,更偏长期债务(通常在转移维护团队时才得以察觉显现)

  • 二者间可视作中期债务

  • Reliability及以下,更偏短期债务

  • Security和Reliability属于关键债务,按比喻来说是债务利息中最高的部分,多见逻辑错误或异常管理不善等问题,直接影响业务,而其它问题有时并不会直接影响业务。

技术债规避举例

某组织挑拣的4条规则

  • Source files should not have any duplicated blocks
  • Classes should not becoupled to too many other classes
  • Methods should not be too complex
  • Control flow statements if for while switch and try should not be nested too deeply

个人看法这几条规则,有三条都跟复杂度有关,抓本质的能力有待提升

代码是资产也是债务

Joshua Bloch(布洛赫):java.math.assertions、 Collections Framework、《Effective Java》作者

Java架构师 Joshua Bloch曾说:随着年龄增长,其意识到 编程不仅仅是让程序运行而已,它是创造一个利于理解,可维护的,高效的作品。一般来说,干净整洁的代码即使运行不快,也可以很容易让它变快。让正确的程序更快,要比让快速的程序正确容易得多。

  • 最小化人们理解它所需要的时间是比最小化代码行数更好的度量方法
  • 别为了获得一丁点 ”性能“ 就牺牲掉整洁,一流的编译器优化通常会自动把短代码转换成内联函数,在现代计算机上,函数调用的代价接近于0
  • 霰弹式修改,是个坏味道

圈复杂度

构造软件有两种方法:一种是简单,明显地没有缺陷,另一种是使其复杂,却没有明显的缺陷。 — 图灵奖得主 tony hoare(霍尔)

有研究表明,圈复杂度大于10的方法存在很大的出错风险

10以下圈复杂度的方法,有bug修复不成功率在5%,20~30的复杂度,不成功率就来到了20%,大于50,修复不成功率来到40%,接近100的复杂度,有60%的几率出现修复失败

应该将复杂的计算分解成具有内聚性的步骤,令大尺度的运算更具可读性

性能 != 效率

效率意味着最短路径,性能意味着以跑代走。就算你用博尔特的速度,虽然高性能,但可能没选择最优路径

影响程序运行时间的一个重要原因是内存中的数据的布局和结构,布局不优良,会造成要花费很多时间获取数据,同时会造成指令冗余

金玉良言

最好维护的代码就是没有代码,好的程序员代码产量是负的,他们通过减少代码来增加功能

回顾收尾

好的代码就像好的笑话一样,它不需要解释(Good code is like a good joke:it needs no explanation)。

有编码经验的人对代码有一定的鉴赏力,能凭感觉给出代码好坏的主观评价,这方面的反例就是意大利面条式代码(spaghetti [ spəˈgeti ] code)

意面式代码:非结构化和难以维护的源代码的代称,有一个复杂和纠结的控制结构,程序流在概念上有如意大利面,扭曲和纠结。

国内圈有更震撼的翻译:屎山代码

意面/屎山代码产生的原因

由于程序员在开发过程中缺乏良好的设计和规划:从而导致逻辑混乱、耦合度高、难以理解和修改,给后续开发人员带来了极大的成本。

不仅局限于代码难读,环境差异,废弃的代码,过时/临时的逻辑,依赖和组件引入及锁片,都在助力屎山代码的形成。

公认的幽默标准 : 每分钟爆粗数量,强调了代码的可读易懂有多么感人和基础

重构~精进

Refactor,Be Better

当已有代码存在问题时,程序员应主动进行重构,优化代码结构和逻辑(对一个东西最好的批评,是做出新东西替代它)

精神误区

得过且过、无能为力、推到重来

面对现实的挑战和过去的包袱,不能站出来的成不了架构师,没有勇气承担的成不了架构师

动手解决

  • 理解不干净背后的东西,勇于承担、拒绝破窗、养成日常清洁(边打扫边生活)的习惯

  • 和产品达成共识,解决“大石头”的同时干掉“小石头”,慢即是快

  • 要干净的不仅仅是代码,善于用减法的思路去对待产品

  • 使用工具,提升效率。君子性非异也,善假于物也。