代码的艺术 - 软件工程学习笔记
软件工程能力
定义
"使用系统化的方法,在保证质量的前提下,更高效率地为客户/用户持续交付有价值的软件或服务的能力。"
五个核心
- 研发的目的是提供价值
- 质量第一
- 实现价值的持续交付
- 系统化和科学的方法
- 持续提升研发效率
如何提高?
- 工程能力的素质要求(公司,团队,个人)
- 人是工程能力的根本
- 工程能力提升源于自我驱动
- 个人能力的素质要求(写好代码,写好文档,做好项目管理)
代码的艺术
思考题
1. 在学校和在公司写代码,二者有什么不同?
在学校写代码或许只关注于代码本身,而缺少与各种其它角色沟通协作。 对整个系统没有整体认识,很少思考系统的健壮性,可用性,可读性。
2. 做软件工程师到底有没有前途?
我认为是非常有前途的,从生存角度来讲,软件工程师高薪,从个人价值角度来讲,软件工程师是能够改变世界的职业,但软件工程师不等于"码农",不能只会写代码,还需要各种专业素质。
3. 如何修炼成为优秀的软件工程师?
打牢基础,持续学习,不断思考,并且付诸实践,归纳出适合自己的方法。
好代码的特性
- 鲁棒 - 对于各种错误情况和异常的有效处理
- 高效 - 使用尽量少的资源
- 简洁 - 简明易懂,方便别人接手
- 简短 - 代码尽量简短,但是不要牺牲可读性,简洁高于简短
- 可测试 - 使用TDD的思想,测试驱动开发
- 共享 - 抽离重复代码,提高代码的复用性,降低代码开发成本
- 可移植 - 适配各种系统的能力
- 可观测/可监控 - 程序状态的收集、保存、对外输出
- 可运维 - 重点关注成本、效率、稳定性
- 可拓展 - 容量可拓展、功能可拓展
坏代码的例子
- 不好的函数名称
- 不好的变量名称
- 没有注释
- 函数不是单一目的(副作用太多,功能复杂,难以复用)
- 不好的排版
- 无法测试
需求分析和系统设计
重要性
"软件工程师似乎都很忙,但是在错误方向上所做的所有努力并不会产生任何价值,而大部分的加班实际上是在做错误的事情,或者是为了补救错误而努力。"
需求分析
如何描述需求
用寥寥数语勾勒出一个系统的功能
对需求分析的误解
- 需求分析和软件工程师没有关系
- 做需求分析时考虑实现细节
系统设计
要点
架构、模块、接口、数据、关键算法、系统设计思路
设计文档的分类
- 总体设计文档
- 子系统设计文档
- 接口定义文档
- 关键算法说明文档
- 数据表设计文档
系统架构
1. 静 - 系统如何组成,包含哪些子系统,每个子系统有什么功能
2. 动 - 描述各子系统之间是如何联动的
3. 细 - 从不同的角度,更详细地刻画出系统的细节和全貌#### 原则和方法
- 单一目的
- 对外关系清晰
- 重视资源约束
- 根据需求做决策
- 基于模型思考
重视对外接口
对外接口比系统内部实现更重要
对外接口的形态
- 模块对外的函数接口
- 平台对外的api
- 系统间的通信协议
- 系统间存在依赖的数据
设计和修改对外接口的注意事项
保证易用性和向前兼容性
如何写出好代码
代码的沟通价值
代码为人而写,统一代码风格,让每个人阅读代码时能够专注于代码逻辑,而不是格式。
提高个人的表达能力也尤为重要。
模块的设计方法
- 单一目的
- 明确对外接口
- 以数据为中心
错误行为
- 把代码量作为模块划分的标准
- 把所有用到的附加功能都放到util模块中导致模块过大难以维护
- 以执行顺序来划分模块
划分模块的方法
- 数据类的模块
- 过程类的模块
函数的设计方法
类和函数
在允许使用独立函数的语言中,对于和类的成员变量无关的函数,都写为独立的函数
函数描述三要素
- 功能 - 这个函数是做什么的
- 传入参数 - 各个函数的含义和限制条件
- 返回值 - 返回值的各种可能性
控制函数的规模
把函数写的短一些
单入口单出口
代码块编写注意事项
- 适当的行间距,把代码段落区分清楚
- 写代码时应该考虑别人是否能看懂
- 注释不是补出来的,可以先写注释,再写代码
软件开发的命名
准确、易懂
如何支持
系统运营
在用户产品研发中,非常依赖于对用户使用数据的收集和分析,以此来决定后续产品研发和迭代的目标。在系统开发中,数据的收集和分析对于系统的迭代同样非常重要。
如果没有足够的数据收集信息系统等于没有上线
BFE系统的例子
对外暴露了上千个内部状态变量,可以被监控系统抓取,用于集群状态的展示,也用于错误的报警。
使用Web Server,向外展示内部的状态信息,这些状态变量按照类别和扩展模块的单位来组织。
成为优秀软件工程师的三条路径
路径一:学习-思考-实践
Stay hungry, stay foolish.
- 阅读经典图书,研究高质量开源代码,养成学习的习惯
- 保持思考,形成自己的思想体系,方法论
天人合一,谓之真;知行合一,谓之善;情景合一,谓之美。
- 勇于实践,所有的进步都来源于失败的经历和遭遇过的挫折
路径二:知识-方法-精神
在这个知识大爆炸的时代,知识是过时最快的,所以我们不仅要学习知识,而且要学习方法。
对于软件工程师,分析问题、解决问题的能力才是最重要的。
道可道,非常道。
精神层面:独立精神,思想自由,不要随波逐流,对完美的不懈追求
路径三:基础乃治学之根本
非宁静无以致远。 求木之长者,必固其根本;欲流之远者,必浚其源泉。
对于一名软件工程师来说,需要具备的基础能力:
- 计算机基础:数据结构、算法、操作系统、系统结构、计算机网络等
- 软件研发:基础的软件编写方法、软件工程方法、编程思想
- 基本的思考能力和沟通能力
- 研究能力:分析问题、解决问题
代码评审
常见误区
- 不重视代码评审
- 缺乏专门工具
- 通过率过高,没有仔细评论
- 人情方面的顾虑
为什么要做好代码评审
重要意义
- 提升代码质量
- 有助于知识传递
- 节省成本
- 有助于辅导他人编码
- 使团队对顶级开发者更有吸引力
没有做好代码评审的后果
- 代码质量差
- 团队内接手代码困难
- 新人得不到有效辅导,提高速度缓慢
为什么要提升代码质量
- 缺少可复用的代码
- 定位、修复bug困难
- 代码可读性差
- 代码难以维护
为什么要提升编码能力
编码能力对软件工程师个人和团队保持竞争力都具有非常关键的作用
如何做好代码评审
常见问题
- 拼写错误
- 未优化的代码实现
- 不必要的复杂代码
- 重复实现的已经存在的代码逻辑
- 缺少必要的注释
- 缺少必要的单元测试
正确态度
- 对于被评审的代码,评审人应该能够完全看懂
- 评审人对于好的代码应该有正确的认识
- 评审人对代码应有一丝不苟的态度
- 将代码评审放在编写代码同等重要的位置
- 把提升代码质量为最终目标
推荐步骤
- 看清代码全貌,搞清模块划分逻辑,明确模块之间关系
- 查看模块,搞清模块内的关键数据、关键的类和函数
- 查看类和函数内部的逻辑,关注逻辑的正确性、实现的合理性、段落划分的合理性、命名的合理性
对坏代码的简单判断
- 花五分钟都不能看懂的代码
- 需要思考才能看懂的代码
- 需要来回翻屏才能看懂的代码
- 没有空行/注释的代码
代码评审的注意事项
- 建立owner制度
- 综合多种沟通机制,如正规的评审工具、面对面沟通、项目文档等
- 控制节奏,每次提交的代码不要太多,"小步快跑"的思路
- 为评审留出时间
- 不要放过任何一行代码
如何成为好的代码评审人
不仅要发现系统中的bug,还要质疑、提出产品需求,挑战、优化系统架构和实现方式。
查看系统需求分析的质量、接口/函数定义的合理性、模块划分的合理性和系统关键机制的合理性。
从一个独立的视角把系统的需求、设计、实现都综合思考一遍。
自己的代码水平也非常重要。#
"代码的艺术"应用实例
Mini-Spider项目
需求分析
题目说明
mini-spider的程序要求如下:
- 使用Python语言,开发一个迷你定向抓取器
- 实现对种子链接以及抓取网页中所包含链接的抓取
- 如果链接符合特定的模式,则把抓取的网页保存到磁盘中
输入
- 种子文件:一些初始的网站URL地址
- 配置文件:包含最大线程数、最大抓取深度等信息
要求
- 网页存储时每个网页单独存为一个文件,以URL为文件名
- 要求支持多线程并行抓取
功能分析
-
读取配置文件
- 读取程序的主配置文件
- 读取种子文件
-
处理网页
- 根据指定的URL抓取网页
- 解析网页
- 将符合条件的网页保存到磁盘中
-
其它功能
- URL的去重
- 多线程并发执行
软件架构
模块切分
- 配置模块
- URL处理模块
- 网页抓取模块
- 存储模块
- 多线程控制模块
系统架构 将mini-spider定义为一个类,将队列、url_table、crawler等作为成员变量,这样组件与总体之间的关系就非常清楚。
数据共用 任务队列和url_table在类中定义,然后通过crawler构造函数的接口传递给crawler。
Crawler的执行逻辑
绝大多数代码通过逻辑的梳理和提取子函数,都可以控制函数的规模,从而极大降低代码中发生错误的可能性,提高代码的可读性和可维护性。
包含六个主要步骤:
- 从任务队列中获取URL和深度
- 从URL中读取数据
- 把数据保存到文件里
- 检查深度
- 从数据中获取新URL并加入到任务队列
- 任务完成
多线程机制
数据互斥访问 使用锁的机制
临界区注意事项
- 尽量不要执行非常消耗时间的操作
- 不要出现没有捕获的异常
- 如果文件的读取操作不是必须互斥的,就应该把它放在临界区外执行
任务的分发 在多线程程序中,使用队列来分发任务是一种常用的方式,每个crawler只需要持续地从队列中读取任务并处理。
程序的优雅退出 对mini-spider来说,在达到预定的抓取深度,并且没有新的抓取任务后,程序要停止执行。## 项目文档
正确认识项目文档
项目文档的重要作用
- 提高沟通的效率
- 提升对"思考过程"的管理,帮我们厘清问题,挑出重点,深入挖掘
- 没有文档的设计不是设计
项目文档的常见误区
- 写项目文档是在浪费时间,项目时间紧,没时间写
- 没写需求和设计文档就可以开始写程序
- 这是个简单问题,不需要写文档
项目文档的常见问题
- 没有接口设计文档,会导致多人协作出现问题
- 项目文档没写好,多次讨论同样的问题
- 没有系统总体架构文档,了解系统的成本高
- 项目缺少文档,导致新人无从入手
- 项目缺少文档,导致团队内的沟通效率低下
- 项目缺少文档,会造成自己工作上的困难
什么时候需要写项目文档
必须要写的几类文档
- 需求分析文档
- 接口设计文档
- 关键性算法设计文档
- 系统总体架构文档
文档内容选择
凡是不那么"显而易见"的地方都最好留下文档
不仅要留下设计结果,也要留下思考过程
项目文档不是写完代码后补出来的
项目文档是写给谁看的
文档是写给别人看的
可以从以下几方面评价项目文档的质量
- 能否让阅读者在5分钟内看懂
- 能否做到问题清楚、重点突出、逻辑清楚
- 能否做到言之有物
- 能否做到言简意赅
- 能否避免别人的误解
项目文档的基本规范
- 封面:说明文档的所属项目、编号、文档的版本号
- 版本历史说明:修改人、修改日期、修改说明
- 编写说明:文档编写的背景和目的,文档内容的概要说明
- 文档板式
项目文档的编写
编写顺序
自顶向下 首先列出文档的一级标题提纲,再针对每个一级标题列出子问题作为二级,三级标题,之后针对每个标题编写具体的内容。
反刍 感觉不好的地方及时修改
标题拟定
- 标题样式的使用
- 标题拟定
- 经典错误:标题不准确、不使用"标题样式"、对标题不编号
段落编写
段落的格式
- 段首一定要缩进
- 每段不要太长
- 每段的第一句要多用心思
- 每段内多句话之间应该具有一定的逻辑性
让重要的内容更醒目#### 问题划分
注意以下三点:
- 是否是一个独立的问题
- 是否是一个重要的问题
- 子问题间的关联
表述模式
分析和解决问题
- 提出问题
- 分析问题
- 解决问题
提出建议
- 出发点
- 实现的手段
- 工作量的预估
- 收益的预估
系统的设计
- 系统的功能
- 系统的组成
- 系统的行为
程序的设计
- 程序的架构
- 程序中包含的数据
- 程序的模块划分
- 程序模块间的调用关系
- 程序模块的关键函数接口
项目管理
重视项目管理
- 好的管理比好的技术更重要
- 每一名软件工程师都是知识工作者,都是管理者
相关基本概念
项目
项目,是在一定的约束条件下(限定时间,限定资源),具有明确目标的一次任务。
四个基本要素:时间、范围、成本、质量
项目管理
目的是让软件项目在整个软件生命周期内,在预定成本内按期按质地完成,最终交付用户使用。
整个软件生命周期:分析、设计、编码、测试、维护等多个阶段
项目四要素间的关系
在给定的质量要求下,可以在项目的时间、范围、成本间做权衡。锁定1~2个要素,改变另外1~2个要素。
两个误区
- 在预定给定项目目标的前提下,要求项目在指定时间内完成,而不考虑人力资源方面的实际情况
- 在项目目标、时间和范围锁定的情况下,通过降低质量要求完成项目
人和时间并不能简单互换
项目管理的过程和步骤
三个步骤:
- 项目规划和启动
- 项目执行和监控
- 项目总结和回顾
项目规划和启动
常见问题
- 项目参与人的角色不明确
- 没有明确给出项目目标和时间计划
- 对项目资源缺乏准确估计
包含内容
- 项目团队的组建
- 项目基础设施的建立
- 项目规划
- 项目启动会
项目执行和监控
项目周报
- A. 项目的总体计划及对应的进展情况
- B. 上周计划、本周进展和下周计划
- C. 项目的风险和处置
项目总结与回顾
项目总结会
做研究
什么是研究
- 发现问题,解决问题
- 去探索所有的可能性
- 研究是要做创新的事情
如何做好研究
发现问题
如何发现问题
- 综述一个方向的发展现状
- 从自己的亲身体会去发现问题
发现问题的精神
- 要有挑战权威的精神
- 一定不要有"想当然"的思想
分析问题
- 注意概念和逻辑
- 分而治之和分类比较
- 假设和场景
解决问题
- 优先解决重要问题
- 可先尝试解决简单的问题
- 学会做取舍
做好研究的必备素质
做人,做事,做学问
关于做人
- 保持心情愉快,身体和心理的健康,比做学问更重要
- 要时刻注意全面的发展自己
- 哲学和语文很重要,要搞清楚别人在做什么,说清楚自己在干什么
- 保持好奇心、感知力、想象力
关于做事
- 从小事做起,我们的灵感常常来自于小事情,来自细节
- 既要勤奋的工作,也要聪明的工作
- 要有团队精神
- 要加强沟通
关于做学问
古今之成大事业、大学问者,必经过三种之境界:
"昨夜西风凋碧树。独上高楼,望尽天涯路。" 此第一境也。
"衣带渐宽终不悔,为伊消得人憔悴。" 此第二境也。
"众里寻他千百度,蓦然回首,让人却在,灯火阑珊处。" 此第三境也。
没有自由思想,没有独立精神,即不能发扬真理,即不能研究学术。
我们应该能感受到研究带来的快乐,一方面来自研究过程中对自我的提升和超越,另一方面研究过程中与他人交流和切磋也是快乐的。
总结
这篇学习笔记涵盖了软件工程的核心理念和实践方法,从代码质量到系统设计,从项目管理到研究方法,为成为优秀的软件工程师提供了系统性的指导。
关键要点:
- 质量第一:好代码是软件工程的基础
- 系统思维:从需求分析到系统设计的全局观
- 持续学习:学习-思考-实践的成长路径
- 团队协作:代码评审和项目管理的重要性
- 研究精神:发现问题、分析问题、解决问题的能力
Stay hungry, stay foolish. 保持学习的热情和对完美的追求。