程序员修炼之道 阅读笔记
本文是程序员修炼之道的阅读笔记和自己的经历以及思考
提供各种选择,不要找蹩脚的借口
如果你犯错了,那么勇敢的承认它,并且承担后果,而不是找各式各样的借口或者撒谎.如果从一开始就判定 当前的做法可能会有问题,那么应该提出来,并且拒绝为此承担后果,当然,同时应当提供更多的,更好的选择.
不要容忍破窗户
“这代码太搓了,看都不想看到它”, “算了,等有空的时候再来修一下吧” — 错. 一旦发现破窗户,我们应该 立即修掉它,而不是让他继续留着.在日常的软件开发中我们肯定有过类似的想法,但最后的实践证明,几乎不会 再去修.甚至 “TODO = never do”,所以如果我们发现了破窗,或者是闻到了代码的坏味道,应当立即修掉他. 整个项目都是烂的,怎么办?重构或者跳槽.
要做变化的催化剂
记住大背景
每个人都会想,这破玩意儿都这么破了,懒得改,我只要维护好我自己的那一部分就好了.但事实上,也许维护 自己那一部分代码的时候又会这么想:反正大家都写的那么烂,我这里也不用太讲究.如此恶性循环下去,整个 项目就会垮掉.所以我们应当主动承担起带动变化的角色.
- 首先要坚决维护好自己(一个人)开发的项目 - 然后要在发现大项目中的坏味道的时候除掉他 - 至于大背景,则是首先你要确定你所做的对项目是真正有益的
使质量成为需求问题
代码不可能完美,日常的编程中,总是把质量权衡在某个度上,因为,优化是无止境的.当然,这并不意味着写出 质量低下的代码是可以理解和原谅的(所以这个度其实很难把控).
定期为你的只是资产投资
程序员所学的知识其实同样也有时间价值 — 和金融资产一样. — 有的知识从出生到现在都没有过时,例 如学习UNIX相关的知识,有的知识火一阵子就开始走下坡路.
- 定期投资 - 多元化投资 --- 不要把你所有的技术都放在一个篮子里,首先从你所在项目使用到的技术了解起,例如 如果是web开发项目,你所需要知道的知识可能有:HTTP协议,HTML/CSS/JS,熟悉所使用的框架,RPC和JSON, 关系型数据库和ORM,缓存和CDN,负载均衡/单点故障,异步任务,事务,数据库扩展,测试/自动化测试等. 当你了解了这些之后,再去寻找更多的知识. - 管理风险 --- 像投资一样,不能总是追求新兴的东西,但是也不能太过于保守. - 低买高卖 --- 如果你看好一门新出的技术,那么就应该立即学习它,就像在一只你看好的股票刚出的 时候就买入 - 重新评估和平衡 --- 你需要定期的评估,目前所掌握的知识,有哪些需要刷新一下自己的"缓存",是不 是有哪些新的知识需要去了解一下
可以为自己设定一系列目标,例如:
- 每年至少学习一种新语言, 这点我做的还不够好 --- 2015-2017上半年这段时间一直都在用Python, 2017年下半年开始才接触Golang,之后会计划的更好(当然,此前接触的C/C++/Java等都忘了,所以不能算) - 每季度阅读一本技术书籍, 这点我做的比较好,每天我都会看一点书,技术有关或无关,但每季度绝对 不止读一本 - 也要读非技术书籍,这点做的不够好,2017年之前几乎都是只读技术书籍,2017年之后才开始改变 - 上课,这点做的不够好,我几乎不去本地的大学上课,因为比较麻烦...不过我会看一些公开课 - 参加本地组织,这点我做的很不好,几乎不去,主要是参加过几次,觉得太水了,很无聊 - 试验不同的环境,嗯,Windows和Linux我都用,不过,前者娱乐后者工作.IDE极少用. - 跟上潮流,这点我得说,目前没有这样的计划,因为毕业的前几年,给自己定的计划是打造扎实的基础 - 上网,我天天上...
批判的分析你读到的和你听到的
自然,所有人嘴里说出来的都会加以粉饰,你看到的也未必就是事实.在高中的时候我明白了这个道理,所以 给自己的签名一直都是”你的眼睛能看多远”.看一门技术也是如此,批判的看,看到优点,也要看到缺点,知道 什么情况下是最佳使用场景,什么时候不适合.
然而我必须承认,要做到非常准确的识别事实,很难 — 要不然我就不会买错一堆书了,目前get到的买书技能:
- 上评分网看看网民的评论,但是当心,可能是刷出来的,也可能你并没有和他们处在同一个阶段. - 问你身边爱读书,读过很多书的人,求他们给予推荐
你说什么和你怎么说同样重要
我一直认为程序员非常重要的三个能力:硬实力,沟通能力和抽象能力. 记住程序是为人服务的,你最终的沟通 对象是人 — 产品经理或者客户. 你要考虑你的听众是不是听得懂,你可以跟程序员说术语,但是不要跟他们 说术语,不是所有人都知道一切的,你也不知道世界上的一切.其实道理很简单,设身处地站在别人的角度上考虑 一下就好.
DRY原则 — Don’t Repeat Yourself.
不要到处复制粘贴,要不然改起来你就知道了,我的个乖乖,会累死的.把重复的代码和逻辑抽象抽离出来,变成 工具函数(utils)或者类内方法,或者服务类(service),或者微服务等.总而言之,不要重复.
- 强加的重复: 你大概不会想看到
i += 1
后面有注释告诉你这是把i的值加一然后重新赋值给i吧 - 无意的重复: 例如一个人有FirstName和LastName,那么如果存储了FullName,就会是一种重复
- 无耐性的重复: 复制粘贴???看起来是很快,改起来你就知道有多痛苦了.幻想着我这里极少可能性会改. 我只能说,别忘了,还有墨菲定律呢!
- 开发者之间的重复: 我现在所开发的系统里, 不同的部门之间数据库就存着同一套系统不同的编码, 你知道 这有多痛苦吗? :cry:
- 强加的重复: 你大概不会想看到
让复用变得容易
消除无关事物之间的影响
关于这两点,我们听到的最多的词语大概是:高内聚,低耦合;针对接口编程等.微服务最近这么火爆,我个人认为 归功于它的特点 — 强制解耦. 不再是到处都是错综复杂的import然后函数调用,每个子系统维护自己的小 领域就好.同时也就降低了自身的复杂性.
时刻想到,如果我们改了这里,会有几个地方受到影响?正交系统给出的答案应该是一个.
KISS原则也是和这个有关系的, Keep it simple, stupid.
不存在最终决策
用曳光弹找到目标
在现实编程过程中,我们总是要对需求作出一定范围内的界定,以及,假设.假如不这么做的话,需求根本无边 无界,完成不了.但是,与之相悖的,我们的假设做的越多,那么抽象程度就越低,可移植能力就越差.因此,对于 做假设这件事情,只能说我们要做到”谨慎且大胆”.
但是对于一个从来没有做过的大型的系统,应该快速出一个最小的可用的系统然后开始完善而不是无止境的 定好文档等.这样我们能尽早得到反馈,同时也能让客户安心.这和快速出原型很类似,区别在于,如果我们说 快速出原型,那么这个原型很可能会被我们丢弃.
为了学习而制作原型
制作原型,是为了快速体验一下既定方案,从而从中体验到这种方案的优缺点,利弊等.不过通常原型都很丑陋, 因为它只是原型而已.
靠近问题领域编程
DSL很强大,但是对使用它的用户也会有要求.日常很少用到.
估算,以避免发生意外
通过代码对进度表进行迭代
估算,我们日常中很多时候都会遇到这种问题,比如你汇报给上级,这个任务你需要多久来完成. 我们进行估算, 然后,用现实结果来评判它, 并且吸取经验, 在下一次估算的时候用上它, 那么我们的估算将会越来越准确.
书中给了我们一种万能回复: “我等会儿回答你” :).
用纯文本保存知识
利用命令行的力量
用好一种编辑器
你懂的 :)
总是使用源码控制
吃过亏你就知道你需要这个
要修正bug,而不是发出指责
不要恐慌
“select”没有问题
不要假定,要证明
尽管我们会发出吐槽,但是我们会修复这个问题.
修bug要冷静,理性的分析. 而不是胡乱猜测乱碰乱撞.
常见的调试方式:
- 找办法复现bug
- 首先应当怀疑自己写的代码 — 而不是编译器,OS等
- 打印栈
- 打印日志
- 单步跟踪
- 向别人/小黄鸭讲你的代码思路
学习一种文本文本操作语言
其实web开发就是文本操纵…
学习Python和Shell吧
编写能编写代码的代码
如果说通过编写代码来生成代码,我一般都不太赞成,生成的代码通常意味着重复的,meaning-less的.我相信会有更好的办法来解决. 但是如果是从一种代码转换到另一种代码,我是支持的,例如我很喜欢的一个工具:pandoc. 我就用它把Markdown转换成网页或者是pdf.
你不可能写出完美的软件
so it is.
通过合约进行设计
早崩溃
编译过程中的检查其实就是这么一回事儿,早点帮你检查出来总归比上线之后崩溃更好.
如果它不可能发生,用断言确保它不会发生
注意前提,如果它不可能发生. 也就是说, 千万千万千万不要用断言替代错误处理!
将异常用于异常的问题
不要滥用异常
要有始有终
记住自己关闭打开的文件啦,释放申请的内存啦… 这点来说,Golang的defer是个好东西, Python的with(这个context manager)也 是个好东西
使模块之间的耦合减至最少
要配置,不要集成
把抽象放进代码,细节放进元数据
使用配置文件而不是写死在代码里,会让软件获得更大的灵活性,比如MySQL Server提供了各式各样的参数,让用户得以控制Server的 表现和资源占用等.
让应用变成可配置的,就可以获得最大的灵活性.
分析工作流,改善并发性
不要对流程发生的前后顺序做太多假设,而应该看是否有地方可以并发.
用服务进行设计
总是为并发进行设计
总是要考虑,这一段代码是否线程安全?是否可重入?如果能满足并发安全,那么单线程执行也会很安全.
使视图与模型分离
MVC在今天应当是大行其道,背后原因就在于,把视图中的无关部分抽象出来,得以解耦和重用代码.
用黑板协调工作流
不要靠巧合编程
我对自己的要求一直都是,自己写下的每一行代码,都需要知道它背后发生了什么.你要知道自己在做什么,软件是怎么跑起来的,而不是 碰巧跑起来了.
估算你的算法的阶
测试你的估算
if i in ("hello", "world", "this", "is", "a", "string")
和if i in set(["hello", "world", "this", "is", "a", "string"])
的效率有什么区别?心中有估计之后,要去验证这个估算是否是正确的,并且将验证结果加入自己的反馈系统中.
早重构,常重构
等你发现项目已经大到改不动了…那时候重构就很麻烦了.
为测试而设计
测试你的软件,否则你的用户就得测试
这应当是和测试驱动开发类似的思想. 实际上测试是个好东西,只不过有时候写测试确实挺烦的…但是,如果我们经常要作出改动, 那么测试是一件非常好的事情,可以帮助我们省去重复的手工测试,可以帮助我们保证兼容性等.
不要使用你不理解的向导代码
不要搜集需求—挖掘它们
与用户一同工作,这样才能像用户一样思考
确实,真正的弄清楚需求非常重要,要不然迟早要被坑的.可以跟有信用的产品经理一一确定自己理解的需求是否完全对了,并且明确 告知对方不会有改动的机会 — 你知道的,不能培养他们想改就改的习惯.
但是这里也有一个陷阱,就是有些人喜欢把所有的细节都弄清楚,然后再开始动手.做不到的,不要过度陷入细节.
抽象比细节活得更长久
使用项目词汇表
这样就能让跟多人更容易沟通吧,不过我没有用过…
不要在盒子外面思考 — 要找到盒子
面对棘手的问题时,列出所有的可能的途径,然后逐一检查列表中的每一项,然后想为什么不能用某个特定的途径,真的吗?证明一下. 在思考难题的时候,更不能做过多的假设,现有假设也应该逐一检查.
倾听反复出现的疑惑 — 等你准备好再开始
有些事情,do it比describe it更好
不要做形式方法的奴隶
围绕功能,而不是工作职务进行组织
我一直认为,不要拘泥于形式,而要理解真正的内涵.就像敏捷开发,并不要一步一步完全遵循步骤,而理解其真正内涵才是最重要的.
昂贵的工具不一定能制作出更好的设计
其实设计是否良好,还是取决与设计者的品味
不要使用手工流程
早测试,常测试,自动测试
要通过全部测试,编码才算完成
通过”蓄意破坏”测试你的测试
测试覆盖状态,而不是代码覆盖
自动化测试啊哥!千万不要手动点点点…
- 单元测试
- 集成测试
- 边界测试
- 验证和校验
- 性能测试
- 回归测试
- 可用性测试
- 资源耗尽,错误及恢复
- …
一个bug只抓一次
找到一个bug,修复之后记得补上一些测试,这样如果下次再出现,就可以在自动化测试中发现. 而不是修完之后就想,好了,修好了,下 次不会在出现了.
把英语当作另一种编程语言
把文档放在代码里,而不是代码外
温和的超出用户的期望
在你的作品上签名
实际上,版本控制工具已经帮你干了这个事情了. 当然原作是指,可以通过增加自豪感而增加责任感.
更多文章
- socks5 协议详解
- zerotier简明教程
- 搞定面试中的系统设计题
- 用peewee代替SQLAlchemy
- frp 源码阅读与分析(一):流程和概念
- Golang(Go语言)中实现典型的fork调用
- DNSCrypt简明教程
- 一个Gunicorn worker数量引发的血案
- Golang validator使用教程
- Docker组件介绍(一):runc和containerd
- Docker组件介绍(二):shim, docker-init和docker-proxy
- 使用Go语言实现一个异步任务框架
- 协程(coroutine)简介 - 什么是协程?
- SQLAlchemy简明教程
- Go Module 简明教程