程序员修炼之道:从小工到专家
这本书不讲深奥的技术,不谈高谈阔论的思想,也不局限于编程语言,本身只是列出一个个很小的 checklist,引发读者的思考,鼓励读者成为实践者。
如果你愿意,你每次写代码前、写代码时、写代码后,把它当成工具书翻一翻;在闲暇之余 check 一下,反思一下。慢慢地其实就不再需要这本书了。
这本书有一个很关键的词,也是原版书名《The Pragmatic Programmer》的主题词:注重实效(Pragmatic)。我是怎么理解这个词呢?
很重要的一点是思想认知进化。
可能会有很多刚入行的程序员,认为工作就是敲代码,完成任务就可以,“跑起来就行了~”。
但是,不能仅限于此。代码只是一种解决问题的工具,是为了业务工作更加高效;那什么让我们的工作更加高效呢,那就是提高使用工具的效率。这不是说敲键盘的速度更快、更有力,写的代码量更多,而是不仅做到,而且做得更好。我认为至少是以下几个方面去做到的:
思想转变
提升自己
选择正确的、成本更小的实现方式
使用更便捷的工具
写更好的代码,这代码量可能比原来更多,可能比原来更小;但是未来写的一定更少。
注意沟通、注意协作,在项目中关注更多“视野”。系统不是一个人建成的,而是合作出来的。
说句实在的,很多时候,涨薪不是你需要做到更多时候得到的,而是你在原有的工作中已经做到更多,被肯定得到的。
怎么做到更强、涨薪、暴富呢
这本书的序中,有几句话可以提出来看看。
能不能让正确的原则指导正确的行动本身,其实就是区分是否是高手的一个显著标志。
但是,道理我相信大家都懂,但是什么是正确的原则?怎么时刻去指导自己的行动?
记得我上面说过的吗,这本书其实就是一些 checklist,每一条都是很短一句话。这本书很多内容都是诠释这些短句,通过举例让其更容易被理解。
ok,东西有了,那去做到它吧:
我们须要抽象出一些简单的词句和规则,靠记忆和不断地提醒,小规模地内化这些小声音,让这些简单的小声音能够时刻从大脑里跳到耳边,提醒自己。
可以没事翻一翻。去看,去实践,到最后也就不需要它了,因为已经自然而然做到了。
你不应该局限于任何特定的技术,而是应该拥有足够广博的背景和经验基础,以让你能在特定情况下选择好的解决方案。你的背景源自对计算机科学的基本原理的理解,而你的经验来自广泛的实际项目。理论与实践的结合使你强大起来。
后面我提炼一下一些有帮助的内容(仅属于我个人意见),仅供参考。条件允许的话,大家可以看一下原书,内容不算多,值得反复看,细细琢磨。
个人篇
主动思考,保持好奇,提高能力
原文也是三句话:保持好奇心,主动思考自己的工作,不断学习提高自己的技艺(这个词很有意思,对应那局:编程是一门艺术)。
这个我相信大家懂得都懂,这已经是演变为“内卷”了,不得不卷起来。
学习也是讲究计划、方法的。
建议学习计划要有目标,可以将大目标拆解为阶段性小目标,以此保持持续学习。
简单的学习肯定不是看就完事了,阅读、记录、总结、输出是一个流程,第一天看完、第二天复习、周末整理回看加强记忆。
保持思考性阅读,即带着批评性思维。
怎么提问题
遇到问题,有两种极端情况,一是直接找人帮忙,二是自己死磕花了很多时间。
其实都不可取,第一是自己很难印象深刻,积累成自己的经验;后者浪费了很多时间,从而耽误了其他或其他人的工作。
合理方式是先自己思考怎么办,再去找答案;如果花了一段时间没找到答案,但是你也有了一些方案或想法。再去找人求解,让同事帮忙解答,可以节省双方的时间。
也许最终答案和你原先的思考有出入,但是你明白了自己的方案或思考,有哪些是对的,哪些是错的,下次你可以直接达到正确的途径了。
注意怎么问、怎么交流?优先当面交流、其次邮件或文档(我比较倾向于有仪式感的,因为写完一定会看一遍有没有问题)、再者 IM。但是无论哪种都要有结构性的交流,这一点我推荐《金字塔原理》。
写对的代码,写好维护的代码,重要的是遵守规范
熵增定律,软件代码、架构总是朝着无序的方向发展。这要求一般我们需要在设计时、写代码时总是遵守规范,对“破窗户”零容忍。破窗户指的是软件中一些较为微小的低劣设计或糟糕代码。
而一旦这种微小的“漏洞”出现,总是会随着时间发展而造成软件出现难维护、难扩展、难设计,甚至出现真实的漏洞:bug。这有点像“温水煮青蛙”,自认为一点点问题没事的,但是一旦出现这样的懈怠,后面难保不会重复。更麻烦的是,CV 大法好,这样的代码会被无限复制。即使没有 CV,在严格规范带来的重复劳动下, 会有更多的同事选择一起懈怠(真人真事)。
所以,感觉一般来说,期望规范达到 5,最好定到 6。不知道大家公司是不是也是这个样子的。。
一旦出现了这样的问题,那到时候反而需要更大的努力才能解决。
规范、准则、原则,说实在的这种东西做起来真难。程序员的本质是“偷懒”,严格规范带来的额外劳动又是最被厌烦的。
建议是不断要求自己,小步实践,内化为习惯。记住代码质量、软件质量也可以是你需要解决的需求
可能有人会讲需求太多了,忙不过来,哪还有给自己加需求的。这个事情比较讲究,无论是这本书还是《10x程序员工作法》,其实都建议可以通过将需求拆解为小任务,从中可以筛选出不重要或不紧急的将其砍掉。但是这个粒度就见仁见智了。
做变化的“催化剂”
这个点我很喜欢。当你觉得某些东西不合理,可以有更好的方案时,希望推广出去,替代原有的方式。
但是方案的尝试需要时间、成本;即使有方案其他人会觉得,要改动代码好麻烦,收益和坑都不清楚;在某些成规模的公司里,一些技术方案还需要经过技术委员会的评审、批准。
设计出你可以合理要求的东西,好好开发它。一旦完成,就拿给大家看,让他们大吃一惊。然后说:“要是我们增加……可能就会更好。”假装那并不重要。坐回椅子上,等着他们开始要你增加你本来就想要的功能。人们发现,参与正在发生的成功要更容易。让他们瞥见未来,你就能让他们聚集在你周围。
这可能是个有画面的装逼模板。但是哦,这需要推动变化的人,要有更大的视野,看到的不只是自己的代码,而是大家的痛点。
我个人做法是,大了讲,从用户角度出发多思考产品设计,关注整个项目与团队之间的协作;小了讲,看看别人写的代码,提的 MR。
程序、架构设计篇
拒绝重复,选择复用
相信都知道 “DRY-Don’t Repeat Yourself”原则吧。
重复代码、重复设计会造成代码臃肿,增加代码改动的难度与风险(需要修改多个地方)。
尽量避免重复,以下是一般几种情况:
代码片段功能类似:可以提取出工具类
代码模块功能类似:下沉为底层模块
不同语言实现同一功能:抽取出公共服务,用一种语言提供服务;通过插件或代理方式提供接入。
不同开发者实现同一个类似功能:同上。
灵活的架构
在项目迭代中,很可能发生功能改变、数据结构改变,甚至底层资源改变。
在设计之初,可能就需要考虑到代码、架构的灵活性,甚至延伸到集成、部署都是不为过的。
在软件设计领域,有很多种方式去做到这样的灵活,比如解耦、抽象、防腐层、配置化等。
“正交性”,解耦合
高内聚,低耦合的核心就是:分层、分模块、抽象。
模块将核心功能集中起来,通过统一的接口对外提供能力;
模块对外部的依赖,通过依赖抽象,而不是实现进行解耦;而依赖逻辑实现接口,对核心逻辑提供支持。
模块内部的逻辑对依赖它的模块不可知,即使内部逻辑改变,上层模块不需要进行任何改变。
这在面向对象的原则中,可以概括类等于得墨忒耳法则或最少知识原则。
MVP 与原型
这个 MVP 不是你想象的 MVP,他指的是 Minimum Viable Product –最简化可实行产品。
核心观点是为了快速响应市场目的,实现最简化、仅保留核心功能的产品迅速上线,接受用户使用和反馈再进行迭代。
一是避免错过风云变幻的市场热点;二是保证最小的试错成本;三是避免没有必要的浪费,因为一个新的市场很多时候都是全靠大家想象的,容易出现偏差。所以快速投入市场,根据用户反馈进行调整,才能迎合用户和市场。
从研发的角度,功能开发更合理,持续集成环境更加完整,测试验证有了真实数据。当然有了用户反馈,目标更加明确。但是往往对研发团队的要求会更高:
需要快速响应、投入的团队。这个一般不会很大,否则不利于调动。
有积累的资源、方案可供使用或借鉴。设计素材、研发工具、脚手架、解决复杂问题的架构设计方案或工具。
完备的基础设施及自动化环境。持续集成、持续部署的自动化环境,自动化测试、灰度测试、数据分析,能够适应新市场流量突增的服务部署与扩展。
而之前提倡的原型更多的是为了演示,是不能交付给用户的,往往是用完即扔的东西。因为它往往会忽略一个真正产品需要的几个特性:
正确性。我们可以使用假设的数据,只需要验证流程。
完整性。比之 MVP 更简陋,核心功能我们都只需要正确流程即可,不用异常流程。
健壮性。错了直接崩溃也无妨。
风格。
建议为了学习而制作原型。
元数据设计
“把细节赶出去!”
元数据到底是什么?严格地说,元数据是关于数据的数据。最为常见的例子可能是数据库schema或数据词典。
宽泛点说,元数据是任何对应用进行描述的数据——应用该怎样运行、它应该使用什么资源,等等。
核心设计思想就是将抽象放进代码,细节放进元数据,这样的优势在于
它迫使你解除你的设计的耦合,从而带来更灵活、可适应性更好的程序。
它迫使你通过推迟细节处理,创建更健壮、更抽象的设计——完全推迟到程序之外。
易于扩展,适应定制化,可以更加方便响应变化。
任务分解与估算
任务分解与估算相辅相成。任务分解更加有利于你对需求认识的把控,对需求进度的把控,对时间管理的把控。甚至于任务很小,小得即使你被打断了,你既可以暂时搁置别人的打断,开发完任务再响应别人;也可以暂时挂起任务,任务挂起的成本很小,因为本不复杂,可以记录下进度,去响应别人的打断,然后再回来继续开发。
分解的准确性,基于你对业务的理解,对需求的认识,以往经验的积累,对自己清晰的认知,甚至于可以总结出一套分析需求,拆解任务的模型。
程序编码篇
按合约设计(DBC)
它关注的是用文档记载(并约定)软件模块的权利与责任,以确保程序正确性。什么是正确的程序?不多不少,做它声明要做的事情的程序。用文档记载这样的声明,并进行校验,是按合约设计(简称DBC)的核心所在。
而描述这样的声明与校验的方式就是:
前条件(precondition)。调用程序或函数的前提条件必须满足,这是调用者的责任。
后条件(postcondition)。程序或函数结束时的事件或状态,保证它一定会结束的。
类不变项(class invariant)。内部维持一个状态或状态集合的完整性校验,可以用这个校验确保程序运行过程中任意中间态的正确性;也允许中间态是可以违背这个校验的,但是结束时这个校验一定是可以通过的。
而做到这点就是通过在设计时考虑:输入域的范围、边界条件、输出数据格式与范围、输入输出怎么样。
可以通过断言、预处理、提前返回错误和崩溃的方式去保证。
持续重构、小步重构
什么时候都可以重构,甚至越早重构越好。
当你发现重复、耦合、过时的逻辑、性能问题、不恰当的规范时,都可以选择重构。
可以一点点进行改变,而不是一次性全部重构,相信我那是很痛苦的。需要较长的周期,开始没法中断,要么全部完成,要么全部退回;可能重构就是抽取类、方法的 CV 大法,一点点这样的情况会觉得很爽,但是多了就会发现重复劳动的枯燥。而小步重构即使出错了,也可以简单退回,重新设计。
所以注意技巧:小步重构、完善的测试保证重构完也要通过测试。
虽然有建议不要重构时添加新功能,但是如果细化到小的变化,我还是比较建议跟着需求走测试,让其变成日常。关键是养成写更好的代码,持续优化的习惯。
可测试性与测试用例
相信不少和我一样都很痛恨写单元测试。考虑单元测试一般覆盖核心逻辑或复杂逻辑,为什么会让人这么望而却步?
很大程度上是核心逻辑没写好,可能依赖于外部很“重”的实现;可能与其他东西糅合了,导致复杂度变高了。
我个人写可测试的代码,关键是一个核心逻辑的方法是有限、合适数量的输入与输出。这样子,我才能穷举出所有可能的输入以及输出,来编写测试用例。
程序并发与工作流
时间是软件架构的一个经常被忽略的方面,是作为软件自身的一种设计要素的时间的角色:
并发:同一时间发生多个事件
次序:事件在时间中的相对位置
尤其是互联网的开发设计中,并发是一个不可忽视的因子,设计之初就必须要考虑进来;如何防止并发冲突?如何保证高并发?
防止并发冲突,改善并发性,一个可能的原则就是分析工作流,可以通过 UML 活动图去分析。
另外,并发方面的编程,要考虑到多进程、多线程、多协程;服务端架构设计还要考虑高并发,如何扩缩容等问题。
系统性能与复杂度估算
系统性能有时候是很难衡量的,比如 CPU 负载、处理时间,内存负载,磁盘读写等。一般都是通过已有知识和经验能够帮助估算。
除此之外,通过对算法的估算获取一个模糊的认知,即复杂度分析、大 O 表示法。
项目篇
合理的需求
这个比较见仁见智了。也遇到过需求把各个流程写得非常清晰的,也有直接甩出一句话要哪哪哪加个状态显示,样式跟另一个地方一样。真的是,大哥这不是一个东西,没法一样的啊。
所以,需要理解需求,透过现象看本质,从用户的角度看待问题。因为研发接到的需求是产品收到业务的问题后而加工出来的解决方式。产品的想法与研发的想法有时候是不一致的,产品会考虑到设计、交互等等,研发会考虑改动风险、工作量。
但是目标是统一的,解决问题。明白是什么问题,我们可以想是不是只是暂时的,可以简单做;或者是否可以用非技术手段;也许有现成的手段可以方便解决核心诉求。
其次,这样才有可能知道你是要解决一类问题,还是一个特定问题,比如下面的需求表达:
只有指定人员才能查看档案【good】
这样的需求过来,开发人员会设计一套权限管理系统,方便后续扩展
只有领导和人事部门才能查看档案【bad】
这样的需求过来,开发人员可能只是做一个简单的配置,进行前置静态检测
只要需求改变,就可能无法满足,甚至需要修改代码
前者的需求表达,才会促使我们设计时,考虑扩展性、兼容性。从而选择抽象,而不是细节。
有点我觉得很重要,和产品交流时,一定明确词汇的统一。
因为产品说的是 A,但是可能会出现研发会考虑到设计时的英文表达,但是没有完全匹配的词汇,就用了 B。而 B 翻译出来是与 A 不一样的,就导致产品说 A,研发说 B的情况。
要针对场景,有最最明确的词汇,尽量使用业务领域的专有词汇。
更多的准备
如果听完需求还是无法设计,那就再搞明白点;如果设计了方案,还是无法编写代码,那就再想想哪里还有问题和不足。
因为一旦没有思考清楚,很可能导致返工,或者是无用功,就怕就是折腾一两个月后发现前面都做错了!
规范
编写程序规范就是把需求归约到程序员能够接管的程度的过程,最后形成大家都能接受的约定。
这其实很重要,尤其是多人协作的项目。
基础设施、自动化
元数据设计可能需要配置中心;单元测试可能有封装好易用的脚手架;自动化 CI/CD的构建、编译、测试、代码质量检查、构建镜像或 jar、部署;甚至自动化测试、灰度部署测试等。
这个我相信应该都有,或都可以有。
完备的测试、自动化测试
自动化测试,早测试、常测试,测试占编码很大一部分;项目需要单元测试、集成测试;核心系统可能还需要性能测试、压力测试;需要有一定的覆盖度。
文档和注释
文档要维护好,不要留坑啊。说真的,如果你接手一个系统,或入职一家公司,没有文档的话,你想想得多痛苦,只能看代码。嘿嘿,然后你发现连注释都没有,不知道会不会直接甩手走人。
记得上大学时,老师应该是教了先有相关文档,才有程序编写;如果没教,那也逃不了,因为写论文的时候一定得有。
————————————————
版权声明:本文为CSDN博主「游戈程序员」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/jiangxiayouyu/article/details/118277341