代码精进之路
2022-03-09 09:59:02
代码的要求
举报
猜你喜欢
大纲/内容
用户的真实感受。用户的感受是我们软件开发最基本的风向标,也是代码性能追求的终极目标。
用户的真实感受
满意:如果任务的响应时间小于 T,用户感觉不到明显的阻碍,就会比较满意;容忍:如果任务的响应时间大于 T,但是小于 F,用户能感觉到性能障碍,但是能够忍受,愿意等待任务的完成;挫败:如果任务的响应时间大于 F 或者失败,用户就不会接受这样的等待。挫败感会导致用户放弃该任务。
最佳等待时间(T)和最大可容忍等待时间(F)的选择有着非常经典的经验值,最佳等待时间是 2 秒以内,最大可容忍等待时间是最佳等待时间的 4 倍,也就是 8 秒以内。
Apdex = (1 × 满意样本数 + 0.5 × 容忍样本数 + 0 × 挫败样本数) / 样本总数
等待时间要短
一致性原则是一个非常基本的产品设计原则,同样也适用于性能的设计和体验。
一个服务能够提供一致的性能体验,拿到 90 分甚至 95 分以上的好成绩,其实有很多挑战。但正是这些挑战,让优秀的程序员和优秀的产品脱颖而出。
体验要一致
管理好计算机资源主要包括两个方面,一个方面是把有限的资源使用得更有效率,另一个方面是能够使用好更多的资源。
资源最大化指的就是完成同一件事情,尽量使用最少的计算机资源,特别是使用最少的内存、最少的 CPU 以及最少的网络带宽。
通过使用更多的内存,来提高 CPU 的使用效率;或者通过使用更多的 CPU,来减少网络带宽的使用;再或者,通过使用客户端的计算能力,来减轻服务端的计算压力。
把资源使用得更有效率
能够使用好更多的资源
算法的复杂程度
代码的资源消耗
16、什么样的代码才是高效的代码?
频繁的需求变更确实让人抓狂。它变更的可不仅仅只是需求,还有不断重构的代码,不断延长的工期,不断增长的投入,以及越来越多的加班。
避免需求膨胀
要从小做起,最重要的就是选择。什么是必须做的?什么是现在就要做的?这是我们做选择时,要时刻准备提出和回答的两个问题。
首先就必须满足的需求,是优先级最高的、最重要的事情,这些事情要小而精致,是我们的时间、金钱、智力投入效率最高的地方,也是回报最丰厚的地方。
识别最核心需求
有一些需求很重要,但不是现在就必须做的。这就需要另外一个方法——迭代演进。第一次我们没有办法完成的事情,就放在第二次考虑。
不要一步到位
17、怎么避免过度设计?
简单直观是快速行动的唯一办法
简单直观的解决方案,有一个很大的优点,就是容易理解,易于传达。事情越简单,理解的门槛越低,理解的人越多,传达越准确。一个需要多人参与的事情,如果大家都能够清晰地理解这件事情,这就成功了一半。
客户也是一个参与者。简单直观的解决方案,降低了用户的参与门槛,减轻了学习压力,能够清晰地传递产品的核心价值,最有可能吸引广泛的用户。
简单直观减轻沟通成本
软件最大的风险,来源于软件的复杂性。软件的可用性,可靠性,甚至软件的性能,归根到底,都是软件的复杂性带来的副产品。越复杂的软件,我们越难以理解,越难以实现,越难以测量,越难以实施,越难以维护,越难以推广。
简单直观降低软件风险
做小事的一个最直观的体现,就是代码的块要小,每个代码块都要简单直接、逻辑清晰。整洁的代码读起来像好散文,赏心悦目,不费力气。
为了保持代码块的简单,给代码分块的一个重要原则就是,一个代码块只做一件事情。
使用小的代码块
接口规范一定要清晰。越简单、越规范的代码块,越容易复用。
遵守约定的惯例
一个优秀的程序员,可能 80% 的时间是在设计、拆解和验证,只有 20% 的时间是在写代码。拿出 20% 的时间写的代码,可能要比拿出 150% 时间写的代码,还要多,还要好。这个世界真的不是线性的。
花时间做设计
拆解问题时,思维导图可以帮助我厘清思路,防止遗漏。
思维导图
时序图可以帮助我理解关键的用例,勾画清楚各个部件之间的联系。
时序图
问题清单,可以记录下要解决和已经解决的问题,帮助我记录状态、追踪进度。
问题清单
借助有效的工具
该怎么做到简单直观?
18、简单和直观,是永恒的解决方案
是从需要解决的现实问题开始的。要解决的问题,可以是用户需求,也可以是现实用例。面对要解决的问题,我们要把大问题分解成小问题,把小问题分解成更小的问题,直到呈现在我们眼前的是公认的事实或者是可以轻易验证的问题。
分解问题时,我们要注意分解的问题一定要“相互独立,完全穷尽”(Mutually Exclusiveand Collectively Exhaustive)。这就是 MECE 原则。使用 MECE 原则,可以帮助我们用最高的条理化和最大的完善度理清思路。
从问题开始
从问题开始,为了让我们能够找到一条主线。然后,围绕这条主线,去寻找解决问题的办法,而不是没有目标地让思维发散。这样,也可以避免需求膨胀和过度设计。
为什么从问题开始?
把大问题分解成小问题,再把小问题分解成更小的问题。在这个问题逐层分解的过程中,软件的接口以及接口之间的联系,也就自然而然地产生了。这样出来的接口,逻辑直观,职责清晰。对应的,接口的规范也更容易做到简单、稳定。
问题分解带给我们的好处。问题的层层简化,会让接口的逻辑更直观,职责更清晰。这种好处,也会传承给后续的接口设计。
自然而来的接口
1. 一件事就是一件事,不是两件事,也不是三件事。
2. 这件事是独立的。
3.这件事是完整的。
一个接口一件事情
一般来说一个对象,总是先要实例化,然后才能调用它的实例方法。构造方法和实例方法之间,就有依赖关系。这种依赖关系,是规范化的依赖关系,有严格的调用顺序限制。编译器可以帮我们检查这种调用顺序。
减少依赖关系
所有接口的设计,都是为了最终的使用。方便、皮实的接口,才是好用的接口。接口要很容易理解,能轻易上手,这就是方便。此外还要限制少,怎么用都不容易出错,这就是皮实。
使用方式要“傻”子主题
19、怎么设计一个简单又直观的接口?
线程有两个重要的特征,就是并发执行和共享进程资源。
1. 使用两个以上的线程;
2. 关心共享资源的变化;
3. 改变共享资源的行为。
什么时候需要同步?
避免线程的同步。如果实在难以避免,就减少线程同步的排队时间。
同步是损害效率的
1. 使用单线程;2. 不关心共享资源的变化;3. 没有改变共享资源的行为。
如果这是一个开放的不可变的类,我们要在接口规范里声明这个类是不可变的。这样调用者就不用考虑多线程安全的问题。没有声明多线程安全,或者不可变的接口,都不能当作线程安全的接口使用。
避免线程同步
减少阻塞时间的一个办法,就是只同步和共享资源变化相关的逻辑。引起共享资源变化的事前准备以及善后处理,属于线程内部变化,不需要同步处理。
减少线程同步时间
20、高效率,从超越线程同步开始!
减少内存的使用,意味着更少的内存分配、更少的内存填充、更少的内存释放、更轻量的垃圾回收。内存的使用减少一倍,代码的效率会成倍地提升,
对于数量有限的对象,我们应该优先考虑使用枚举类型,比如交通标志,国家名称等等。
减少实例数量
Java 语言里,有一些历史遗留的接口设计问题,会无意中导致不必要的实例。
避免不必要的实例
Java 原始数据类型的自动装箱(boxing)与拆箱(unboxing)的类型转换。
避免使用原始数据类
由于 Java 内在的单实例模式,我们可以很方便地使用 Java 的原始数据类型,而不用担心实例数量的增长。对于复合的类,我们也可以自己设计单实例模式,从而减少多实例带来的不必要的开销。
使用单实例模式
减少实例的尺寸,就是减少这个实例占用的内存空间。这个空间,不仅包括实例的变量标识符占用的空间,还包括标识符所包含对象的占用空间。
第一个是尽量减少独占的空间;第二个是尽量使用共享的实例。
尽可能多地共享资源,这是一条提高效率的基本原则。在编写代码时,如果能够引用,就坚决不要拷贝;如果能够复用,就坚决不要新创。
减小实例的尺寸
使用更少的内存
一类对象,虽然不是不可变的类的实例,但是它的修改方法被禁止了。当我们使用这些对象的代码时,没有办法对它做出任何修改。这样,这些对象就有了和不可变的实例一样的优点,可以放心地引用。
无法修改的对象
不可变的类
21、怎么减少内存使用,减轻内存管理负担?
为了避免初始化的遗漏或者不必要的代码重复,我们一般建议“声明时就初始化”。但是,如果初始化涉及的计算量比较大,占用的资源比较多或者占用的时间比较长,声明时就初始化的方案可能会占用不必要的资源,甚至成为软件的一个潜在安全问题。
局部变量标识符的声明应该和它的使用尽可能地靠近。这样的规范,除了阅读方面的便利之外,还有效率方面的考虑。局部变量占用的资源,也应该需要时再分配,资源的分配和它的使用也要尽可能地靠近。
延迟分配
用到声明时再初始化,这就是延迟初始化。或者说不到需要的时候,就不进行初始化。
声明时就初始化的好处是简单、直接、代码清晰、容易维护。但是,如果初始化占用的资源比较多或者占用的时间比较长,这个方案就有可能带来一些负面影响。
延迟初始化
22、黑白灰,理解延迟分配的两面性
为了更有效地利用计算资源,我们使用有序的代码,调动起独立的事件。
异步模式在网络阻塞期间,能够更好地利用其他的计算资源,从而提高整体的效率。
从过程到事件
异步是怎么实现的?
23、使用有序的代码,调动异步的事件
JMH 是为 Java 语言或者其他基于 JVM的编程语言设计的一个基准测试工具。
性能测试工具
StringBuilder 和 StringBuffer 为什么快呢?因为 StringBuilder 和 StringBuffer 的内部实现,预先分配了一定的内存。字符串操作时,只有预分配内存不足,才会扩展内存,这就大幅度减少了内存分配、拷贝和释放的频率。
StringBuilder 为什么比 StringBuffer 还要快呢?StringBuffer 的字符串操作是多线程安全的,而 StringBuilder 的操作就不是。
1. 频繁的对象创建、销毁,有损代码的效率;
2. 减少内存分配、拷贝、释放的频率,可以提高代码的效率;
3. 即使是单线程环境,使用线程同步依然有损代码的效率。
字符串的建议
1. Java 的编译器会优化常量字符串的连接,我们可以放心地把长的字符串换成多行;
2. 带有变量的字符串连接,StringBuilder 效率更高。如果效率敏感的代码,建议使用StringBuilder。String 的连接操作可读性更高,效率不敏感的代码可以使用,比如异常信息、调试日志、使用不频繁的代码;
3. 如果涉及大量的字符串操作,使用 StringBuilder 效率更高;
4. 除非有线程安全的需求,不推荐使用线程安全的 StringBuffer。
String、StringBuilder、StringBuffer的如何使用
字符串的操作
解决这个问题的办法通常是使用 SoftReference 和 WeakReference 来存储对象的引用,或者主动地定期清理。
缓存的处理是一个复杂的问题,使用 SoftReference 和 WeakReference未必能够满足你的业务需求。更有效的缓存解决方案,依赖于具体的使用场景。
内存的泄露
有很多系统资源,需要明确地关闭,要不然,占用的系统资源就不能有效地释放。比如说,数据库连接、套接字连接和 I/O 操作等。原则上,所有实现了 Closable 接口的对象,都应该调用 close() 操作;所有需要明确关闭的类,都应该实现 Closable 接口。
未关闭的资源
遗漏的hashCode在使用 Hashtbale、HashMap、HashSet 这样的依赖哈希(hash)值的集合时,有时候我们会忘记要检查产生哈希值的对象,一定要实现 hashCode() 和 equals() 这两个方法。缺省的 hashCode() 实现,返回值是每一个对象都不同的数值。即使是相等的对象,不同的哈希值,使用基于哈希值的集合时,也会被看作不同的对象。这样的行为,可能不符合我们的预期。而且,使用没有实现 hashCode() 和 equals() 这两个方法的对象,可能会造成集合的尺寸持续增加,无端地占用内存,甚至会造成内存的泄漏。
遗漏的hashCode
实现 hashCode() 这个方法的,并没有要求不相等对象的返回值也必须是不相等的。但是如果返回的哈希值不同,对集合的性能就会有比较大的影响。
撞车的哈希值
24、有哪些招惹麻烦的性能陷阱?
增长对软件的要求,就是要有处理越来越多工作和越来越大规模的能力或者潜力。这种能力,通常称之为可伸缩性
指的是提高同一个处理单元处理更多负载的能力。比如,硬件上,增加服务器的硬盘、内存和 CPU;软件上,优化算法、程序和硬件的使用等。
规模垂直扩张是传统的提高负载的方式,方式方法都比较直观,效果也立竿见影。但是,规模垂直扩张成本很高,而且是非线性的。
规模垂直扩张
规模水平扩张,指的是通过增加更多的处理单元,来处理更多的负载。我们常见的例子,就是增加服务器。分布式系统、负载均衡、集群系统这些技术,提供的就是规模水平扩张的能力。
优秀的规模水平扩张技术,可以使用很多廉价的机器,提供大规模的计算能力。一般情况下,规模水平扩张的成本要低于规模垂直扩张。而且,如果其中一个节点出了问题,只要其他节点还在正常工作,整个系统也可以照常运转。如果想要添加一个新节点,系统也不需要停顿。规模水平扩张技术的这些特点,非常适用于高可用性系统。
规模水平扩张
规模扩张能力
影响代码水平规模扩张的最重要的一个因素,就是用户的状态数据。比如,用户的登录状态,用户购物车里的商品信息,HTTP 连接里缓存的会话数据等。
麻烦的状态数据
25、怎么编写可持续发展的代码?
一般来说,当我们使用类似的代码或者类似的功能超过两次时,就应该考虑这样的代码是不是可以复用了。
当我们拷贝粘贴一段代码时,也许会做一点微小的修改,然后用到新的代码里。这时候,我们就要考虑,这段拷贝的代码是不是可以抽象成一个方法?有了抽象出来的方法,我们就不需要把这段代码拷贝到别的地方了。如果这段代码有错误,我们也只需要修改这个方法的实现就可以了。
不要重新发明轮子
改进现有的轮子。如果发现轮子有问题,不要首先试图去重新发明一个相同的轮子,而是去改进它。
如果一个可以复用的代码出了问题,我们要第一时间叫喊起来。这对代码的维护者而言,是一个发现问题、改进代码的机会。一般来说,代码维护者,都喜欢这样的声音,并且能够及时地反馈。我们可以通过发邮件,提交 bug 等我们知道的任何渠道,让代码的维护者知晓问题的存在。这样,我们就加入了改进的过程,间接影响了代码的质量。
推动轮子的改进
一个软件产品中,一个单一功能,只应该有一个轮子。如果有多个相同的轮子,不仅难以维护,而且难以使用,会造成很多编码的困扰。
不要重复多个轮子
每个人都会写很多烂代码,过去写过,未来可能还会再写。这些烂代码,如果运行得很好,没有出现明显的问题,不必要耗费过多的时间去改写烂代码。
什么时候修改烂代码呢?代码投入使用之前,以及代码出问题的时候,就是我们修改烂代码的时候。
该放手时就放手
1. 要提高代码的复用比例,减少编码的绝对数量
2. 要复用外部的优质接口,并且推动它们的改进
3. 烂代码该放手时就放手,以免引起不必要的兼容问题
总结
26、怎么尽量“不写”代码?
一致性的性能体验,是软件产品赢得竞争的关键指标。复杂的,反应迟钝的软件,很难赢得用户的尊敬。
1. 提升用户体验
通过降低软件的复杂度,提高软件的复用,提前考虑性能问题,可以降低软件研发成本,缩短软件开发周期。
2. 降低研发成本
经济的代码可以降低软件的复杂度,提高计算资源的使用效率,降低运营成本。
3. 降低运营成本
复杂的代码和性能低下的代码,更容易成为黑客攻击的目标。如果一个服务器,需要耗费很多资源才能处理一个请求,那么数量很少的模拟请求攻击,就可以导致服务器瘫痪。
4. 防范可用性攻击
为什么需要经济的代码?
第一个是识别核心需求,我们要从用户的角度出发,知道什么是核心需求,什么是衍生需求,什么是无效需求。
迭代演进,有所主次。
避免需求膨胀和过度设计,是编写经济代码最需要注意的根基性问题。
1. 避免过度设计
设计一个简单直观的接口,首先,我们要从问题开始。把问题逐步拆解成一个个已经完全穷尽的小问题,这就是我讲到的“相互独立,完全穷尽”原则。在拆解的过程中,软件的接口与接口之间的关系会自然而然地产生。
2. 选择简单直观
我们就不需要线程同步了:使用单线程;不关心共享资源的变化;没有改变共享资源的行为。
如果线程同步不可避免,就要想办法减少线程同步时间。
3. 超越线程同步
减少内存的使用主要有两个方法,第一个方法是减少实例的数量,第二个办法是减小实例的尺寸。
4. 减少内存使用
学会规避一些常见的性能陷阱,比如字符串的操作、内存泄露、未正确关闭的资源和遗漏的 hashCode 等。
5. 规避性能陷阱
经济的代码需要跟得上产品的规模扩张。特别是支持规模水平扩张。状态数据是影响规模水平扩张能力的最重要的因素。分离无状态数据、提供无状态服务,减少有状态服务的规模,是提升规模水平扩张能力的最佳实践。
6. 规模扩张能力
怎么编写经济的代码?
需求是真实的客户需求吗?
要解决的问题真实存在吗?
需求具有普遍的意义吗?
这个需求到底有多重要?
需求能不能分解、简化?
需求的最小要求是什么?
这个需求能不能在下一个版本再实现?
需求评审
能使用现存的接口吗?
设计是不是简单、直观?
—个接口是不是只表示一件事情?
接口之间的依赖关系是不是明确?
接口的调用方式是不是方便、皮实?
接口的实现可以做到不可变吗?
接口是多线程安全的吗?
可以使用异步编程吗?
接口需不需要频繁地拷贝数据?
无状态数据和有状态数据需不需要分离?
有状态数据的处理是否支持规模水平扩张?
设计评审
有没有可以重用的代码?
新的代码是不是可以重用?
有没有使用不必要的实例?
原始数据类的使用是否恰当?
集合的操作是不是多线程安全?
集合是不是可以禁止修改?
实例的尺寸还有改进的空间吗?
需要使用延迟分配方案吗?
线程同步是不是必须的?
线程同步的阻塞时间可以更短吗?
多状态同步会不会引起死锁?
是不是可以避免频繁的对象创建、销毁?
是不是可以减少内存的分配、拷贝和释放频率?
静态的集合是否会造成内存泄漏?
长时间的缓存能不能及时清理?
依赖哈希值的集合,储存的对象有没有实现 hashCode0 和 equalsQ 方法?
hashCode( 的实现,会不会产生撞车的哈希值?text direction=\"ltr\" lengthadjust=\"spacingAndGlyphs\" height=\"32\" textlength=\"277\" x=\"144\" y=\"1254\" style=\
代码的清理,有没有变更代码的逻辑?
代码评审
经济代码的检查清单
第一个习惯是,要尽早地考虑性能问题。如果你最早接触的是需求制定,就从需求开始考虑;如果你最早接触的是软件架构,就从架构层面开始考虑;如果你最早接触的是软件设计,就从软件设计开始考虑;如果你最早接触到的是代码,代码也有很多性能问题可以考虑。总之,要主动、尽早地考虑效率问题。
第二个习惯是,性能的实践经验需要日积月累。性能的实践经验和技术丰富繁杂,大到产品蓝图,小到每一行代码,中间还有软件的架构、选型、部署等诸多环节,都有很多的最佳实践可以积累。而且这些最佳实践,也会随着时间的推移发生变化,比如说会出现更好的技术方案,曾经的技术满足不了新需求等。所以,我们也要随时更新我们的储备,摒弃过时的经验。
27、编写经济代码的检查清单
1.不起眼的小问题,也会有巨大的安全缺陷,造成难以估量的损失;
2.编写安全的代码,是我们必须要掌握的基础技能;
3.安全问题,既是技术问题,也是管理问题。
28、为什么安全的代码这么重要?
1. 识别并且选择最重要的事情;
2. 确定自己最擅长的事情,全力以赴地做好。
3. 选择你的帮手,充分信任并授权
基本要求
从用户感受的角度出发,定义和计量软件缺陷,是其中一个比较好的、常用的软件缺陷评估体系。
1.缺陷影响的深度,软件缺陷带来的问题的严重性:软件缺陷导致的最严重的问题是什么?
2.缺陷影响的广度,软件缺陷带来问题的可能性:软件缺陷导致问题出现的频率是多大?
关注用户感受
1. 高优先级(P1):高严重性、高可能性;
2. 中优先级(P2):高严重性、低可能性;低严重性、高可能性;
3. 低优先级(P3):低严重性、低可能性。
软件缺陷的优先等级
在一个好的软件缺陷评估体系中,不是只有代码错误才会被关注,没有错误的代码,也可能存在需要变更或者改进的“缺陷”。
程序员认为是严重的缺陷,用户可能一点儿都感受不到;程序员认为无关紧要的事情,放到了用户的使用场景中,可能就是非常严重的事故。从外向内看缺陷,要求我们站在用户的角度思考问题,看待缺陷。这是一个可以让我们深切关注用户感受的视角。从用户视角出发的决策,可以让我们的时间使用得更有市场价值。
缺陷,需要从外向内看
1. 第一优先级(P1):高严重性、高可能性;
2. 第二优先级(P2):高严重性、中可能性;中严重性、高可能性;
3. 第三优先级(P3):高严重性、低可能性;中严重性、中可能性;低严重性、高可能性;
4. 第四优先级(P4):中严重性、低可能性;低严重性、中可能性;
5. 第五优先级(P5):低严重性、低可能性。
细化的优先级
软件缺陷优先等级的定义是为了帮助我们更好地解用户的感受程度,以及安排时间和处理事情。
1. 如果已经存在应对办法,优先等级可以下调;
2. 如果软件缺陷引起广泛的公开关注,优先等级应该上调;
3. 如果软件缺陷影响了重要的客户,优先等级应该上调;
4. 如果软件缺陷影响了重要的商业运营,优先等级应该上调。
优先级的灵活性
P1 的事情需要我们立即全力以赴、必须完成;P2 的事情需要我们协调资源,尽快完成;P3 的事情需要我们密切关注,尽量完成。
1. 有什么事情是你必须要做的?
只有你能够修复的 bug,你可以记到自己名下,负责修复这些缺陷。
2. 哪些事情是只有你能做的?
适合别人修复的 bug,如果还没有记到别人名下,你可以琢磨下谁是最合适的人选,然后和他商量,看他有没有时间,愿不愿意负责这个缺陷。当然,别人也可能会问你愿不愿意修复另外一些缺陷。
3. 哪些事情是别人可以帮你做的?
管理好自己的时间
由于编写安全代码本身的挑战性,以及消除安全漏洞的复杂性,业界通常需要进行大范围的合作,以便准确、快速、周全地解决安全缺陷问题。
私密性指的是数据未经授权,不得访问,解决的是“谁能看”的问题。
1. 对私密性的影响(Confidentiality)
完整性指的是数据未经授权,不得更改,解决的是“谁能改”的问题。
2. 对完整性的影响(Integrity)
可用性值得是数据经过授权,可以访问,解决的是“可以用”的问题。
3. 对可用性的影响(Availability)
4. 对授权范围的影响(Authorization Scope)
安全缺陷的严重性划分
1. 安全攻击的路径(Attack Vector)
2. 安全攻击的复杂度(Attack Complexity)
3. 安全攻击需要的授权(Privileges Required)
4. 安全攻击是否需要用户参与(User Interaction)
安全缺陷的可能性划分
通用缺陷评分系统使用了标识符系统和计分系统,通过标识符来标识测量维度的指标,通过十分制的计分来衡量安全问题的严重程度。由于测量维度的增多以及评分计算的复杂性,我们通常使用工具来记录和查看安全缺陷问题的等级。
安全漏洞,需要大范围协作
29、如何评估代码的安全缺陷?
好的代码是坚持使用最直观的编码方式,而不是追求代码简短,避免很多不必要的错误
减少代码错误
节省程序员思考的时间
节省代码阅读者的时间
好的代码好处
1. 容易理解
2. 没有明显的安全问题
3. 能够满足最关键的需求
4. 有充分的注释
5. 使用规范的命名
6. 经过充分的测试
好代码
1. 难以阅读的代码
2. 浪费大量计算机资源的代码
3. 代码风格混乱的代码
4. 复杂的、不直观的代码
5. 没有经过适当测试的代码
坏代码
好代码和坏代码
计划
分析和设计
代码实现
测试
运营
维护
流程
项目经理
设计人员
开发人员
测试人员
客户
服务人员
参与人员
软件生命周期
1. 代码写得又快又好,是“经济”的;代码写得快,但是错误多,不是一个“经济”的行为
2. 代码跑得又快又好,是“经济”的;代码跑得快,但是安全问题突出,不是一个“经济”的行为
3. 代码写得精简易懂,是“经济”的;代码写得精简,但是没人看得懂,不是一个“经济”的行为
比如
优秀的代码需要具备三个特征: 经济、规范、安全
程序员间合作交流最重要的语言便是代码,换句话说,这就需要程序员规范地编写代码,使用大家都接受的风格。不规范的代码,程序员可能节省了眼前的时间,但是测试、运营、维护阶段,就需要更多的时间。而一旦问题出现,这些代码会重新返工,又回到我们手里,需要阅读、修改,再一次浪费我们自己的时间。对于这些代码,每一点时间的付出,都意味着投入,意味着浪费,意味着我们损失了做更有意义事情的机会
1、什么是好代码
优秀一个非常优秀的程序员,他主观上非常认真,能力又非常强,但他也会犯非常“低级”、“幼稚”的错误
第一个观点是好的程序员不会写坏的代码
对于自身,我们对自身的希望,对别人,可以更加宽容。一个好的团队,我们首先要思考如何提供一种比较好的机制,减少此类错误的发生
第二个观点是同一个错误不能犯两次
错误并不可怕,你不必为此深深自责,更不应该责备他人。要不然,一旦陷入自责和指责的漩涡,很多有建设意义的事情,人们可能没有意识去做;或者即使意识到了,也没法做,做不好。
第三个观点是一个人犯了错误并不可怕,怕的是不承认错误
普遍的观点
提高程序员的修养,是一个永不过时的课题。从别人的失败和自己的失败中学习、积累、提高,是一个程序员成长的必修课
优秀的代码源于我们对细节的热情和执着
如果你能够找到哪怕仅仅是一个小问题的一个小小的改进办法,都有可能会给你的代码质量带来巨大的提升和改变
程序猿
编译器的警告,我们一定要非常警觉。能消除掉所有的警告,你就应该消除掉所有的警告。就算实在没有办法消除掉编译警告,那你也一定要搞清楚警告产生的原因,并确认编译警告不会产生任何后续问题
编译器
软件测试会尽可能地覆盖关键逻辑和负面清单,以确保关键功能能够正确执行,关键错误能够有效处理
一个没有良好回归测试的软件,很难保证代码变更的质量;也会使得代码变更充满不确定性,从而大幅地提高代码维护的成本
回归测试
代码评审是一个有效的在软件研发过程中抵御人类缺陷的制度。通过更多的眼睛检查软件代码,被忽视的错误更容易被逮住,更好的设计和实现更容易浮现出来
静态代码分析(Static Code Analysis)是通过对源代码的检查来发现潜在问题的一种软件质量保障方式
代码覆盖率(Code Coverage)是一个反映测试覆盖程度的指标。它不仅仅量化测试的指标,也是一个检测代码缺陷的好工具
代码分析
如何避免
2、如何避免错误
需要能够熟练操控一门编程语言
掌握一门编程语言
程序员的存在不是为了写代码,而是为了解决现实问题,实现现实价值
优秀的程序员还要深入理解问题,懂得问题的最核心价值。只有理解了问题,看到了解决问题的价值,我们才能够真正解决好问题
解决现实的问题
能够发现关键问题,是一个好程序员和优秀程序员的分水岭
可以从一个被动的做事情的程序员,升级为一个主动找事情的程序员
优秀的程序员,能够发现解决方案背后的妥协和风险。所以,他可以预设风险防范措施,设置软件的适用边界
优秀的程序员,能够敏锐地观察到产品的关键问题,或者客户未被满足的需求。所以,他可以推动产品持续地进步和演化
发现关键的问题
优秀的程序员,一定是懂得妥协,懂得选择,一步一步把事情沉静地朝前推动的人
我们写的每一行代码,都可能存在问题。有时候,我发现别人的代码的问题;有时候,别人发现我的代码的问题。我们最后都会明白,要坦诚地面对别人的问题,也要坦然地面对自己的问题
沉默的前行者
优秀的程序员是他人可以依赖的伙伴
优秀的程序员,知道团队合作的重要性,是一个优秀的团队成员。他在团队中能够快速学习、成长,变得越来越优秀,也能够帮助其他团队成员变得越来越优秀。
编程语言、花样工具、逻辑思维、解决问题这些“硬技能”可以决定我们的起点的话,影响力、人际关系这些“软技能”通常影响着我们可以到达的高度。
可以依赖的伙伴
优秀的程序员是高效的时间管理者。
优秀的程序员会更好地管理时间,或者提高效率,或者用好时间。
要坚持做需要做的事情。不需要的、不紧急的、价值不大的,我们可以暂时搁置起来。一个人,能做的事情是有限的,能把最重要的事情最好,就已经很了不起了。
时间管理者
3、优秀程序员的特征
编码规范指的是针对特定编程语言约定的一系列规则,通常包括文件组织、缩进、注释、声明、语句、空格、命名约定、编程实践、编程原则和最佳实践等。
一旦学会了编码规范,并且严格地遵守它们,可以让我们的工作更简单,更轻松,少犯错误。
代码编程规范
复杂是代码质量的敌人。越复杂的代码,越容易出现问题。
在编码的时候,我们应该尽量使代码风格直观、逻辑简单、表述直接。
规范的代码,可以降低代码出错的几率
在代码制造的每一道关卡,规范执行得越早,问题解决得越早,整个流水线的效率也就越高。
规范的代码,可以提高编码的效率
在一个软件生命周期里,软件维护阶段花费了大约 80% 的成本
很多软件代码,其生命的旅程超越了它的创造者,超越了团队的界限,超越了组织的界限,甚至会进入我们难以预想的领域。
规范的代码,降低软件维护成本
我们要尽早地使用编码规范,尽快地培养对代码风格的敏感度。 良好的习惯越早形成,我们的生活越轻松。
编码规范越使用越高效
4、代码规范的价值
为标识符提供附加的信息,赋予标识符现实意义。帮助我们理顺编码的逻辑,减少阅读和理解代码的工作量
使代码审核变得更有效率,专注于更重要的问题,而不是争论语法和命名规范这类小细节,提高开发效率
提高代码的清晰度、可读性以及美观程度
避免不同产品之间的命名冲突
一个好的命名规范的好处
驼峰命名法指的是使用大小写混合的格式,单词之间不使用空格隔开或者连接字符连接的命名方式。
驼峰命名法
在蛇形命名法中,单词之间通过下划线“_”连接,比如“out_of_range”。
蛇形命名法(snake_case)
在蛇形命名法中,单词之间通过连字符“-”连接,比如“background-color”。
串式命名法(kebab-case)
命名方法
要有准确的意义(名字要能够准确、完整地表达出它代表的意义,可以见字知意,名副其实。)
Java 倾向于使用驼峰命名法
C 语言倾向于使用蛇形命名法
CSS 使用串式命名法。
严格遵守命名规范
可读性强的名字优先于简短的名字,尽量使用完整的词汇。
不要使用缩写、简写、缩略词,除非这些词语被广泛使用。
不要使用太短的名字,比如一个字母,除非是广泛接受的特例(i/j/k/m/n 表示临时使用的整数,c/d/e 表示临时使用的字符)
避免含糊、混淆或者误导。
不要混合使用英文和汉语拼音。
可读性优先
命名原则
5、命名规范
代码块内所有的内容都是为了一个目标服务的,不能把无关的内容放在同一个代码块里。同一个代码块里语句的相互联系比与相邻代码块里的语句关系更为紧密;
保持代码块的单一性,一个代码块只能有一个目标
代码块是一个完整的信息块。一个代码块
注意代码块的完整性
代码块过多,会让人觉得路径太长,逻辑复杂,不容易阅读理解。一个基础的代码块最好不要超过 25 行(通常显示屏小半个页面),否则就会有增加阅读理解的困难。
代码块数量要适当。
程序分块
合理利用空白空间,空白空间不仅区分代码块,还可以表示代码块之前的联系
阅读的习惯顺序是从左到右,代码也如此。因此不同行,但同级别的代码要靠左对齐。
同级别代码块靠左对齐
阅读代码总是从上往下读,不同行的同级别的代码块之间,要使用空行分割。
同级别代码块空行分割
区分不同行的不同级别的代码,可以使用缩进。缩进的目的是为了让我们更直观地看到缩进线,从而意识到代码之间的关系。
下一级代码块向右缩进
同一行内的代码块,可以使用空格区分开不同的逻辑单元。
同行内代码块空格区隔
如何利用空白空间
利用空白空间
每行代码字符数的限制。一般情况下,每行代码不要超出 80 个字符
如果一行不足以容纳一个表达式,就需要换行
在逗号后换行。
在操作符前换行。
高级别的换行优先。
新的换行与上一行同级别表达式的开头对齐。
如果上述规则导致代码混乱或者代码太靠右,使用 8 个空格作为缩进(两个缩进单位)。
换行原则
基本的换行原则
整理代码有一个基本的思想,那就是把代码分割成大脑能够有效识别并记忆的信息块,通过合理地使用空行、空格和缩进,把这些信息块清晰地呈现出来。清晰的代码结构,可以帮助我们理顺编码的思路,提高编码的效率,减少编码的错误,提高代码的可读性,降低代码的维护成本。
6、代码格式
因为注释不需要运行,所以没有常规的办法来测试它
注释难以维护,这是使用注释带来的最大的麻烦。
注释为我们提供了一个借口。使用注释来解释代码,是注释的本意。但是,我们有时候会过度依赖解释,从而放弃了潜在的替代方案。
注释的缺点
第一种类型,是记录源代码版权和授权的
第二种类型,是用来生成用户文档的
第三种类型,是用来解释源代码的
常见的注释类型
针对第一种注释类型,也就是固定的版权和授权信息,使用一般的星号注释符(/-/)
针对第二种注释类型,即生成用户文档的注释,使用 Javadoc 要求的格式,文档注释符(/-*/)。除了首行使用特殊的文档注释符(/)
针对第三种注释类型,也就是代码解释注释,只使用行注释符(//)
注释风格
准确,错误的注释比没有注释更糟糕。
必要,多余的注释浪费阅读者的时间。
清晰,混乱的注释会把代码搞得更乱。
原则
7、注释
一行一个声明
标识符的声明应该和它的使用尽可能地靠近,特别是局部变量的标识符声明。
局部变量需要时再声明
为了阅读和记忆,类变量的声明则要集中。因为类变量无论是私密变量,还是公开变量,在类的方法实现中,随时都可以调用。我们需要把这些变量放在一起,以便于修改和查找。
类属性要集中声明
声明时就应该完成初始化。声明时初始化,可以防止初始化的遗漏或者不必要的代码重复。
声明时就初始化
类声明和方法声明后,要使用花括号把实现的代码包括进来。
左括号不要单独成行,要紧随在语句尾部,以一个空格隔开
右括号单独一行。
尾随的花括号
靠紧的小括号
语义相关的词语,常见的搜索模式,要尽量放在用一行。
搜索优化的换行
八大原则
8、标识符声明
识别子类的方法是不是重写方法
Override
重写方法可以不遵守父类方法的规范
在声明继承关系中,Java注解该如何使用?
第一件事情是,如果接口的设计存在不合理性,或者新方法取代了旧方法,我们应该尽早地废弃该接口。
Deprecated
第二件事情是,如果我们在现有的代码中使用了废弃的接口,要尽快转换、使用替换的方法。
在废弃退役接口的情况下,如何使用注解?
SuppressWarnings
Java 提供了一个不推荐使用的注解,SuppressWarnings。这个注解告诉编译器,忽略特定的警告。警告是非常有价值的信息,忽略警告永远不是一个最好的选项。
忽略警告
1. 重写的方法,总是使用
2. 过时的接口,尽早废弃
3. 废弃的接口,不要使用
9、Java注解
异常状况的处理会让代码的效率变低。一个流畅的业务,它实现代码的执行路径,理想的状况就是没有任何异常状况发生。
异常的原因
非正常异常(Error)
运行时异常(RuntimeException)
非运行时异常(检查型异常(CheckedException))
可处理异常(Exception)
异常分类
应用程序需要处理异常(CheckedException 和 RuntimeException),就需要我们在方法的规范描述文档中清楚地标记异常。没有标记的异常,应用程序没有办法通过文档了解哪些异常需要处理、什么状况下会抛出异常以及该怎么处理这些异常。
标记清楚抛出异常
异常类名(IllegalArgumentException,FileNotFoundException)
异常描述(“Invalid file path”)
异常堆栈(at sun.security.ssl.InputRecord.read(InputRecord.java:504))
处理好捕获异常
不要使用异常机制处理正常业务逻辑
异常的使用要符合具体的场景
具体的异常要在接口规范中声明和标记清楚
10、处理异常
1. 版权和许可声明
2. 命名空间(package)
3. 外部依赖(import)
代码文件的头部结构
1. 类的规范
2. 类的声明
3. 类的属性和方法
代码文件对象结构
1. 类的属性
2. 构造方法
3. 工厂方法
4. 其他方法
类的内部代码结构
1.方法的规范
2. 方法的声明
3. 方法的实现
方法的代码结构
1. 方法的简短介绍
2. 方法的详细介绍(可选项)
3. 规范的注意事项 (使用 apiNote 标签,可选项)
4. 实现方法的要求 (使用 implSpec 标签,可选项)
5. 实现的注意事项 (使用 implNote 标签,可选项)
6. 方法参数的描述
7. 返回值的描述
8. 抛出异常的描述:需要注意的是,抛出异常的描述部分,不仅要描述检查型异常,还要描述运行时异常
9. 参考接口索引(可选项)
10. 创始版本(可选项)
方法规范的实现
1. public/private/protected(访问控制限定词,制定访问权限)
2. abstract(抽象类或者抽象方法,具体实现由子类完成)
3. static(静态类、方法或者类属性)
4. final(定义不能被修改的类、方法或者类属性)
5. transient(定义不能被序列化的类属性)
6. volatile(定义使用主内存的变量)
7. default(声明缺省的方法)
8. synchronized(声明同步的方法)
9. native(声明本地的方法,也就是 Java 以外的语言实现的方法)
10. strictfp(声明使用精确浮点运算)
按顺序使用限定词
1. 版权和许可声明代码块
2. 命名空间代码块
3. 外部依赖代码块
4. 类的代码块
5. 类的属性与方法之间
6. 类的方法之间
7. 方法实现的信息块之间
使用空行分割代码块
11、组织好代码块
提高协作效率的最高技巧不是提高沟通技巧,而是要减少沟通的数量,提高沟通的质量,尤其是要减少数量。
区分外部接口和内部实现
无论对于调用者,还是实现者来说,外部接口的使用都要有章可循,有规可依
合约要成文
合约既然是我们协作的依靠,就一定要清晰可靠、容易遵循,不能有模棱两可的地方。
接口规范主要用来描述接口的设计和功能,包括确认边界条件、指定参数范围以及描述极端状况。
合约要清楚
接口的设计和规范的制定,一定要谨慎再谨慎,小心再小心,反复推敲,反复精简。
合约要稳定
接口规范,我们的原则是,能不变更就不变更;必须的变更,一定要反复思量该怎么做才能把影响降到最低。
变更要谨慎
接口规范是协作合约
JavaDoc 就是一种顾及了多方利益的一种组织形式。它通过文档注释的形式,在接口声明的源代码定义和描述接口规范。这种和源代码结合的方式,可以方便我们维护接口规范,也有利于保持接口规范和接口声明的一致性。
JavaDoc 工具可以把文档注释,转换为便于阅读为 HTML 文档。这样就方便规范的使用者阅读了。
使用Java Doc
1. 起草接口规范,或者起草提议的修订规范
2. 找相关领域的专家,审议草案,并根据评审意见,修改接口规范
3. 如果领域专家审议通过,提交兼容性和规范性审查程序;并根据审查意见,相应地修改接口规范
4. 兼容性和规范性审查通过,修改接口合约
5. 按照议定的接口规范,编写最终的实现的代码
接口制定和修订步骤
12、接口文档规范
1. 从用户的角度出发来思考用户指南,用户指南要容易上手
2. 用户指南和源代码一样,也有开发周期,也是需要维护的
13、用户指南
在不损害代码质量的前提下,效率可以节省我们的时间和成本。这种节省不仅仅停留在编码阶段,更体现在整个软件的生命周期里。
1. 提高编码的效率
代码的质量在于它和预期规范的一致性。一致、简单、规范的代码易于测试。相反,复杂的代码会加大测试的难度,难以达到合适的测试覆盖率。
2. 提高编码的质量
代码的维护要求代码必须能够修改,增加新功能,修复已知的问题。如果代码结构的清晰、易于阅读理解,那么问题就容易排查和定位。
3. 降低维护的成本
要想让更多的人参与,就需要一致的编码风格,恰当地使用文档。要方便他们阅读,便于解释。使用编码规范可以让一个程序员减少出错,避免不必要的困扰。
4. 扩大代码的影响
为什么需要编码规范?
自主模式的运行是无意识的、快速的、不怎么耗费脑力;
自主模式在熟悉的环境中是精确的,所作出的短期预测是准确的,遇到挑战时,会第一时间做出反应。然而,它存在成见,容易把复杂问题简单化,在很多特定的情况下,容易犯系统性的错误。比如说,第一印象、以貌取人,就是自主模式遗留的问题。
自主模式(快系统)
控制模式需要集中注意力,耗费脑力,判断缓慢,如果注意力分散,思考就会中断。
控制模式能够解决更复杂的问题。但刻意掌控会损耗大脑能量,而且很辛苦。处于控制模式中太长时间,人会很疲惫,丧失一部分动力,也就不愿意去做启动控制模式了。
控制模式(慢系统)
自主模式和控制模式的分工合作是高效的,损耗最小,效果最好。快速的、习惯性的决断交给勤快省力的自主模式,复杂的、意外的决断由耗时耗力的控制模式接管。
编码规范中很大一部分内容,是增加共识、减少意外,扩大自主思维模式覆盖的范围,减少控制模式必须参与的内容。
模式识别的认知是一柄双刃剑,既是福音也是祸害。它可以帮助我们毫不费力地使用经验,但习惯一旦养成就很难改变,我们不愿意打破旧模式,去学习新模式和接受新技术。
程序员很容易理解和自己编码风格类似的代码。如果编码风格和自己的大相径庭,就会感到焦躁和烦恼。编码风格一旦形成,就难以更改,转变很痛苦。
识别模式
对于既定模式的识别,是通过猜测进行的。对于每一个新场景,大脑立即会把它起始部分当作一个线索,然后通过拟合所有已知模式的起始部分,来预测模式的其余部分,猜测“言外之意”。我们掌握的信息越少,就越有可能犯错误。
编写代码时,我们要有意识地提供足够的线索和背景,使用清晰的结构,加快模式的识别,避免造成模式匹配过程中的模糊和混淆带来的理解障碍。
猜测模式
感官记忆是对我们感官体验的记忆,非常短暂(大约三秒钟)
短期记忆是我们可以回忆的,刚刚接触到的信息的短暂记忆。短期记忆很快,但是很不稳定,并且容量有限。如果中途分心,即便只是片刻,我们也容易忘记短期记忆的内容。
工作记忆是我们在处理认知任务时,对信息进行短暂存贮并且执行操作的记忆。工作记忆将短期记忆和长期记忆结合起来,处理想法和计划,帮助我们做出决策。
长期记忆涵盖的记忆范围从几天到几十年不等。为了成功学习,信息必须从感官或短期记忆转移到长期记忆中。和短期记忆相比,长期记忆记忆缓慢,但是保持长久,并且具有近乎无限的容量。
在组织代码时,不要让短期记忆超载,要使用短小的信息快,方便阅读;要适当分割需要长期记忆和短期记忆的内容,比如接口规范和代码实现,帮助读者在工作记忆和长期记忆中组织和归档信息。
记忆模式
我们的眼睛一次只能专注于一个很小的区域,忽视该区域以外的内容。
当我们阅读时,我们的眼睛习惯从左到右,从上到下移动,所以靠左的信息更容易被接受,而靠右的信息更容易被忽略。但是,当我们快速阅读或者浏览特定内容时(比如搜索特定变量),眼睛就会只喜欢上下移动,迅速跳过。聚焦区域小,眼睛倾向于上下移动,这就是报纸版面使用窄的版面分割,而不是整幅页面的原因之一。
眼睛的运动
编码规范的心理因素
代码是按照编码指南编写的吗?
代码能够按照预期工作吗?
文件是不是在合适的位置?
支撑文档是不是充分?
代码是不是易于阅读、易于理解?代码是不是易于测试和调试?有没有充分的测试,覆盖关键的逻辑和负面清单?名字是否遵守命名规范?名字是不是拼写正确、简单易懂?名字是不是有准确的意义?代码的分块是否恰当?代码的缩进是否清晰、整洁?有没有代码超出了每行字数的限制?代码的换行有没有引起混淆?每一行代码是不是只有一个行为?变量的声明是不是容易检索和识别?变量的初始化有没有遗漏?括号的使用是不是一致、清晰?源代码的组织结构是不是一致?
版权信息的日期有没有变更成最近修改日期?限定词的使用是不是遵循既定的顺序?有没有注释掉的代码?有没有执行不到的代码?有没有可以复用的冗余代码?复杂的表达式能不能拆解成简单的代码块?代码有没有充分的注释?注释是不是准确、必要、清晰?不同类型的注释内容,注释的风格是不是统一?有没有使用废弃的接口?能不能替换掉废弃的接口?不再推荐使用的接口,是否可以今早废弃?
继承的方法,有没有使用 Override 注解?有没有使用异常机制处理正常的业务逻辑?异常类的使用是不是准确?异常的描述是不是清晰?是不是需要转换异常的场景?转换异常场景,是不是需要保留原异常信息?有没有不应该被吞噬的异常?外部接口和内部实现有没有区分隔离?接口规范描述是不是准确、清晰?接口规范有没有描述返回值?接口规范有没有描述运行时异常?接口规范有没有描述检查型异常?接口规范有没有描述指定参数范围?接口规范有没有描述边界条件?接口规范有没有描述极端状况?接口规范的起草或者变更有没有通过审阅?接口规范需不需要标明起始版本号?产品设计是不是方便用户使用?用户指南能不能快速上手?用户指南的示例是不是可操作?用户指南和软件代码是不是保持一致?
编码规范的检查清单
1. 使用 Git 或者 Mercurial 这样成熟的版本控制工具,以及像 Bugzilla,Phabricator,Jira 这样的 bug 管理工具。
2. 找一个工具,可以生成可视化的代码变更页面。比如 OpenJDK 的 webrev,或者像Phabricator 这样的集成工具。
3. 找到一个集中的地方,可以让所有人都看到代码变更页面,都可以方便地提意见。比如,OpenJDK 使用 cr.openjdk.java.net 展示,使用邮件列表讨论。GitHub 和Phabricator 这样的集成工具也有类似展示窗口。
4. 制定小制度,没有经过评审的代码,不能提交。OpenJDK 的提交,是用过检查Reviewed-by 这样的提交描述字段,来自动判断的。Phabricator 这样的集成工具,也有类似的强制设置。
5. 制定代码评审的通过准则,比如不规范的代码,没有通过测试的代码,以及没有测试代码的变更,不能提交。如果允许例外,提交者要解释清楚。
6. 把测试归拢起来,找一个自动化的工具,可以执行回顾测试。比如说使用“maketest”命令行,就可以执行所有的回归测试。类似 Phabricator 这样的集成工具,也有类似的测试执行功能。
7. 定期执行代码分析,并且把分析结果记录下来,检查、分析、改进。这个分析工具可以是 SpotBugs,或者其他的商业软件。
8. 把需求分析、架构设计、接口设计,也当作 bug 管理,纳入到评审的过程中来。
9. 改进激励标准。程序员的评价标准不要狭隘在编写了多少行代码上,还要看看参与了多少评审,发现了多少问题。
10. 鼓励内部推广软件开发经验,比如说什么样的插件可以编写规范的代码,什么样的代码容易出现安全问题,什么样的代码效率比较高。有了成熟的经验后,就总结下来,推广出去,形成团队的重要共识和财富。
如何建立代码规范流水线
14、规范代码检查
代码的性能并不是可以多块地进行加减乘除,而是如何管理内存、磁盘、网络、内核等计算机资源。
为了管理代码的性能,在一定程度上,我们需要很好地了解计算机的硬件、操作系统以及依赖库的基本运行原理和工作方式。一个好的架构师,一定会认真考虑、反复权衡性能要求。
怎么理解代码的性能?
第一件事情是,我们的代码是不是正确?代码正确这个门槛特别低。如果代码出现了大范围的错误,说明编程还没有入门。
第二件事情是,我们的代码运行起来有没有效率,运营成本低不低?这是判断代码是否经济的一个标准。编写经济的代码的门槛稍微高一些,它需要更多的知识和经验,但它也是能让我们脱颖而出的一个基本功。门槛越高,跨越门槛的价值就越大。我们要是一直不愿意跨越这个高门槛,面临的竞争压力就会越来越大。
需不需要学习性能?
越早考虑性能问题,我们需要支付的成本就越小,带来的价值就越大。甚至是,和不考虑性能的方案相比,考虑性能的成本可能还要更小。
需不需要考虑代码性能?
扩展硬件并不是总能够线性地提高系统的性能。出现性能问题,投入更多的设备,只是提高软件性能的一个特殊方法。而且,这不是一个廉价的方法。
硬件扩展能解决性能问题吗?
敏捷开发最重要的一个原则,就是高质量地工作。没有高质量的工作作为基础,敏捷开发模式就会越走越艰难,越走越不敏捷,越走成本越高。而性能问题,是最重要的质量指标之一。
性能问题能滞后处理吗?
采用性能工程思维,才能确保快速交付应用程序,而不用担心因为性能耽误进度。性能工程思维通过流程“左移”,把性能问题从一个一次性的测试行为,变成一个贯穿软件开发周期的持续性行为;从被动地接受问题审查,变成主动地管理质量。在软件研发的每一步,每一个参与人员,都要考虑性能问题。整个过程要有计划,有组织,能测量,可控制。
采用性能工程思维,架构师知道他们设计的架构支持哪些性能的要求;开发工程师清楚应该使用的基本技术,而不是选择性地忽略掉性能问题;项目管理人员能够在开发软件过程中跟踪性能状态;性能测试专家有时间进行负载和压力测试,而不会遇到重大意外。
要有性能工程的思维
什么时候开始考虑性能问题?
15、为什么需要经济的代码?
向看的大佬麻烦移步极客时间
代码精进之路
0 条评论
回复 删除
下一页
职业:暂无
作者其他创作: