mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2025-11-16 14:13:46 +08:00
mod
This commit is contained in:
122
极客时间专栏/10x程序员工作法/综合运用/38 | 新入职一家公司,怎么快速进入工作状态?.md
Normal file
122
极客时间专栏/10x程序员工作法/综合运用/38 | 新入职一家公司,怎么快速进入工作状态?.md
Normal file
@@ -0,0 +1,122 @@
|
||||
<audio id="audio" title="38 | 新入职一家公司,怎么快速进入工作状态?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/d0/10/d0023484fa7c8442eba027784d1eac10.mp3"></audio>
|
||||
|
||||
你好,我是郑晔。
|
||||
|
||||
经过前面几个模块的学习,我们分别领略了各个原则在不同场景下的应用,相信你对于这些原则的理解也上了一个台阶。但实际工作并不会清晰地告诉你,到底该运用哪个原则来解决问题。
|
||||
|
||||
所以,在接下来的三讲中,我挑选了程序员职业生涯中三个非常经典的场景,与你一起看看怎么在实际的工作中运用好已经学习到的这些原则。
|
||||
|
||||
在综合运用这个模块的第一讲,我们就来谈谈,当你加入一家新公司时,应该怎么做。
|
||||
|
||||
IT 行业快速发展,无数的机会涌现了出来,程序员频繁流动是这个行业的一个典型特征。频繁换工作,无论是对公司,还是对个人都是成本很高的一件事。所以,在加入一个新公司时,怎么让自己快速融入,尽快发挥价值,是摆在我们面前的一个重要问题。
|
||||
|
||||
以行业标准来看,我换工作的速度是很低的,但因为之前工作的原因,我需要到不同的公司与不同的人合作,每到一个新公司,工作的内容就是全新的,就如同换了一个新工作一般。因为合作周期有限,我不可能像普通员工入职新公司一样,花几个月时间慢慢熟悉,只能在尽可能短的时间内,快速上手,而且还要提出自己的新想法。
|
||||
|
||||
那我是怎么做的呢?其实,我就是运用这个专栏里提到的各种方法解决这个问题。下面我就来分享一下具体的做法。
|
||||
|
||||
## 运用思考框架
|
||||
|
||||
还记得专栏之初我提出的思考框架吗?我们要问三个问题:
|
||||
|
||||
- Where are we?(我们现在在哪?)
|
||||
- Where are we going?(我们要到哪儿去?)
|
||||
- How can we get there?(我们如何到达那里?)
|
||||
|
||||
先来看第一个问题,如果刚刚加入一家公司,哪怕我们不是一脸懵,也只是对公司业务有一个简单地了解,这是我们的现状。
|
||||
|
||||
第二个问题来看看我们的目标。一般来说,我们都是打算在新公司大展身手,但这个答案太宽泛了,我们还需要进一步细化。在这个公司长远的发展是以后的事,我们还是把第一步的目标制定成能够达到上手工作的程度,比如,能够解决日常的项目问题。
|
||||
|
||||
那接下来,我们就需要回答第三个问题了,怎么才能够达到这个目标呢?我们需要做一个分解。
|
||||
|
||||
你可以回想一下过往的工作经验,要在一个项目上工作起来,先要了解什么呢?很多人的第一反应是技术,我是程序员嘛,当然就是技术优先了。估计大多数人进到项目里,都是一头奔进代码里,然后,从各种细节研究起来。技术肯定是你要了解的,但它不应该是第一位的。
|
||||
|
||||
**技术解决的是“怎么做”的问题,而我们第一个应该了解的问题是“做什么”。**一个软件到底在做什么,能够回答这个问题的就是业务。所以,我们排在第一优先级的事情应该是业务。
|
||||
|
||||
了解业务和技术都只是让你扮演好你个人的角色,但我们通常都是在一个团队内工作的,所以,还要解决好与其他人协作的问题,这就需要我们了解团队本身是如何运作的。
|
||||
|
||||
好,我们已经将大目标做了一个分解,得到了三个小目标:
|
||||
|
||||
- 业务;
|
||||
- 技术;
|
||||
- 团队运作。
|
||||
|
||||
## 从大图景入手
|
||||
|
||||
接下来,我们来针对每一个目标,进一步看看需要了解哪些内容。
|
||||
|
||||
#### 业务
|
||||
|
||||
首先是业务。这是程序员入手新项目时最容易忽略的点。在这个专栏中,我在不同的模块中都说到了知识结构的重要性,没有结构的知识是零散的。所以,不管做任何项目,都要先从大图景入手。只有了解了大图景,各种知识才能各归其位。
|
||||
|
||||
**对于一个普通的程序员来说,业务就是这个大图景。**
|
||||
|
||||
如果你了解了业务,你自己就可以推演出基本的代码结构。但反过来,如果让你看了代码,从中推演出业务,那几乎是不可能的。
|
||||
|
||||
事实上,每次了解到一个业务,我都会在脑子中过一下,如果是我做这个业务,我会怎么做。这样一来,我就会先在整体上有一个预判,后面再对应到实际的代码上,就不会那么陌生了。
|
||||
|
||||
要了解业务,我一般都会请人给我讲一下,这个业务是做什么的,解决什么样的问题,具体的业务流程是什么样子的,等等。
|
||||
|
||||
在初期的了解中,我并不会试图弄懂所有的细节,因为我的目标只是建立起一个基本的框架,有了这个初步的了解,后续再有问题,我就知道该从哪里问起了。
|
||||
|
||||
理论上,了解业务是每个程序员都该做的事,但事实上,这也常常是出问题的地方。在请别人给我讲解业务的过程中,我发现,很多人是分不清业务和技术的,经常把二者混在一起讲。如果你跟着他的思路走,很容易就会陷入到对细节的讨论中。
|
||||
|
||||
所以,了解业务时,一定要打起精神,告诉自己,这个阶段,我要了解的只是业务,千万别给我讲技术。
|
||||
|
||||
#### 技术
|
||||
|
||||
了解完业务,就该到技术了。这是程序员最喜欢的话题。但即便是了解技术,也要有个顺序,所以,我们先从宏观内容开始。第一个问题就是这个系统的技术栈:Java、JavaScript 还是.NET,这样,我就可以对用到的工具和框架有个大致的预期。
|
||||
|
||||
接下来是系统的业务架构,这个系统包含了哪些模块,与哪些外部系统有交互等等。最好能够有一张或几张图将架构展现出来。现实情况是,不少项目并没有现成的图,那就大家一起讨论,在白板上一起画一张出来,之后再来慢慢整理。
|
||||
|
||||
有了一个初步的体系,接下来,就可以稍微深入一些。
|
||||
|
||||
我会选择从外向内的顺序了解起。首先是外部,这里的外部包括两个部分:
|
||||
|
||||
- 这个系统对外提供哪些接口,这对应着系统提供的能力;
|
||||
- 这个系统需要集成哪些外部系统,对应着它需要哪些支持。
|
||||
|
||||
一旦涉及到与外部打交道,就涉及到外部接口是什么样子的,比如,是用 REST 接口还是 RPC(Remote Procedure Call,远程方法调用) 调用,抑或是通过 MQ(Message queue,消息队列)传递消息。
|
||||
|
||||
不要简单地认为所有接口都是你熟悉的,总有一些项目会采用不常见的方式,比如,我曾见过有系统用 FTP 做接口的。
|
||||
|
||||
所有这些都相当于信息承载方式,再进一步就是了解具体的信息是什么格式,也就是协议。
|
||||
|
||||
今天常见的协议是 JSON 格式,或者是基于某个开源项目的二进制编码,比如:[Protocol Buffers](http://developers.google.com/protocol-buffers/)、[Thrift](http://thrift.apache.org) 等等。一些有年头的系统可能会采用那时候流行的协议,比如:XML;有一些系统则采用自己特定领域的协议,比如,通信领域有大量3GPP 定义的协议。
|
||||
|
||||
一般来说,从外部接口这件事就能看出一个项目所处的年代,至少是技术负责人对技术理解的年代。
|
||||
|
||||
了解完外部,就该了解内部了。了解内部系统也要从业务入手,对应起来就是,这个系统由哪些模块组成,每个模块承担怎样的职责。如果系统已经是微服务,每个服务就应该是一个独立的模块。
|
||||
|
||||
通常这也是一个发现问题的点,很多系统的模块划分常常是职责不清的,因此会产生严重的依赖问题。在前面的内容中,我多次提到限界上下文,用限界上下文的视角衡量这些模块,通常会发现问题,这些问题可以成为后续工作改进的出发点。
|
||||
|
||||
业务之后是技术,对应着我需要了解分层。前面说过,[分层结构反映着系统的抽象。](http://time.geekbang.org/column/article/88309)我希望了解一个模块内部分了多少个层,每个层的职责是什么。了解了这些对系统的设计,也就对系统有了一个整体的认识。
|
||||
|
||||
设计之后,就到了动手的环节,但还不到写代码的时候。我会先从构建脚本开始,了解项目的常用命令。我预期从版本控制里得到的是一个可以构建成功的脚本,如果不是这样,我就知道哪里需要改进了。
|
||||
|
||||
最后才是代码,比如,代码的目录结构、配置文件的位置、模块在源码上的体现等等,这是程序员最熟悉的东西,我就不多说了。作为初步的接触,了解基本的东西就够了,代码是我们后期会投入大量精力的地方,不用太着急。
|
||||
|
||||
#### 团队运作
|
||||
|
||||
最后,我们还要了解一下团队运作。同样从外部开始,这个团队有哪些外部接口,比如,需求是从哪来的,产品最终会由谁使用,团队需要向谁汇报。如果有外部客户,日常沟通是怎么安排的。
|
||||
|
||||
再来就是内部的活动,一方面是定期的活动,比如,站会、回顾会议、周会,这些不同活动的时间安排是怎样的;另一方面是团队的日常活动,比如,是否有每天的代码评审、是否有内部的分享机制等等。
|
||||
|
||||
通过了解这些内容,基本上可以大致判断出一个团队的专业程度,也可以知道自己需要帮助的时候,可以找谁帮忙,为自己更好地融入团队打下基础。
|
||||
|
||||
你也许会问,了解这么多东西需要很长时间吧?其实不然,因为只需要从整体上有认知,如果有人很清楚团队现状的话,你可以去请教,也许一天就够了,这也是我往往能够快速上手的原因。接下来,就该卷起袖子干活了!
|
||||
|
||||
## 总结时刻
|
||||
|
||||
我给你介绍了怎么把前面学到的知识运用在了解一个项目上,按照业务、技术和团队运作三个方面去了解。
|
||||
|
||||
大多数程序员习惯的工作方式,往往是从细节入手,很难建立起一个完整的图景,常常是“只见树木不见森林”,而我的方式则是**从大到小、由外而内**,将要了解的内容层层分解,有了大图景之后,很容易知道自己做的事情到底在整体上处于什么样的位置。我把上面的内容总结了成一份供你参考。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/6e/e7/6e2248978ff0b4c8957925792292f2e7.jpg" alt="">
|
||||
|
||||
附赠一点小技巧:使用“行话”。在交流的过程中,学习一点”行话“。这会让人觉得你懂行,让你很快得到信任,尽早融入团队。
|
||||
|
||||
如果今天的内容你只能记住一件事,那请记住:**了解一个项目,从大图景开始。**
|
||||
|
||||
最后,我想请你分享一下,你在入职一个新公司遇到过哪些困难呢?欢迎在留言区写下你的想法。
|
||||
|
||||
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。
|
||||
110
极客时间专栏/10x程序员工作法/综合运用/39 | 面对遗留系统,你应该这样做.md
Normal file
110
极客时间专栏/10x程序员工作法/综合运用/39 | 面对遗留系统,你应该这样做.md
Normal file
@@ -0,0 +1,110 @@
|
||||
<audio id="audio" title="39 | 面对遗留系统,你应该这样做" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/02/0e/02f57380fce929f7c50e35d74825210e.mp3"></audio>
|
||||
|
||||
你好,我是郑晔。
|
||||
|
||||
在上一讲中,结合着“新入职一家公司”的场景,我给你讲了如何在具体情况下应用我们前面学到的知识。这一讲,我们再来选择一个典型的实际工作场景,将所学综合应用起来。这个场景就是面对遗留系统。
|
||||
|
||||
在《[34 | 你的代码是怎么变混乱的?](http://time.geekbang.org/column/article/87845)》中,我给你讲了代码是会随着时间腐化的,无论是有意,还是无意。即便是最理想的场景,代码设计得很好,维护得也很精心,但随着技术的不断升级进步,系统也需要逐步升级换代。
|
||||
|
||||
比如,我们一直认为电信是一个独特的领域,与 IT 技术是完全独立的,学好 CT(Communication Technology,通信技术)就可以高枕无忧了。但随着 IT 技术的不断发展,今天的电信领域也开始打破壁垒,拥抱 IT 技术,提出了 ICT 的概念(Information and Communications Technology,信息通信技术)。
|
||||
|
||||
所以,无论怎样,系统不断升级改造是不可避免的事。问题是,你连自己三个月前写的代码都不愿意维护,那当面对庞杂的遗留系统时,你又该何去何从呢?
|
||||
|
||||
很多人的第一直觉是,我把系统重写一下就好了。不经思考的重写,就像买彩票一样,运气好才能写好,但大多数人没有这么好运气的,我们不能总指望买彩票中大奖改变生活。那有什么稍微靠谱的一点的路呢?
|
||||
|
||||
## 分清现象与根因
|
||||
|
||||
面对庞大的遗留系统,我们可以再次回到思考框架上寻找思路。
|
||||
|
||||
- Where are we?(我们现在在哪?)
|
||||
- Where are we going?(我们要到哪儿去?)
|
||||
- How can we get there?(我们如何到达那里?)
|
||||
|
||||
第一个问题,面对遗留系统,我们的现状是什么呢?
|
||||
|
||||
我在这个专栏前面的部分,基本上讨论的都是怎么回答目标和实现路径的问题。而对于“现状”,我们关心的比较少。因为大多数情况下,现状都是很明显的,但这一次不一样。也许你会说,有什么不一样,不就是遗留系统,烂代码,赶紧改吧。但请稍等!
|
||||
|
||||
请问,遗留系统和烂代码到底是不是问题呢?其实并不是,**它们只是现象,不是根因。**
|
||||
|
||||
在动手改动之前,我们需要先分析一下,找到问题的根因。比如,实现一个直觉上需要两天的需求,要做两周或更长时间,根因是代码耦合太严重,改动影响的地方太多;再比如,性能优化遇到瓶颈,怎么改延迟都降不下来,根因是架构设计有问题,等等。
|
||||
|
||||
所以,最好先让团队坐到一起,让大家一起来回答第一个问题,现状到底是什么样的。还记得我在《[25 | 开发中的问题一再出现,应该怎么办?](http://time.geekbang.org/column/article/83841)》中提到的复盘吗?这就是一种很好的手段,让团队共同确认现状是什么样子的,找到根因。
|
||||
|
||||
为什么一定要先做这个分析,直接重写不就好了?因为如果不进行根因分析,你很难确定问题到底出在哪,更关键的是,你无法判断重写是不是真的能解决问题。
|
||||
|
||||
如果是架构问题,你只进行模型的调整是解决不了问题的。同样,如果是模型不清楚,你再优化架构也是浪费时间。所以,我们必须要找到问题的根源,防止自己重新走上老路。
|
||||
|
||||
## 确定方案
|
||||
|
||||
假定你和团队分析好了遗留系统存在问题的根因,顺利地回答了第一个问题。接下来,我们来回答第二个问题:目标是什么。对于遗留系统而言,这个问题反而是最好回答的:重写某些代码。
|
||||
|
||||
你可能会问,为什么不是重构而是重写呢?以我对大部分企业的了解,如果重构能够解决的问题,他们要么不把它当作问题,要么早就改好了,不会让它成为问题。所以我们的目标大概率而言,就是要重写某些代码。
|
||||
|
||||
但是,在继续讨论之前,我强烈建议你,**先尝试重构你的代码,尽可能在已有代码上做小步调整,不要走到大规模改造的路上,因为重构的成本是最低的。**
|
||||
|
||||
我们真正的关注点在于第三个问题:怎么做?我们需要将目标分解一下。
|
||||
|
||||
要重写一个模块,这时你需要思考,怎么才能保证我们重写的代码和原来的代码功能上是一致的。对于这个问题,唯一靠谱的答案是测试。对两个系统运行同样的测试,如果返回的结果是一样的,我们就认为它们的功能是一样的。
|
||||
|
||||
不管你之前对测试是什么看法,这个时候,你都会无比希望自己已经有了大量的测试。如果没,你最好是先给这个模块补测试。因为只有当你构建起测试防护网了,后续的修改才算是走在坚实的道路上。
|
||||
|
||||
说到遗留代码和测试,我推荐一本经典的书:Michael Feathers 的《[修改代码的艺术](http://book.douban.com/subject/2248759/)》(Working Effectively with Legacy Code),从它的英文名中,你就不难发现,它就是一本关于遗留代码的书。如果你打算处理遗留代码,也建议你读读这本书。
|
||||
|
||||
在2007年,我就给这本书写了一篇[书评](http://book.douban.com/review/1226942/),我将它评价为“这是一本关于如何编写测试的书”,它会教你如何给真实的代码写测试。
|
||||
|
||||
这本书对于遗留系统的定义在我脑中留下了深刻印象:遗留代码就是没有测试的代码。这个定义简直就是振聋发聩。按照这个标准,很多团队写出来的就是遗留代码,换言之,自己写代码就是在伤害自己。
|
||||
|
||||
有了测试防护网,下一个问题就是怎么去替换遗留系统,答案是分成小块,逐步替换。你看到了,这又是任务分解思想在发挥作用。
|
||||
|
||||
我在《[36 | 为什么总有人觉得5万块钱可以做一个淘宝?](http://time.geekbang.org/column/article/88764)》中提到,淘宝将系统改造成 Java 系统的升级过程,就是将业务分成若干的小模块,每次只升级一个模块,老模块只维护,不增加新功能,新功能只在新模块开发,新老模块共用数据库。新功能上线,则关闭老模块对应功能,所有功能替换完毕,则老模块下线。
|
||||
|
||||
这个道理是普遍适用的,差别只是体现在模块的大小上。如果你的“小模块”是一个系统,那就部署新老两套系统,在前面的流量入口做控制,逐步把流量从老系统转到新系统上去;如果“小模块”只在代码层面,那就要有一段分发的代码,根据参数将流程转到不同的代码上去,然后,根据开发的进展,逐步减少对老代码的调用,一直到完全不依赖于老代码。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/35/8c/35b5beb135cd01e701a78df559c4e38c.jpg" alt="">
|
||||
|
||||
这里还有一个小的建议,按照分模块的做法,将新代码放到新模块里,按照新的标准去写新的代码,比如,测试覆盖率要达到100%,然后,让调用入口的地方依赖于这个新的模块。
|
||||
|
||||
最后,有了测试,有了替换方案,但还有一个关键问题,新代码要怎么写?
|
||||
|
||||
要回答这个问题,我们必须回到一开始的地方,我们为什么要做这次调整。因为这个系统已经不堪重负了,那我们新做的修改是不是一定能解决这个问题呢?答案是不好说。
|
||||
|
||||
很多程序员都会认为别人给留下的代码是烂摊子,但真有一个机会让你重写代码,你怎么保证不把摊子弄烂?这是很多人没有仔细思考过的问题。
|
||||
|
||||
如果你不去想这个问题,即便今天你重写了这段代码,明天你又会怨恨写这段代码的人没把这段代码写好,只不过,这个被抱怨的人是你自己而已。
|
||||
|
||||
要想代码腐化的速度不那么快,一定要在软件设计上多下功夫。**一方面,建立好领域模型,另一方面,寻找行业对于系统构建的最新理解。**
|
||||
|
||||
关于领域模型的价值,我在专栏前面已经提到过不少次了。有不少行业已经形成了自己在领域模型上的最佳实践,比如,电商领域,你可以作为参考,这样可以节省很多探索的成本。
|
||||
|
||||
我们稍微展开说说后面一点,“寻找行业中的最新理解”。简言之,我们需要知道现在行业已经发展到什么水平了。
|
||||
|
||||
比如说,今天做一个大访问量的系统,我们要用缓存系统,要用 CDN,而不是把所有流量都直接转给数据库。而这么做的前提是,内存成本已经大幅度降低,缓存系统才成为了标准配置。拜 REST 所赐,行业对于 HTTP 的理解已经大踏步地向前迈进,CDN 才有了巨大的进步空间。
|
||||
|
||||
而今天的缓存系统已经不再是简单的大 Map,有一些实现得比较好的缓存系统可以支持很多不同的数据结构,甚至支持复杂的查询。从某种程度上讲,它们已经变成了一个性能更好的“数据库”。
|
||||
|
||||
有了这些理解,做技术选型时,你就可以根据自己系统的特点,选择适合的技术,而不是以昨天的技术解决今天的问题,造成的结果就是,代码写出来就是过时的。
|
||||
|
||||
前面这个例子用到的是技术选型,关于“最新理解”还有一个角度是,行业对于最佳实践的理解。
|
||||
|
||||
其实在这个专栏里,我讲的内容很多都是各种“最佳实践”,比如,要写测试,要有持续集成,要有自动化等等,这些内容看似很简单,但如果你不做,结果就是团队很容易重新陷入泥潭,继续苦苦挣扎。
|
||||
|
||||
既然选择重写代码,至少新的代码应该按照“最佳实践”来做,才能够尽可能减缓代码腐化的速度。
|
||||
|
||||
总之,**改造遗留系统,一个关键点就是,不要回到老路上。**
|
||||
|
||||
## 总结时刻
|
||||
|
||||
我们把前面学到的各种知识运用到了“改造遗留系统”上。只要产品还在发展,系统改造就是不可避免的。改造遗留系统,前提条件是要弄清楚现状,知道系统为什么要改造,是架构有问题,还是领域模型混乱,只有知道根因,才可能有的放矢地进行改造。
|
||||
|
||||
改造遗留系统,我给你几个建议:
|
||||
|
||||
- 构建测试防护网,保证新老模块功能一致;
|
||||
- 分成小块,逐步替换;
|
||||
- 构建好领域模型;
|
||||
- 寻找行业中关于系统构建的最新理解。
|
||||
|
||||
如果今天的内容你只能记住一件事,那请记住:**小步改造遗留系统,不要回到老路上。**
|
||||
|
||||
最后,我想请你分享一下,你有哪些改造遗留系统的经验呢?欢迎在留言区分享你的做法。
|
||||
|
||||
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。
|
||||
118
极客时间专栏/10x程序员工作法/综合运用/40 | 我们应该如何保持竞争力?.md
Normal file
118
极客时间专栏/10x程序员工作法/综合运用/40 | 我们应该如何保持竞争力?.md
Normal file
@@ -0,0 +1,118 @@
|
||||
<audio id="audio" title="40 | 我们应该如何保持竞争力?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/69/9f/69d72c90040f9090f6a70f70d8c0809f.mp3"></audio>
|
||||
|
||||
你好,我是郑晔。
|
||||
|
||||
在前面两讲,我结合着两个程序员要直接面对的场景,讨论了如何综合运用前面学习到的知识,这一讲的内容可能不涉及到实际的应用场景,但与每个人的发展息息相关。我想谈谈如何走好程序员这条路。
|
||||
|
||||
## 焦虑的程序员
|
||||
|
||||
让我们再次用思考框架分析一下问题。首先,现状是什么?关于这个问题,我并不打算讨论个体,因为每个人的情况千差万别,我准备从整体入手。
|
||||
|
||||
IT 行业是一个快速发展变化的行业,一方面,我们不断地看到有人快速取得成功,另一方面,我们也听到了许多充满焦虑的声音。获得大的成功总是一个小概率事件,大多数人面对的还是日常的柴米油盐。
|
||||
|
||||
**我们的焦虑来自于对未来的不确定性,而这种不确定性是一个特定时代加上特定行业的产物。**
|
||||
|
||||
如果把时间倒回到上个世纪80年代之前,虽然当时的生活条件一般,但很少有人会为未来的发展焦虑,因为那时候,人们可以清晰地看到自己未来的人生,尽管那种人生可能是平淡的。
|
||||
|
||||
但今天的我们处在一个人类历史上少有的快速发展的时代,我们看不清以后的人生,大脑却还停留在上一代人的思维习惯上。
|
||||
|
||||
IT 行业在国内的大发展也就最近20多年的事,行业里很少有走过完整职业生涯的程序员。也正是因为如此,我们经常会产生了各种焦虑:
|
||||
|
||||
- 我刚刚入行时,有人问,程序员能做到30岁吗?
|
||||
- 我快30岁时,有人问,35岁还能做程序员吗?
|
||||
- 我35岁时,讨论变成了40岁的程序员该怎么办。
|
||||
|
||||
估计等国内有越来越多的程序员走完了整个职业生涯,就会有人关心,程序员退休之后的生活应该是什么样子了。
|
||||
|
||||
从长期来看,只要生活中还有需要用自动化解决的问题,程序员这个群体还是很有前景的。但随着时间的推移,程序员这个职业的溢价也会越来越低,单纯凭借身处这个行业就获得好发展的可能性也越来越低,想让自己的职业生涯走得更顺畅,还需要找到更好的目标,不断努力。
|
||||
|
||||
## 成为 T 型人
|
||||
|
||||
我们再来回答下一个问题:目标是什么。也许这时候,每个人脑子里想到的职业发展路线都不一样,但我准备用一个统一的目标回答你:成为 T 型人。
|
||||
|
||||
**什么叫 T 型人?简言之,一专多能。**
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a9/19/a9274fd47bf59fd4d795e7e319616b19.jpg" alt="">
|
||||
|
||||
**有了“一专”,“多能”才是有意义的,否则,就是低水平重复,而这正是很多人职业生涯不见起色的真正原因。**
|
||||
|
||||
**这里的“专”不是熟练,而是深入。**你可能是个有着10年丰富经验的程序员,但实际上只不过是重复了10年解决同样难度的问题而已,这根本就不算深入,也就没有做到真正意义上的“一专”。
|
||||
|
||||
你会发现很多优秀的人,在很多方面都会很优秀,这是“一专”带来的触类旁通。
|
||||
|
||||
当你有了“一专”,拓展“多能”,就会拥有更宽广的职业道路。比如,我拥有了深厚的技术功底,通晓怎么做软件:
|
||||
|
||||
- 如果还能够带着其他人一起做好,就成了技术领导者。
|
||||
- 如果能够分享技术的理解,就有机会成为培训师。
|
||||
- 如果能够在实战中帮助别人解决问题,就可以成为咨询师。
|
||||
|
||||
反过来,当你有了“多能”,也可以拓宽你的视野,帮你认清自己的“一专”怎样更好地发挥价值,而不是狭隘地认为自己有了技术,就已经天下尽在掌握了。视野窄,缺乏大局观,也成为了许多程序员再进一步的阻碍。事实上,这个专栏里的很多内容都是帮你打开“多能”的视角。
|
||||
|
||||
也许你会说,我在公司已经独当一面了,应该算有“一专”了吧?但我想说的是,可能还不够。只做一个公司的专家,受一个公司的波动影响太大,而成为行业的专家,才会降低自己职业生涯的风险。
|
||||
|
||||
有时,我在面试时会问候选人这样一个问题:“如果让你在一次技术大会上做分享,你会讲什么呢?”我真正的问题是,以行业标准衡量,你觉得你在哪个方面是专家呢?
|
||||
|
||||
大多数人从来没有思考过这个问题,他们只是日常在完成自己的工作,即便在某一方面已经做得很不错了,但依然算不上专家,因为他们缺乏深度思考。
|
||||
|
||||
比如,你非常熟悉 Kafka,知道它的各种参数,也读过它的实现原理。但如果我问你,Kafka 为什么要把自己定位成一个分布式流平台,它要想成为一个流平台,还要在哪方面做得更好?你的答案是什么呢?
|
||||
|
||||
这其中的差别就是,前面所谓的熟悉,只是熟悉别人的思考结果,而后面则是一个没有现成答案的东西。学习微积分是有难度,但同发明微积分相比,难度根本不在一个层次上。当然,我不是说你要熟悉所有工具的发展过程,而是自己要在一个特定的方面拥有深度的思考。
|
||||
|
||||
也许你会说,这个要求实在是太高了吧!没错,这确实是一个很高的要求。但“取法于上,仅得为中;取法于中,故为其下。”
|
||||
|
||||
其实,很多人的焦虑就源自目标太低,找不到前进的动力。给自己定下一个可以长期努力的目标,走在职业的道路上才不致于很快丧失动力。
|
||||
|
||||
## 在学习区成长
|
||||
|
||||
现在我们来回答第三个问题,怎么达到目标。既然要朝着行业中的专家方向努力,那你就得知道行业中的专家是什么样。我的一个建议是,向行业中的大师学习。
|
||||
|
||||
你或许会说,我倒是想向大师学习,但哪有机会啊!好在 IT 行业中的许多人都是愿意分享的,我们可以读到很多大师级程序员分享的内容。
|
||||
|
||||
我在入行的时候,有幸读了很多经典之作,比如,出身贝尔实验室的很多大师级程序员的作品,诸如《[C 程序设计语言](http://book.douban.com/subject/1139336/)》《[程序设计实践](http://book.douban.com/subject/1173548/)》、《[Unix 编程环境](http://book.douban.com/subject/1033144/)》等,还有一些像 Eric Raymond 这样沉浸编程几十年的人写出的作品,诸如《[Unix 编程艺术](http://book.douban.com/subject/1467587/)》,以及前面提及的 Kent Beck、Martin Fowler 和 Robert Martin 等这些人的作品。
|
||||
|
||||
读这些书的一个好处在于,你的视野会打开,不会把目标放在“用别人已经打造好的工具做一个特定的需求”,虽然这可能是你的必经之路,但那只是沿途的风景,而不是目标。
|
||||
|
||||
接下来,我们要踏上征程,怎么才能让自己的水平不断提高呢?我的答案是,找一个好问题去解决,解决了一个好的问题能够让你的水平快速得到提升。什么是好问题?就是比你当前能力略高一点的问题,比如:
|
||||
|
||||
- 如果你还什么都不会,那有一份编程的工作就好。
|
||||
- 如果你已经能够写好普通的代码,就应该尝试去编写程序库。
|
||||
- 如果实现一个具体功能都没问题了,那就去做设计,让程序有更好的组织。
|
||||
- 如果你已经能完成一个普通的系统设计,那就应该去设计业务量更大的系统。
|
||||
|
||||
为什么要选择比自己水平高一点的问题?这与我们学习成长的方式有关。Noel Tichy 提出了一个“学习区”模型,如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/62/b1/6236b2bd674fe1ff0edcd9485755b8b1.jpg" alt="">
|
||||
|
||||
- 最内层是舒适区(Comfort Zone),置身其中会让人感觉良好,但也会因为没有挑战,成长甚微,你可以把它理解成做你最熟悉的事情。
|
||||
- 最外层是恐慌区(Panic Zone),这是压力极大的地方,完全超出了你的能力范围,你在其中只会感到无比的焦虑。
|
||||
- 中间的是学习区(Learning Zone),事情有难度,又刚好是你努力一下可以完成的,这才是成长最快的区域。
|
||||
|
||||
根据这个模型,只有一直身处学习区才能让人得到足够的成长,所以,我们应该既选择比自己能力高一点的问题去解决,不要总做自己习惯的事,没有挑战,也不要好大喜功,一下子把自己的热情全部打散。
|
||||
|
||||
在学习区成长,就不要满足于当前已经取得的成绩,那已经成为你的舒适区。因为我们有远大的目标在前面指引,完成日常的工作只不过是个人成长路上的台阶。
|
||||
|
||||
也许你会说,我的工作不能给我个人成长所需的机会,怎么办呢?实际上,别人只会关心你是否完成工作,成长是自己的事情,很多机会都要靠自己争取,前面提到的那些具体做法完全是你可以在工作范围内,自己努力的事情。
|
||||
|
||||
如果你当前的工作已经不能给你提供足够好的问题,那就去寻找一份更有挑战性的工作。在 IT 行业,跳槽似乎是一件很常见的事,但很多人跳槽的时候,并不是以提升自己为目标的。造成的结果是,不断地做同一个层面的工作,自然也就很难提升自己的水平。
|
||||
|
||||
为什么程序员都愿意到大厂工作?因为那里有高水平的人和好的问题。但如果只是到大厂去做低水平的事,那就是浪费时间了。所以,即便你真的想到大厂工作,与谁一起工作,做什么事,远比进入大厂本身要重要得多。
|
||||
|
||||
如果你真的能够不断向前进步,迟早会遇到前面已经没有铺就好的道路,这时候,就轮到你创造一个工具给别人去使用了。比如,2012年,我在项目中受困于集成问题,却找不到一个我想要的、能在单元测试框架里用的模拟服务器,于是,我写了[ Moco](http://github.com/dreamhead/moco)。
|
||||
|
||||
最后,我还想鼓励你分享所得。我在《[28 | 结构化:写文档也是一种学习方式](http://time.geekbang.org/column/article/84663)》中和你说过,输出是一种将知识连接起来的方式,它会让人摆脱固步自封,也会帮你去创造自己的行业影响力,机会会随着你在行业中的影响力逐渐增多,有了行业影响力,你才有资格成为行业专家。
|
||||
|
||||
当你成为了一个行业级别的专家,就可以在这条路上一直走下去,而不必担心自己是不是拼得过年轻人了,因为你也在一直前进!
|
||||
|
||||
## 总结时刻
|
||||
|
||||
程序员是一个充满焦虑的群体,焦虑的本质是对未来的不确定。工作在这个时代的程序员是一个特殊的群体,一方面,这个大时代为我们创造了无数的机会,另一方面,因为程序员是一个新的行业,所以,很多人不知道未来是什么样子的,焦虑颇深。
|
||||
|
||||
从目前的发展来看,IT 行业依然是一个非常有前景的行业,但想在这条路上走好,需要我们成为 “T ”型人才,也就是“一专多能”。一专多能的前提是“一专”,让自己成为某个方面的专家。这个专家要放在行业的标准去看,这才能降低因为一个公司的波动而造成的影响。
|
||||
|
||||
成为行业专家,要向行业的大师学习,给自己定下一个高的目标,然后是脚踏实地,找适合自己的问题去解决,让自己一直在学习区成长。
|
||||
|
||||
如果今天的内容你只能记住一件事,那请记住:**在学习区工作和成长。**
|
||||
|
||||
最后,我想请你分享一下,你有哪些保持自己竞争力的心得呢?欢迎在留言区写下你的想法。
|
||||
|
||||
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。
|
||||
253
极客时间专栏/10x程序员工作法/综合运用/划重点 | “综合运用”主题内容的全盘回顾.md
Normal file
253
极客时间专栏/10x程序员工作法/综合运用/划重点 | “综合运用”主题内容的全盘回顾.md
Normal file
@@ -0,0 +1,253 @@
|
||||
<audio id="audio" title="划重点 | “综合运用”主题内容的全盘回顾" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/00/c0/009d4185fa58cd570b1bb438bfe9f5c0.mp3"></audio>
|
||||
|
||||
你好,我是郑晔。
|
||||
|
||||
又到了我们划重点的时间了,因为篇幅关系,“综合运用”这个模块最为短小精悍。
|
||||
|
||||
在这个模块中,我们把前面学到的各种知识综合起来,运用在实际的工作场景中,让你知道这些内容并不是一个个孤立的实践,在实际工作中,唯有将它们结合起来,才能发挥最大功效。
|
||||
|
||||
## 重点复习
|
||||
|
||||
在这个模块中,我们学习到了一些新知识。
|
||||
|
||||
<li>
|
||||
**“学习区”学习模型**
|
||||
<ul>
|
||||
- 舒适区,舒适而缺乏成长。
|
||||
- 恐慌区,超出能力范围。
|
||||
- 学习区,有难度而可以达成。
|
||||
- 在学习区练习才能得到足够的成长。
|
||||
|
||||
**T 型人才,一专多能**
|
||||
|
||||
- 知识的广度。
|
||||
- 专业技能的深度。
|
||||
- 有“一专”,“多能”才是有意义的。
|
||||
|
||||
在这个模块中,我们还了解了一些重要的思路,让我们把工作做得更好。
|
||||
|
||||
<li>
|
||||
**进入新工作,从全面了解开始**
|
||||
<ul>
|
||||
- 业务:做什么。
|
||||
- 技术:怎么做。
|
||||
- 团队运作:怎么与人协作。
|
||||
- 从大到小,由外及内地了解工作。
|
||||
|
||||
面对遗留系统,稳扎稳打,小步前行
|
||||
|
||||
<li>基础理念
|
||||
<ul>
|
||||
- 烂代码只是现象,要了解根因。
|
||||
- 能重构,先重构,大规模改造是迫不得已的选择。
|
||||
- 小步前行。
|
||||
|
||||
- 构建测试防护网。
|
||||
- 将大系统分解成小模块,逐步替换。
|
||||
- 新旧模块并存,由分发模块调度。
|
||||
- 建立好领域模型。
|
||||
- 寻找行业对于系统构建的最新理解。
|
||||
|
||||
**程序员的职业发展**
|
||||
|
||||
<li>程序员的焦虑来自于对未来的不确定性,这种不确定性是一个特定时代加上特定行业的产物。
|
||||
<ul>
|
||||
- 快速发展的中国经济。
|
||||
- 程序员在中国是一个新兴职业。
|
||||
|
||||
## 实战指南
|
||||
|
||||
<li>
|
||||
<p>了解一个项目,从大图景开始。<br>
|
||||
——《[38 | 新入职一家公司,怎么快速进入工作状态?](http://time.geekbang.org/column/article/89981)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>小步改造遗留系统,不要回到老路上。<br>
|
||||
——《[39 | 面对遗留系统,你应该这样做](http://time.geekbang.org/column/article/90231)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>在学习区工作和成长。<br>
|
||||
——《[40 | 我们应该如何保持竞争力?](http://time.geekbang.org/column/article/90864)》</p>
|
||||
</li>
|
||||
|
||||
## 额外收获
|
||||
|
||||
在这个模块的最后,针对大家在学习过程中的一些问题,我也进行了回答,帮你梳理出一个思路,更好地理解学到的内容:
|
||||
|
||||
- 推行新观念,找愿意改变的人,做具体的事。
|
||||
- Lead by Example.
|
||||
- 外部系统应该用接口隔离,这种做法体现了接口隔离原则(ISP),也是防腐层概念的体现。
|
||||
- 外部系统的测试,能模拟的就模拟,能本地的就本地。
|
||||
|
||||
## 留言精选
|
||||
|
||||
关于入职一家新公司,怎么快速进入工作状态这个问题,西西弗与卡夫卡 同学分享了他的方法:
|
||||
|
||||
>
|
||||
有朋友正在转型,从乙方商业化产品的交付经理转向新公司的产品经理。原本得心应手的思维方式和工作习惯,遇到了巨大挑战。以前只需依据已有产品的功能出解决方案,能做就能做,不能实现就是不能实现,到某个时间交付什么功能很明确,考核是以交付签字为准。现在需要面对各方需求,自己想明白用户真正的问题是什么,最终要交付的价值是什么,没有一个实体的谁人来签字,只有不断地迭代。
|
||||
|
||||
|
||||
>
|
||||
借鉴领域驱动设计,可以采用以下方法。简单描述的话,是一个点、一个圈再加一个箭头线,是不是有点像丘比特?
|
||||
|
||||
|
||||
>
|
||||
一个“点”,指的是用户核心价值。这是最关键的一条,基本上只能靠自己想明白。想,不是闭门造车式的苦思冥想,可以是已有的领域经验,可以从书本中学习,可以是大家的各种吐槽,可以是自己从旁边观察用户的实践,还可以是自己变身为用户的实践。
|
||||
|
||||
|
||||
>
|
||||
有些人会纠结“点”想的对不对,迟迟不敢动手。其实一开始想得对不对不是那么重要,关键是要有这“点”,然后快速到市场上验证,根据反馈再调整。
|
||||
|
||||
|
||||
>
|
||||
一个“圈”,指的是围绕核心价值划出的范围,即领域驱动设计中的限界上下文。产品经理面临的一个现实是,各种人都会给你提需求,只要他们觉得和你有关,还时不时来问什么时候可以实现。
|
||||
|
||||
|
||||
>
|
||||
需求轰炸之下很容易焦虑,不光自己焦虑,所有的利益相关者都会焦虑。依据核心价值,框出需求范围,在和各方交流过程中可以有一种确定性,减少焦虑,利于行动。
|
||||
|
||||
|
||||
>
|
||||
大家(不光是研发团队,也包括其他需求方)就能明白,哪些和当前核心价值密切相关,我们优先考虑;哪些与核心价值有关但它不在我们的范围内,属于其他团队,需要他们协助;哪些有关系,但目前没想清楚价值大不大,并且代价可能很高建议先搁置。范围不是一成不变,它随着时间会发生变动,所以我们不要追求固定,只要保证在某个时间段内,大家一致认同即可。
|
||||
|
||||
|
||||
>
|
||||
一个“箭头”,指的是实现路径,箭头指向核心目标(核心价值)。目标(核心价值)和范围描绘的是终极,而从现实到终极还有很多路要走,可能的路径还有很多条。我们需要琢磨怎么走更稳当,怎么走代价比较低,路上关键的里程碑是什么。路径对不对是其次,重要的是思考过程,可以把关键点需要交付的价值、需要支持的资源等等梳理清楚。
|
||||
|
||||
|
||||
另外,西西弗与卡夫卡 同学还对于程序员如何保持竞争力的问题给出了非常不错的建议。
|
||||
|
||||
>
|
||||
补充我的一些做法。工作中不要满足当前需求,要经常从自己上级主管甚至老板角度来审视自己的工作,思考业务的终极目标,持续琢磨扩展边界,挑战工作难度。
|
||||
|
||||
|
||||
>
|
||||
平时多看书多思考,除了钻研某个领域,还要多有涉猎,拓展领域,成为终身学习者。
|
||||
|
||||
|
||||
>
|
||||
适当运动维持健康,你有更多体力和更强抗压能力的时候,就可以超过不少人。
|
||||
|
||||
|
||||
>
|
||||
保持竞争力除了上述之外,要保持乐观,相信大多数事都有解决方法,在多数人都容易放弃的时候,你的坚持,就是竞争力。
|
||||
|
||||
|
||||
对于新入职一家公司的场景,Y024 同学分享了他快速进入工作状态的方法:
|
||||
|
||||
>
|
||||
1.我会在权限允许的范围内,时不时的到处翻翻 ftp、内部 wiki 等资源,星星点点构建全貌(业务、技术、团队)。
|
||||
|
||||
|
||||
>
|
||||
2.梳理系统数据流。去年很火的电视剧「大江大河」里,宋运辉初入职场的方式就很值得借鉴:先走通全部流程,有个全貌,利用图书馆、师傅等资源再自己动手各个击破并绘制流程图,最终实践检验认知,以技术说话融入团队。
|
||||
|
||||
|
||||
>
|
||||
(他就每天只要天气晴朗,绕着设备上上下下、里里外外地跑。一个星期下来,全部流程走通;两个星期不到,原理搞通,仪表能读,普通故障能应付;第三星期开始,他可以开出维修单,但得给师父过目;第四星期起,谁有事请假他可以顶上,坐到仪表盘前抄表看动态做操作。师父说他学得很快。
|
||||
|
||||
|
||||
>
|
||||
第四星期起,没人可以让他顶替时候,他在仪表室后面支起绘图板。先画出工艺流程图,经现场核对无误,又让师父审核后,开始按部就班地根据液体走向,测绘所有设备的零件图、装配图、管段图等。
|
||||
|
||||
|
||||
>
|
||||
这工作最先做的时候异常艰难,首先是绘图不熟练,很多小毛病,尤其是遇到非标零件,还得到机修工段测绘,有时一天都绘不成一个小小非标件。如果车间技术档案室有图纸还好,可以对照着翻画,可档案室里的图纸残缺不全,前后混乱,想找资料,先得整理资料。
|
||||
|
||||
|
||||
>
|
||||
资料室中年女管理员乐得有个懂事的孩子来帮她整理,索性暗暗配把钥匙给宋运辉,要是她下班不在的时候,让宋运辉自己偷偷进来关上门寻找资料。
|
||||
|
||||
|
||||
>
|
||||
机修工段的人本来挺烦这个宋运辉,说他一来维修单子多得像雪片,支得他们团团转,有人还趁宋运辉上班时候冲进控制室指桑骂槐,被寻建祥骂了回去,差点还打起来。但后来集中一段维修高峰后,维修单子又少了下去,上面还表扬跑冒滴漏少很多,一工段和机修工段各加一次月奖,可见设备性能好转。
|
||||
|
||||
|
||||
>
|
||||
再以后遇到维修,他们不能确定要用什么零件,打个内线电话给控制室问宋运辉,一问就清楚。双方关系渐渐变得铁起来。基层有时候很简单,只要拿得出技术,别人就服。 )
|
||||
|
||||
|
||||
另外,Y024 同学还很认真地整理了专栏提到的部分图书:
|
||||
|
||||
>
|
||||
郑老师拍案惊奇书单及简评,最近各大书店有活动,可以借机囤起来了。
|
||||
|
||||
|
||||
>
|
||||
<p>1.重构<br>
|
||||
作者: Martin Fowler<br>
|
||||
[https://book.douban.com/subject/1229923/](https://book.douban.com/subject/1229923/)<br>
|
||||
严格说来,我并没有完整的读完这本书,不过,正如作者自己所说,这样的书原本就不指望能够读完,因为有一大部分其实是参考手册。正是我读过的部分让我知道了重构,让我知道这么做可以把代码写得更好。</p>
|
||||
|
||||
|
||||
>
|
||||
<p>2.敏捷软件开发<br>
|
||||
作者: Robert C·Martin<br>
|
||||
[https://book.douban.com/subject/1140457/](https://book.douban.com/subject/1140457/)<br>
|
||||
这是一本名字赶潮流,内容很丰富的书,这本书让我开始理解软件设计,从此不再刻意追求设计模式。</p>
|
||||
|
||||
|
||||
>
|
||||
<p>3.测试驱动开发<br>
|
||||
作者: Kent Beck<br>
|
||||
[https://book.douban.com/subject/1230036/](https://book.douban.com/subject/1230036/)<br>
|
||||
读的是英文版,因为当时中文版还没有出版,所以,我不敢说,我通过这本书很好的理解了测试驱动开发,但它却为我打开了一扇门,让我知道了一种更好的工作方式。</p>
|
||||
|
||||
|
||||
>
|
||||
<p>4.修改代码的艺术<br>
|
||||
作者: Michael Feathers<br>
|
||||
[https://book.douban.com/subject/2248759/](https://book.douban.com/subject/2248759/)<br>
|
||||
这是一本讲解如何编写测试的书。至于这本书的具体内容,我的评价是实用。如果说不足,那么,这本书缺少一个列表,就像Martin Fowler为《重构》所做的那样,出什么样的问题,应该采用怎样的手法进行处理。</p>
|
||||
|
||||
|
||||
对于如何面对遗留系统, 毅 同学提到:
|
||||
|
||||
>
|
||||
<p>1.了解原系统已实现的功能,没有文档就在心中划分好内部功能模块;<br>
|
||||
2.各模块的边界及关联,对于业务交叉点先思考通信机制;<br>
|
||||
3.看代码,通常是瓶颈优先,业务上是先复杂后简单;<br>
|
||||
4.选定切入点;<br>
|
||||
5.正式改造时先把原有功能抽象出来使用现有实现,改造的过程完成前不会受影响;<br>
|
||||
6.改造完成后切换到新实现进行测试;<br>
|
||||
7.稳定后替换旧实现;<br>
|
||||
8.重复4-7。</p>
|
||||
|
||||
|
||||
Wei 同学对于“T型人”的说法感触很深:
|
||||
|
||||
>
|
||||
“T型人”这个太说到点了,到底是做“专”还是做“广”,哪条路线一直是我思考的方向;工作上跟大牛工作过,给我感觉几乎是全能的,我一直都想像他们那样,做一个多面手,但是如何做广,这一直是困扰我的一个问题。
|
||||
|
||||
|
||||
>
|
||||
我是dev出身,但是现实遇到的问题往往跟数据库,发布的平台相关;这样说下来,各种相关领域,数据库、k8s、网络协议、DNS ,都需要大量时间去积累;有时候什么都懂一点,反而让自己应该定位什么角色感到迷茫了,掌握的水平不足以让自己去应聘DBA、Ops,但是只是应聘dev似乎又有点“浪费”,跟那些熟悉最新语言/框架的对比起来没特殊竞争力。
|
||||
|
||||
|
||||
>
|
||||
今天学习“T型人”这个概念,让我好好思考了自己到底应该怎么定位。我首先是一个developer,这个是根;对语言特性的熟练掌握,各种best practices,例如课程中提到的TDD等应该熟练应用起来;然后在这上面拓展,学习架构知识,多思考对不同系统应该怎么设计,老师提到的DDD会认真学习应用;再有软件最终还是给用户使用,而不是单单提交代码。相关的数据库、k8s、监控运用根据实际遇到的问题再学习解决。
|
||||
|
||||
|
||||
>
|
||||
最重要的是,在学习区终身学习和工作!
|
||||
|
||||
|
||||
对于如何持续保持竞争力的问题,enjoylearning 同学提到:
|
||||
|
||||
>
|
||||
程序员如何保持竞争力很重要,在这个年轻人学习能力不断提升的IT行业,作为老程序员经验阅历眼光以及技术前沿判断力就显得越来越重要。
|
||||
|
||||
|
||||
>
|
||||
说起来这个职业是一个需要终身学习的职业,年龄不重要,能力才重要,是不是让自己永远呆在学习区更重要。
|
||||
|
||||
|
||||
对于技术推广,desmond 同学的理解也很棒:
|
||||
|
||||
>
|
||||
技术推广,不要先推广最难的部分,先推广能让对方感到最明显好处的部分。取得对方的信任,是友好沟通的基础。
|
||||
|
||||
|
||||
**感谢同学们的精彩留言。我们的专栏更新已经进入尾声阶段,后续我会为大家做一些对整个专栏进行全盘复习的内容,敬请期待。**
|
||||
|
||||
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。
|
||||
250
极客时间专栏/10x程序员工作法/综合运用/总复习 | 重新审视“最佳实践”.md
Normal file
250
极客时间专栏/10x程序员工作法/综合运用/总复习 | 重新审视“最佳实践”.md
Normal file
@@ -0,0 +1,250 @@
|
||||
<audio id="audio" title="总复习 | 重新审视“最佳实践”" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/1f/f5/1f6e2baf484f5a95464a719781910df5.mp3"></audio>
|
||||
|
||||
你好,我是郑晔。
|
||||
|
||||
我承诺的正文内容已经全部交付给你,恭喜你完成了整个专栏的学习!希望通过对这些内容的学习,你已经对“如何做好软件”有了一个全新的认识。
|
||||
|
||||
在这个专栏中,我给你讲了很多行业中的最佳实践,比如:测试、持续集成等等,但因为这个专栏叙述方式的关系,一些有关联的实践被放到了不同的模块下讲解。
|
||||
|
||||
所以在这一讲中,我们将按照最佳实践的维度重新审视这些内容。我会将这些知识重新串联起来,帮你做一个对专栏的整体复习。
|
||||
|
||||
## 产品
|
||||
|
||||
做产品,很多时候是面向不确定性解决问题。目前这方面最好的实践是“精益创业”。对于精益创业的最简单的理解就是“试”。试也有试的方法,精益创业提出了一个“开发(build)- 测量(measure)- 认知(learning)”这样的反馈循环,通过这个循环得到经过验证的认知(Validated Learning)。
|
||||
|
||||
既然是对不确定产品特性的尝试,最好的办法就是低成本地试。在精益创业中,最小可行产品(MVP)就是低成本的试法。最小可行产品,就是“刚刚好”满足用户需求的产品。理解这个说法的关键在于用最小的代价,尝试可行的路径。
|
||||
|
||||
在产品的打磨过程中,可以采用用户测试的方式,直接观察用户对产品的使用。作为程序员,我们要尽可能吃自家的狗粮,即便你做的产品不是给自己使用的产品,也可以努力走近用户。
|
||||
|
||||
<li>
|
||||
<p>**精益创业**<br>
|
||||
相关阅读:《[06 | 精益创业:产品经理不靠谱,你该怎么办?](http://time.geekbang.org/column/article/76260)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**最小可行产品(MVP)**<br>
|
||||
相关阅读:《[19 | 如何用最小的代价做产品?](http://time.geekbang.org/column/article/80691)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**用户测试、验证产品特性、吃自家狗粮**<br>
|
||||
相关阅读:《[26 | 作为程序员,你也应该聆听用户声音 ](http://time.geekbang.org/column/article/84185)》</p>
|
||||
</li>
|
||||
|
||||
## 需求
|
||||
|
||||
当我们确定做一个产品功能时,怎么描述需求也是很重要的。产品列表式的需求描述方式最容易出现问题的地方在于,看不清需求的全貌。
|
||||
|
||||
用户故事是一个好的需求描述方式:作为一个什么角色,要做什么样的事,以便达成一种怎样的效果。
|
||||
|
||||
在用户故事中,验收标准是非常重要的一环。即便不是采用用户故事描述需求,也依然建议先将验收标准定义清楚。
|
||||
|
||||
开发团队对需求的理解普遍偏大,基本上都是一个主题。在开发之前,先将需求拆分成小粒度的。衡量一个用户故事拆分是否恰当,一个标准是 INVEST 原则。有了拆分出来的用户故事,就可以进行估算了,估算的过程也是对需求加深理解的过程,过大的用户故事应该再次拆分。
|
||||
|
||||
当我们有了拆分之后的需求,就可以对需求进行优先级讨论了。先做重要性高的事,而不是一股脑地去做所有的需求。只有分清了需求的优先级,才能方便地对需求进行管理。
|
||||
|
||||
<li>
|
||||
<p>**用户故事**<br>
|
||||
相关阅读:《[04 | 接到需求任务,你要先做哪件事?](http://time.geekbang.org/column/article/75100) 》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**需求的分解与估算**<br>
|
||||
相关阅读:《[17 | 程序员也可以“砍”需求吗?](http://time.geekbang.org/column/article/79520)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**需求管理、优先级**<br>
|
||||
相关阅读:《[18 | 需求管理:太多人给你安排任务,怎么办?](http://time.geekbang.org/column/article/80428)》</p>
|
||||
</li>
|
||||
|
||||
## 持续集成
|
||||
|
||||
在开发中,写出代码并不是终点,我们要把代码集成起来。集成要经常做,改动量越小,集成就可以做得越频繁,频繁到每次提交都去集成,这就是持续集成。
|
||||
|
||||
持续集成发展到今天已经是一套完整的开发实践。想要做好持续集成,你需要记住持续集成的关键是“快速反馈”。
|
||||
|
||||
- 怎样快速得到反馈。
|
||||
- 怎样反馈是有效的。
|
||||
|
||||
持续集成,可以继续延展,将生产部署也纳入其中,这就是持续交付。如果持续交付,再向前一步,就可以同产品验证结合起来。
|
||||
|
||||
持续交付的关键点,是在不同的环境验证发布包和自动化部署。不同的环境组成了持续交付的构建流水线,而自动化部署主要是 DevOps 发挥能力的地方。持续交付的发展,让交付物从一个简单的发布包变成了一个拥有完整环境的 Docker 镜像。
|
||||
|
||||
持续集成和持续交付可以将诸多的实践贯穿起来:单元测试、软件设计、任务分解、主分支开发、DevOps 等等。所以,如果一个公司希望做过程改进,持续集成是一个好的出发点。
|
||||
|
||||
<li>
|
||||
<p>**持续集成发展史 **<br>
|
||||
相关阅读:《[05 | 持续集成:集成本身就应该是写代码的一个环节](http://time.geekbang.org/column/article/75977)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**快速反馈**<br>
|
||||
相关阅读:《[24 | 快速反馈:为什么你们公司总是做不好持续集成?](http://time.geekbang.org/column/article/83461)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**持续集成,贯穿诸多实践**<br>
|
||||
相关阅读:《[答疑解惑 | 持续集成,一条贯穿诸多实践的主线 ](http://time.geekbang.org/column/article/85049)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**持续交付**<br>
|
||||
相关阅读:《[32 | 持续交付:有持续集成就够了吗?](http://time.geekbang.org/column/article/87229)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**与产品结合:持续验证**<br>
|
||||
相关阅读:《[答疑解惑 | 持续集成、持续交付,然后呢?](http://time.geekbang.org/column/article/89050) 》</p>
|
||||
</li>
|
||||
|
||||
## 测试
|
||||
|
||||
测试是一个典型的程序员误区,很多程序员误以为测试只是测试人员的事。理解了软件变更成本,知道了内建质量之后,我们就应该清楚,测试应该体现在全部的开发环节中。这一思想在开发中的体现就是自动化测试。
|
||||
|
||||
想要写好自动化测试,需要先理解测试金字塔,不同的测试运行成本不同。为了让软件拥有更多的、覆盖面更广的测试,需要多写单元测试。
|
||||
|
||||
编写测试的方式有很多,一种实践是测试驱动开发(TDD)。先写测试,然后写代码,最后重构,这就是 TDD 的节奏:红——绿——重构。测试驱动开发的本质是测试驱动设计,所以,编写可测试的代码是前提。
|
||||
|
||||
要想做好 TDD,一个重要的前提是任务分解,分解到非常小的微操作。学会任务分解,是成为优秀程序员的前提条件。
|
||||
|
||||
想写好测试,需要懂得好测试是什么样子的,避免测试的坏味道。好测试有一个衡量标准:A-TRIP。
|
||||
|
||||
我们不只要写好单元测试,还要站在应用的角度写测试,这就是验收测试。验收测试现在比较成体系的做法是行为驱动开发(BDD),它让你可以用业务的语言描述测试。
|
||||
|
||||
<li>
|
||||
<p>**单元测试、自动化测试、蛋卷和冰淇淋模型**<br>
|
||||
相关阅读:《[12 | 测试也是程序员的事吗?](http://time.geekbang.org/column/article/77917)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**测试驱动开发**<br>
|
||||
相关阅读:《[13 | 先写测试,就是测试驱动开发吗?](http://time.geekbang.org/column/article/78104)》<br>
|
||||
相关阅读:《[14 | 大师级程序员的工作秘笈](http://time.geekbang.org/column/article/78507) 》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**测试练习**<br>
|
||||
相关阅读:《[15 | 一起练习:手把手带你拆任务](http://time.geekbang.org/column/article/78542) 》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**简单的测试、测试的坏味道、A-TRIP**<br>
|
||||
相关阅读:《[16 | 为什么你的测试不够好?](http://time.geekbang.org/column/article/79494) 》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**验收测试、写好验收测试用例**<br>
|
||||
相关阅读:《[32 | 持续交付:有持续集成就够了吗?](http://time.geekbang.org/column/article/87229)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**外部系统测试,用接口隔离**<br>
|
||||
相关阅读:《[答疑解惑 | 如何在实际工作中推行新观念?](http://time.geekbang.org/column/article/91127) 》</p>
|
||||
</li>
|
||||
|
||||
## 编码与设计
|
||||
|
||||
编码和设计,是软件开发中最重要的一环。在我看来,编码和设计是一体,想清楚才能写出好代码。很多程序员追求写好代码,却没有一个很好的标准去衡量代码的好坏。结合着软件设计的一些理念,我给你一个编写好代码的进步阶梯,希望你能达到用业务语言编写代码的程度。
|
||||
|
||||
用业务语言编写代码,需要对软件设计有着良好的理解。提到设计,人们的初步印象是“高内聚低耦合”,但这是一个太过高度抽象的描述。SOLID 原则是一个更具实践性的指导原则,有了原则做指导,就可以更好地理解设计模式了。
|
||||
|
||||
有了基础原则,我们会知道将不同的代码划分开,这样就产生了分层。好的分层可以构建出抽象,而其他人就可以在这个抽象上继续发展。对于程序员来说,构建自己的核心抽象是最关键的一步。
|
||||
|
||||
目前构建核心抽象最好的方式是领域驱动设计(DDD),它将我们思考的起点拉到了业务层面,通过战略设计将系统按照不同的上下文划分开来,再通过战术设计,指导我们有效地设计一个个的领域模型。
|
||||
|
||||
但无论怎样做设计,前提是使用适当的技术解决适当的问题,不要把技术用复杂,把团队带入泥潭。
|
||||
|
||||
<li>
|
||||
<p>**业务语言写代码**<br>
|
||||
相关阅读:《[21 | 你的代码为谁而写?](http://time.geekbang.org/column/article/82581)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**架构设计**<br>
|
||||
相关阅读:《[34 | 你的代码是怎么变混乱的? ](http://time.geekbang.org/column/article/87845)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**分层、抽象**<br>
|
||||
相关阅读:《[35 | 总是在说MVC分层架构,但你真的理解分层吗?](http://time.geekbang.org/column/article/88309)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**业务与技术**<br>
|
||||
相关阅读:《[36 | 为什么总有人觉得5万块钱可以做一个淘宝?](http://time.geekbang.org/column/article/88764) 》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**微服务**<br>
|
||||
相关阅读:《[37 | 先做好DDD再谈微服务吧,那只是一种部署形式 ](http://time.geekbang.org/column/article/89049)》</p>
|
||||
</li>
|
||||
|
||||
## 项目准备
|
||||
|
||||
从头开始一个项目时,一个好的实践就是把一切都准备好。迭代0就是这样一个把迭代准备好的实践,从需求到技术,做好充分的准备工作再开启项目,你会显得从容不迫。在技术方面,迭代0最重要的准备工作就是构建脚本,它是后续很多工作的基础,比如,持续集成。
|
||||
|
||||
<li>
|
||||
<p>**迭代0,做基础的准备**<br>
|
||||
相关阅读:《[10 | 迭代0: 启动开发之前,你应该准备什么?](http://time.geekbang.org/column/article/77294)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**构建脚本,让项目一开始就自动化**<br>
|
||||
相关阅读:《[30 | 一个好的项目自动化应该是什么样子的? ](http://time.geekbang.org/column/article/86561)》</p>
|
||||
</li>
|
||||
|
||||
## 其余的最佳实践
|
||||
|
||||
除了几个花大篇幅介绍的最佳实践,我们还提到了很多不同的最佳实践。
|
||||
|
||||
## DoD
|
||||
|
||||
完成的定义(DoD),是一个确保合作各方理解一致的实践。它是一个清单,由一个个检查项组成,每个检查项都是实际可检查的。有了 DoD,做事就只有两种状态:完成和未完成。
|
||||
|
||||
<li>**完成的定义,DOD**<br>
|
||||
相关阅读:《[03 | DoD价值:你完成了工作,为什么他们还不满意?](http://time.geekbang.org/column/article/74828)》</li>
|
||||
|
||||
## 站会
|
||||
|
||||
站会,一种轻量级的会议形式,用来同步每天发生的事情。一般来说,只说三件事:昨天做了什么,今天打算做什么,遇到了什么问题。
|
||||
|
||||
<li>**站会**<br>
|
||||
相关阅读:《[22 | 轻量级沟通:你总是在开会吗?](http://time.geekbang.org/column/article/82844) 》</li>
|
||||
|
||||
## 看板
|
||||
|
||||
看板,一种项目管理工具, 将正在进行的工作可视化。通过看板,可以发现团队正在进行工作的很多问题。看板有实体和电子之分,可以根据自己的项目特点进行选择。
|
||||
|
||||
<li>**看板**<br>
|
||||
相关阅读:《[23 | 可视化:一种更为直观的沟通方式 ](http://time.geekbang.org/column/article/83082)》</li>
|
||||
|
||||
## 回顾会议
|
||||
|
||||
回顾会议,是一种复盘实践,让团队成员对一个周期内发生的事情进行回顾。回顾会议一般分为讲事实、找重点和制定行动项三个部分。但在开始回顾之前,会先进行安全检查,确保每个人都能放心大胆地说真话。
|
||||
|
||||
<li>
|
||||
<p>**回顾会议**<br>
|
||||
相关阅读:《[25 | 开发中的问题一再出现,应该怎么办?](http://time.geekbang.org/column/article/83841) 》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**回顾会议中的安全检查**<br>
|
||||
相关阅读:《[答疑解惑 | 持续集成,一条贯穿诸多实践的主线](http://time.geekbang.org/column/article/85049) 》</p>
|
||||
</li>
|
||||
|
||||
## 重构
|
||||
|
||||
重构,是程序员的基本功,把调整代码的动作分解成若干可以单独进行的“重构”小动作,一步步完成。重构的前提是识别代码的坏味道。保证代码行为不变,需要有测试配合,而重构的方向是,重构成模式(Refactoring to Patterns)。重构的过程和编写代码的过程最好结伴而行,最佳实践就是测试驱动开发。
|
||||
|
||||
<li>
|
||||
<p>**重构**<br>
|
||||
相关阅读:《[加餐 | 你真的了解重构吗?](http://time.geekbang.org/column/article/85915)》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**在测试驱动开发中重构**<br>
|
||||
相关阅读:《[13 | 先写测试,就是测试驱动开发吗?](http://time.geekbang.org/column/article/78104)》</p>
|
||||
</li>
|
||||
|
||||
## 分支开发
|
||||
|
||||
分支开发模型,是每个团队都要面临的问题。行业中有两种常见的分支模型,一种是基于主干的开发模型,一种是分支开发模型。分支开发符合直觉,却不是最佳实践。主分支开发模型是与其他实践配合最好的模式,但也需要开发者有着良好的开发习惯。如果并行开发多个功能,可以考虑 Feature Toggle 和 Branch by Abstraction。
|
||||
|
||||
<li>
|
||||
<p>**分支开发**<br>
|
||||
相关阅读:《[14 | 大师级程序员的工作秘笈](http://time.geekbang.org/column/article/78507) 》</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**Feature Toggle 和 Branch by Abstraction**<br>
|
||||
相关阅读:《[答疑解惑 | 如何分解一个你不了解的技术任务?](http://time.geekbang.org/column/article/81515) 》</p>
|
||||
</li>
|
||||
|
||||
## Fail Fast
|
||||
|
||||
Fail Fast 是一个重要的编程原则:遇到问题,尽早报错。不要以构建健壮系统为由,兼容很多奇怪的问题,使得 Bug 得以藏身。
|
||||
|
||||
<li>**Fail Fast**<br>
|
||||
相关阅读:《[27 | 尽早暴露问题: 为什么被指责的总是你?](http://time.geekbang.org/column/article/84374) 》</li>
|
||||
|
||||
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。
|
||||
152
极客时间专栏/10x程序员工作法/综合运用/总复习 | 重新来“看书”.md
Normal file
152
极客时间专栏/10x程序员工作法/综合运用/总复习 | 重新来“看书”.md
Normal file
@@ -0,0 +1,152 @@
|
||||
<audio id="audio" title="总复习 | 重新来“看书”" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/23/21/23525c4e466c9a9714f94df40727cc21.mp3"></audio>
|
||||
|
||||
我们继续复习,在上一讲中,我从最佳实践的角度带领大家回顾了专栏里的一些内容。这一讲,我们换个复习的角度。在专栏进行的过程中,有一些同学注意到我引用了大量的书籍,提出让我把这些书做一个整理。
|
||||
|
||||
Wei 同学提到:
|
||||
|
||||
>
|
||||
有一个小建议: 在每一个主题模块的小结中,把文章中提到的书籍做一个书单方便读者。
|
||||
|
||||
|
||||
刘晓林 同学提到:
|
||||
|
||||
>
|
||||
郑老师在专栏中推荐了很多非常好的书籍作为参考,可否考虑在某一期中,将这些参考书籍整理成一个书单,按照专栏的主题做个小分类,然后每本书简单点评两句作为领读内容。希望在专栏的结束语之前可以看到这个书单。
|
||||
|
||||
|
||||
Y024 同学甚至在留言中帮我总结了一个[小清单](http://time.geekbang.org/column/article/90231),而有人也在豆瓣上做出了一个[豆列](http://www.douban.com/doulist/112766085/),罗列了专栏中提到的一些书。
|
||||
|
||||
在今天这一讲中,我就站在“看书”的视角,带着你进行一次复习。这些书大多是在我个人成长过程中,给我留下深刻印象的。
|
||||
|
||||
我希望你在结束这个专栏学习之后,开启的是另外一段学习历程,用这些书提升自己的水平,夯实自己的基础知识。学习了这个专栏之后,你拥有了一个新的知识结构,再来看这些书就会有一种全新的体验。
|
||||
|
||||
此外,在这次的内容中,我会提到几本专栏中没有提到的书,算是给你在学习路上的一个补充。我还制作了一个[豆列](http://www.douban.com/doulist/113366760/),方便你去找到这些书。
|
||||
|
||||
## 编码实践
|
||||
|
||||
<li>
|
||||
如果你想详细学习如何写好代码,我推荐你去读 Robert Martin 的《[代码整洁之道](http://book.douban.com/subject/4199741/)》(Clean Code),这本书几乎覆盖了如何把代码写好的方方面面。
|
||||
</li>
|
||||
<li>
|
||||
《[实现模式](http://book.douban.com/subject/3324516/)》是一本关于如何写好代码的书,更具体一点是,编写别人能够理解的代码。它的作者 Kent Beck 是许多软件开发实践的开创者。但 Kent Beck 的写作能力一般,他的很多作品被埋没了。只有细细品味,才能体会到 Kent Beck 深厚的功力。
|
||||
</li>
|
||||
<li>
|
||||
我提升自己编码水平的理解是从《[程序设计实践](http://book.douban.com/subject/1173548/)》(The Practice of Programming)这本书开始的,这本书的作者是 Brian Kernighan 和 Rob Pike,这两个人都出身于大名鼎鼎的贝尔实验室,参与过 Unix 的开发。
|
||||
</li>
|
||||
<li>
|
||||
如果你想从日常开发中提升自己的效率,可以读一下《[卓有成效的程序员](http://book.douban.com/subject/3558788/)》。假如你不曾思考过这个问题,这本书会让看到一些不同的工作方式,我也给这本书写过一篇[书评](http://book.douban.com/review/1517237/)。不过,这本书里的技巧太具体了,所以,有一些已经有些过时了。
|
||||
</li>
|
||||
|
||||
## 设计
|
||||
|
||||
<li>
|
||||
SOLID 原则是一种面向对象软件设计原则。早在1995年,Robert Martin 就提出了这些[设计原则的雏形](http://groups.google.com/d/topic/comp.object/WICPDcXAMG8?hl=en),然后在他的《[敏捷软件开发:原则、实践与模式](http://book.douban.com/subject/1140457/)》这本书中,比较完整地阐述了这五个原则,后来,他有把这些原则进一步整理,成了今天的 “SOLID”。有了设计原则做基础,这本书后面讲了设计模式,理解起来就容易多了。虽然书名是关于敏捷的,但这是一本讲设计的书。
|
||||
</li>
|
||||
<li>
|
||||
设计和架构有什么区别?2017年,Robert Martin 出版了《[架构整洁之道](http://book.douban.com/subject/30333919/)》(Clean Architecture),他在其中告诉我们,二者没有区别。所以,这也是一本关于设计的书,给出了 Robert Martin 对设计的最新理解。你可以把它看成《[敏捷软件开发:原则、实践与模式](http://book.douban.com/subject/1140457/)》的修订版。
|
||||
</li>
|
||||
<li>
|
||||
《[设计模式](http://book.douban.com/subject/1052241/)》**不推荐阅读**,它是设计模式的开山之作,但它的起点是 Erich Gamma 的博士论文,其写作风格偏向学术,而且中文版翻译得也很一般。这里将它罗列出来只是因为其历史重要性。如果你想学习设计模式,现在有一些更容易入门的书,比如《[Head First 设计模式](http://book.douban.com/subject/2243615/)》。
|
||||
</li>
|
||||
<li>
|
||||
Martin Fowler 的《[企业应用架构模式](http://book.douban.com/subject/1230559/)》将软件开发当时常见的解决方案汇集成模式,今天看来很多模式已经习以为常,但当年出场可是技惊四座的。从这本书的名字你不难看出,它出版的年代是企业级开发盛行的年代。[Martin Fowler 一直认为这本书没有写完](http://www.martinfowler.com/eaaDev/),希望能够继续更新,但不知道何时能看到这本书的新版。
|
||||
</li>
|
||||
<li>
|
||||
《[Unix 编程艺术](http://book.douban.com/subject/1467587/)》也是一本讲软件设计的书,只不过,它选择的切入点是 Unix 中的设计,从中你可以学到“只做一件事,把它做好”、“文本化”等编程理念,有助于你改善日常的工作。这样的书,也就只有 Eric Raymond 这样沉浸编程几十年的人才能写出来。
|
||||
</li>
|
||||
|
||||
## 工程实践
|
||||
|
||||
<li>
|
||||
Kent Beck 有一本知名的软件工程之作《[解析极限编程](http://book.douban.com/subject/6828074/)》(Extreme Programming Explained),它介绍了一种软件开发方法:极限编程。但更重要的是,今天很多主流的软件开发最佳实践都是从这里出来的。这本书可以理解成诸多最佳工程实践的总纲。
|
||||
</li>
|
||||
<li>
|
||||
Martin Fowler 在1999年写下软件行业的名著《[重构:改善既有代码的设计](http://book.douban.com/subject/4262627/)》(Refactoring: Improving the Design of Existing Code),把重构这个小圈子实践带到了大众视野。2018年底,Martin Fowler 时隔近20年后,又写出了[《重构》第二版](http://book.douban.com/subject/30468597/)。把他对这些年行业发展的新理解融入到重构实践中。重构应该有个目标,这个目标就是“重构成模式”,而这也是一本专门的书:《[重构与模式](http://book.douban.com/subject/5360962/)》(Refactoring to Patterns)。
|
||||
</li>
|
||||
<li>
|
||||
《[测试驱动开发](http://book.douban.com/subject/1230036/)》是 Kent Beck 为世人展示 TDD 做法的一本书。它好的地方需要自己体会,Kent Beck 并没有显式的讲出来,比如:任务分解。
|
||||
</li>
|
||||
<li>
|
||||
Jez Humble 和 Dave Farley 的《[持续交付](http://book.douban.com/subject/6862062/)》(Continuous Delivery)让持续集成再进一步,将生产环境纳入了考量。乔梁,他是《持续交付》这本书的中文版译者,而且在这本书出版近十年后,他自己写了《[持续交付 2.0](http://book.douban.com/subject/30419555/)》,把自己多年来关于持续交付的新理解整理了进去。
|
||||
</li>
|
||||
<li>
|
||||
说到遗留代码和测试,我推荐一本经典的书:Michael Feathers 的《[修改代码的艺术](http://book.douban.com/subject/2248759/)》(Working Effectively with Legacy Code),从它的英文名中,你就不难发现,它就是一本关于遗留代码的书。如果你打算处理遗留代码,也建议你读读这本书。这本书我也写过[书评](http://book.douban.com/review/1226942/),你可以了解一下我对它看法。
|
||||
</li>
|
||||
|
||||
## 领域驱动设计
|
||||
|
||||
<li>
|
||||
Eric Evans 2003年写了《[领域驱动设计](http://book.douban.com/subject/1629512/)》,向行业介绍一下 DDD 这套方法论,立即在行业中引起广泛的关注。但实话说,Eric 在知识传播上的能力着实一般,这本关于 DDD 的开山之作,其写作质量却难以恭维,想要通过它去学好 DDD,是非常困难的。所以,在国外的技术社区中,有很多人是通过各种交流讨论逐渐认识到 DDD 的价值所在,而在国内 ,DDD 几乎没怎么掀起波澜。
|
||||
</li>
|
||||
<li>
|
||||
2013年,在 Eric Evans 出版《领域驱动设计》十年之后,DDD 已经不再是当年吴下阿蒙,有了自己一套比较完整的体系。Vaughn Vernon 将十年的精华重新整理,写了一本《[实现领域驱动设计](http://book.douban.com/subject/25844633/)》,普通技术人员终于有机会看明白 DDD 到底好在哪里了。所以,你会发现,最近几年,国内的技术社区开始出现了大量关于 DDD 的讨论。
|
||||
</li>
|
||||
<li>
|
||||
因为《实现领域驱动设计》实在太厚,Vaughn Vernon 又出手写了一本精华本《[领域驱动设计精粹](http://book.douban.com/subject/30333944/)》,让人可以快速上手 DDD,这本书也是我向其他人推荐学习 DDD 的首选。
|
||||
</li>
|
||||
|
||||
## 产品与需求
|
||||
|
||||
<li>
|
||||
精益创业是 Eric Ries 最早总结出来的。他在很多地方分享他的理念,不断提炼,最终在2011年写成一本同名的书:《[精益创业](http://book.douban.com/subject/10945606/)》。如果说精益创业是理论,《[精益创业实战](http://book.douban.com/subject/20505765/)》这本书则给了你一个操作流程。
|
||||
</li>
|
||||
<li>
|
||||
Mike Cohn 是敏捷理念的一个重要传播者,我们在讲测试金字塔时,提到了他的著作《[Scrum敏捷软件开发](http://book.douban.com/subject/5334585/)》(Succeeding with Agile)。敏捷开发有两大流派:一派是工程实践,另一派是管理实践。如果你对 Scrum 这类管理实践感兴趣,可以读一下这本书。
|
||||
</li>
|
||||
<li>
|
||||
如果你对用户故事这个话题感兴趣,推荐阅读 Mike Cohn 的两本书《[用户故事与敏捷方法](http://book.douban.com/subject/4743056/)》(User Stories Applied)和《[敏捷软件开发实践 估算与计划](http://book.douban.com/subject/26811747/)》(Agile Estimating and Planning)。
|
||||
</li>
|
||||
|
||||
## 开发文化
|
||||
|
||||
<li>
|
||||
软件行业里有一本名著叫《[人月神话](http://book.douban.com/subject/1102259/)》,这算是软件开发领域第一本反思之作。今天,我们讨论的很多词汇都出自这本书,比如,没有银弹、焦油坑等等。虽然这本书出版于1975年,但其中提到的问题,依然困扰着今天的程序员。
|
||||
</li>
|
||||
<li>
|
||||
开源概念的提出者 Eric Raymond,他的《[大教堂与集市](http://book.douban.com/subject/25881855/)》推开了开源大门。今天开源软件已经成为程序员日常工作的一部分,但如果没有 Eric Raymond 这些人的努力,我们还必须与复杂的企业级软件搏斗。了解一下开源的历程,可以帮助你更好地理解今天的幸福。
|
||||
</li>
|
||||
<li>
|
||||
程序员应该如何做,Robert Martin 也写了一本书《[程序员的职业素养](http://book.douban.com/subject/11614538/)》(Clean Coder),其中对大多数程序员最重要的一点建议是,说“不”。
|
||||
</li>
|
||||
|
||||
## 软件开发拾遗
|
||||
|
||||
<li>
|
||||
高德纳的《[计算机程序设计艺术](http://book.douban.com/subject/26681685/)》肯定是一套程序员都知道,但没几个人读完的书。算法的讲解经过几十年已经有了很好的发展,如果学算法,肯定有更好的选择。如果你想看图灵奖获得者如何从根上思考问题,不妨找来这套书来翻翻。
|
||||
</li>
|
||||
<li>
|
||||
《[快速软件开发](http://book.douban.com/subject/3151486/)》(Rapid Development),**不推荐阅读**。在这本书中,作者首次提出了解决集成问题的优秀实践:Daily Build,每日构建。通过这个名字,我们便不难看出它的集成策略,即每天集成一次。它其中很多实践在当时是先进的,但今天看来有些落伍了。如果你只想从中收获一些理念性的东西,可以去读读。
|
||||
</li>
|
||||
<li>
|
||||
《[C 程序设计语言](http://book.douban.com/subject/1139336/)》、《[Unix 编程环境](http://book.douban.com/subject/1033144/)》等出自贝尔实验室大师级程序员之手,他们的书都值得一读,其中的内容今天看来可能有些过时,但他们解决问题的方式和手法却值得慢慢品味。
|
||||
</li>
|
||||
<li>
|
||||
我在讲淘宝技术变迁时,提到了《[淘宝技术这十年](http://book.douban.com/subject/24335672/)》,这本书算不上经典,但可以当做休闲读物。
|
||||
</li>
|
||||
|
||||
## 技术之外
|
||||
|
||||
<li>
|
||||
管理大师彼得·德鲁克有一本经典著作《[卓有成效的管理者](http://book.douban.com/subject/1322025/)》,虽然标题上带着管理者几个字,但在我看来,这是一本告诉我们如何工作的书,每个人都可以读一下。
|
||||
</li>
|
||||
<li>
|
||||
尤瓦尔·赫拉利的《[人类简史](http://book.douban.com/subject/25985021/)》或《[未来简史](http://book.douban.com/subject/26943161/)》,是我第一次学到“大历史观”这个说法,历史不再是一个个单独的历史事件,而是一个有内在逻辑的发展脉络。
|
||||
</li>
|
||||
<li>
|
||||
《[从一到无穷大](http://book.douban.com/subject/1102715/)》是一本著名科普著作,它向我们介绍了20世纪以来的科学进展。作者乔治·伽莫夫既是热宇宙大爆炸模型的提出者,也是生物学上最早提出“遗传密码”模型的人。虽然这本书出版自1947年,但以现在社会的整体科学素养,还是有必要读读这本书的。
|
||||
</li>
|
||||
<li>
|
||||
史蒂芬·柯维(Stephen Richards Covey)的《[高效能人士的七个习惯](http://book.douban.com/subject/26284789/)》,其中的理念我在专栏两个不同的地方提到过,一个是讲以终为始时,那段关于智力创造的论述,另一个是讲优先级时提到的艾森豪威尔矩阵。这本书值得每个人阅读,很多程序员欠缺的就是这些观念性的东西。
|
||||
</li>
|
||||
<li>
|
||||
很多程序员都是科幻小说迷,编程和科幻,这两个都是需要想象力的领域。刘慈欣的《[三体](http://book.douban.com/subject/6518605/)》,不说它给 IT 行业带来的丰富的词汇表吧,作为科幻小说来说,它就是一流的,值得阅读。它会让你仰望星空,打开思维。如果你对科幻小说有兴趣,推荐阅读阿西莫夫的《[银河帝国](http://book.douban.com/subject/26389895/)》系列,这是科幻小说界的扛鼎之作,你会看到,一部出版于1942年的书里就有大数据的身影。
|
||||
</li>
|
||||
<li>
|
||||
对于程序员来说,最好的工作状态就是进入心流,它会让你忘我工作。如果你对心流的概念感兴趣,可以去读米哈里·契克森米哈赖的著作《[心流](http://book.douban.com/subject/27186106/)》,这位作者就是心流概念的提出者。
|
||||
</li>
|
||||
|
||||
好,今天的复习就到这里,你有哪些经典的书可以推荐给这个专栏的同学呢?欢迎在留言区写下分享你的想法。
|
||||
|
||||
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。
|
||||
|
||||
|
||||
142
极客时间专栏/10x程序员工作法/综合运用/答疑解惑 | 如何在实际工作中推行新观念?.md
Normal file
142
极客时间专栏/10x程序员工作法/综合运用/答疑解惑 | 如何在实际工作中推行新观念?.md
Normal file
@@ -0,0 +1,142 @@
|
||||
<audio id="audio" title="答疑解惑 | 如何在实际工作中推行新观念?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/40/c0/40f03dd93abd997603b2e37aadb6e6c0.mp3"></audio>
|
||||
|
||||
你好,我是郑晔。
|
||||
|
||||
在整个专栏的最后一个大模块"综合运用"中,我们把前面学到的各种原则和知识穿插在一起应用在了不同的场景中。在这个模块的答疑中,我们也综合汇总一次,把整个专栏中出现的一些有趣却还没有来得及讨论的问题放在一起。
|
||||
|
||||
## 问题1:想要推行 DDD,阻力很大怎么办?
|
||||
|
||||
段启超 同学提到
|
||||
|
||||
>
|
||||
<p>想在公司内推行DDD,阻力真的很大,首先是很多人对DDD没概念,需要一定的学习成本,二是团队间相互隔离,沟通成本很高,起码的通用语言都很难达成。<br>
|
||||
——《[37 | 先做好DDD再谈微服务吧,那只是一种部署形式](http://time.geekbang.org/column/article/89049)》</p>
|
||||
|
||||
|
||||
段启超同学提到的这个问题是一个非常典型的问题,而且,这个问题并不仅仅局限于 DDD。你在一个地方看到了一些好东西:技术、实践,或是想法,然后想把它运用在自己的项目中,希望项目越做越好,越来越顺利。但在实际情况中,想在一个组织内推广一些不一样的东西,都会面临层层阻力。
|
||||
|
||||
我在《[40 | 我们应该如何保持竞争力?](http://time.geekbang.org/column/article/90864)》中提到了一个学习模型,你只要在学习区不断地练习新技能,很快就可以超越同侪。其中的原因是,大部分人只习惯待在舒适区,在舒适区的人能力上的进步非常有限。也因为在舒适区实在太舒适了,走出舒适区会让人产生焦虑,所以,人的内心是惧怕改变的。
|
||||
|
||||
你有良好的愿望,驱动你自己去改变是一件可控的事,有愿意和你一起改变的人是一件幸运的事,但你指望所有人一下子和你走上同一条道路,这是一件几乎不可能的事,即便你是很高层的领导,让所有人与你保持一致也不现实。
|
||||
|
||||
我曾经在一个大公司做过敏捷咨询,这还是由他们顶层领导推动的敏捷转型,但依然是困难重重。那些习惯于待在自己舒适区的人总会找到各种神奇的理由告诉你,他们的情况有多么特殊,这些最佳实践在他们那里是不适用的。
|
||||
|
||||
我们放弃了吗?并没有。我们的做法是,找一个团队来做试点。
|
||||
|
||||
换句话说,我们找到了几个愿意改变的人,把这些最佳实践应用在他们的项目上。在这种情况下,大家的目标是一致的,就是希望让这个项目得到改善。所以,大家自然会想尽一切办法,克服遇到的困难。比如,我们当时的切入点是持续集成。
|
||||
|
||||
<li>
|
||||
他们的代码都在老旧的 ClearCase 上,每个人修改文件要先去竞争文件锁,特别不利于小步提交,所以,我们推动着将 ClearCase 改成了稍微进步一点的 Subversion。(好吧,你能听出来这是一个有些年头的故事。)
|
||||
</li>
|
||||
<li>
|
||||
代码是用 C 语言编写的,在他们的代码规模下,编译时间会很长。于是,我们决定搭建一个分布式构建系统,这需要有很多台电脑。不过,他们的硬件是严格管控的,申请电脑是很困难的,虽然花了很大的力气,但最终我们做到了。
|
||||
</li>
|
||||
<li>
|
||||
以往团队都是几天甚至几周才提交一次代码,我们先将代码提交的要求限定在每人每天至少提交一次,为此,我们专门坐下来与团队成员一起分解任务,将他们理解的大任务拆分成一个一个的小任务。
|
||||
</li>
|
||||
<li>
|
||||
……
|
||||
</li>
|
||||
|
||||
想做事,只需要一个理由就够了,不想做,理由有一万个。劝那些不想改变的人改变是异常耗时而且收效甚微。最好的办法是,**找到愿意和你一起改变的人,做一件具体的事。**
|
||||
|
||||
我们并没有劝说谁去听从我们的想法,只是在一个一个地解决问题。我们花了很长时间,最终建立起了持续集成,看到大屏幕上的绿色标识,我颇为感动。原本只需要一两天搭建的持续集成,在一个复杂组织中,它要花费那么长时间,这也是我从未经历过的。
|
||||
|
||||
当我们把这件事做成之后,其他团队看到了效果,开始纷纷效仿。于是,原本复杂的各种规定也开始纷纷松绑,比如,他们再也不需要为申请电脑发愁了。至于之前质疑我们的人,因为看到了成效,他们的关注点就成了怎么把事能做成。
|
||||
|
||||
后来我听说,他们在组织内部专门建立了一个持续集成中心,为各个团队提供了公共的构建资源,提升了整体的效率。
|
||||
|
||||
Linus Torvalds 曾经说过:“Talk is cheap. Show me the code. ”讲道理很容易,但也难以让人真正的信服。同样,做事很难,但成果摆在那里,让人不得不信服。
|
||||
|
||||
在英文中对这种行为有一个说法叫“Lead by Example”,通常用来形容团队领导以身作则的行事风格。当你寻求改变时,无论你的角色是什么,你都需要扮演好领导者的角色,“Lead by Example”送给你!
|
||||
|
||||
## 问题2:测试怎么写?
|
||||
|
||||
andyXH 同学提到
|
||||
|
||||
>
|
||||
<p>目前对于 TDD 还是处于理解状态,不知道如何真正的在项目工程中使用。因为项目工程往往还有很多其他调用,如rpc,数据库服务,第三方服务,不知道在这个过程如何处理。期待老师在之后文章中讲解。<br>
|
||||
——《[13 | 先写测试,就是测试驱动开发吗?](http://time.geekbang.org/column/article/78104)》</p>
|
||||
|
||||
|
||||
梦倚栏杆 同学提到
|
||||
|
||||
>
|
||||
<p>从数据库或者第三方api查询类内容需要写测试吗?这种测试怎么写呢?如果不需要写,会发现大量展示类系统不需要写测试了,感觉怪怪的。<br>
|
||||
——《[16 | 为什么你的测试不够好?](http://time.geekbang.org/column/article/79494)》</p>
|
||||
|
||||
|
||||
闷骚程序员 同学提到
|
||||
|
||||
>
|
||||
<p>假设我要测试的函数是一个关于tcp的网络发送函数,我想问一下,老师在写类似这样功能的单元测试是怎么实现的?<br>
|
||||
——《[39 | 面对遗留系统,你应该这样做](http://time.geekbang.org/column/article/90231)》</p>
|
||||
|
||||
|
||||
TimFruit 同学提到
|
||||
|
||||
>
|
||||
<p>问个问题,一般web服务依赖数据库,这部分如何做好单元测试?如果去掉数据库,很难测试相应的sql语句。<br>
|
||||
——《[39 | 面对遗留系统,你应该这样做](http://time.geekbang.org/column/article/90231)》</p>
|
||||
|
||||
|
||||
大家看到了,这是一类非常典型的问题。一般来说,如果写的测试是一些业务逻辑的测试,大多数人还知道怎么测,一旦涉及到外部系统、数据库,很多人就不知道该怎么办了。
|
||||
|
||||
我们先来回答一个问题,你要测外部系统的什么?
|
||||
|
||||
你当然会说,我的整个系统都依赖于外部系统,没有了它,我的系统根本运行不起来,不能完成工作啊!但是,我的问题是你要测的是什么?
|
||||
|
||||
我知道很多人一想到外部系统,第一反应是:“我的整段代码都是依赖于外部系统的,因为外部系统不好测,所以,我这段代码都没法测了。”如果你是这样想的,说明你的代码将对外部系统的依赖在业务代码中散播开了,这是一种严重的耦合。**外部系统对你来说,应该只是一个接口。**
|
||||
|
||||
我在《[13 | 先写测试,就是测试驱动开发吗?](http://time.geekbang.org/column/article/78104)》中说过,想写好测试,先要站在可测试的角度思考。假设我同意你关于外部系统不好测的观点,那应该做的是尽量把能测的部分测好。将对外部系统的依赖控制在一个小的范围内。
|
||||
|
||||
一个好的做法就是设计一个接口,让业务代码依赖于这个接口,而第三方依赖都放在这个接口的一个具体实现中。我在《[34 | 你的代码是怎么变混乱的?](http://time.geekbang.org/column/article/87845)》中提到了 SOLID 原则,这种做法就是 **接口隔离原则(ISP)**的体现。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f9/1d/f96a509dcb40199c2c7388c2060fc91d.jpg" alt="">
|
||||
|
||||
如果你能够站在系统集成的角度思考,这个部分就是系统与系统之间的集成点。我在《37 | 先做好DDD再谈微服务吧,那只是一种部署形式》提到了 DDD。在 DDD 的战略设计中,有一个概念叫上下文映射图(Context Map),在不同上下文中集成最常见的一种模式是防腐层(Anti-Corruption Layer,ACL)。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/89/ac/89d0199dfa5b3d2c226e23549fbbe5ac.jpg" alt="">
|
||||
|
||||
很多系统在实现时就是缺少了防腐层,造成的结果就是系统耦合极其严重。因为外部服务的任何修改都会造成自己的代码跟着大幅度变动,更极端的情况是,我见过一个网关系统在自己的业务逻辑中直接依赖于第三方服务传过来的 JSON 对象,造成内存资源的极大浪费,网关本身极其不稳定。
|
||||
|
||||
至此,你知道了,**如果有任何外部系统,都要设计防腐层,用接口做隔离。**这样,才能保证你的业务代码是可测的。如果外部系统真的不好测,这种做法将大幅度降低不可测的比例,尽可能提高测试覆盖率。
|
||||
|
||||
我们前面的假设是,外部系统不好测,但真的不好测吗?
|
||||
|
||||
作为 Moco 这个模拟服务器的作者,我肯定是不会同意这个说法。如果你的系统依赖的外部系统是最常见的 REST 服务,那 Moco 就是给这种场景准备的。我给你看一个最简单的[例子](http://github.com/dreamhead/moco/blob/master/moco-doc/usage.md#api-example),这是 Moco 中最简单的用法:
|
||||
|
||||
```
|
||||
|
||||
@Test
|
||||
public void should_response_as_expected() throws Exception {
|
||||
HttpServer server = httpServer(12306);
|
||||
server.response("foo");
|
||||
|
||||
running(server, new Runnable() {
|
||||
@Override
|
||||
public void run() throws IOException {
|
||||
Content content = Request.Get("http://localhost:12306").execute().returnContent();
|
||||
assertThat(content.asString(), is("foo"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
在这个例子里,你设置外部服务的行为,让它按照你的需求返回特定的内容,然后,运行你的服务去访问这个外部服务,它和你访问真实服务效果是一样的。而且,通过 Moco,你还可以模拟出一些真实服务不可能给你做出的效果,比如,连接超时。
|
||||
|
||||
这里给出的是一个用 Java 编写的例子。如果你采用的是其他语言,也可以使用 Moco 的 [Standalone](http://github.com/dreamhead/moco/blob/master/moco-doc/usage.md#standalone) 模式,用 JSON 配置出一个模拟服务器。
|
||||
|
||||
对于数据库的测试,如果你采用的是 [Spring Framework](http://spring.io/projects/spring-framework),它就提供了一套完整的方案,比如:你可以在运行测试时插入一些数据,然后,在测试执行完毕之后,回滚回去,保证测试的可重复性。
|
||||
|
||||
事实上,它对测试的支持已经非常强大了,远不止于数据库。如果你采用的是[ Spring Boot](http://spring.io/projects/spring-boot),对[测试的支持](http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html)就更加完整了,但基础还是 Spring Framework 提供的。如果用到真实的数据库,最好是一套独立的本地数据库,保证环境的可控。
|
||||
|
||||
对于外部服务的测试,简言之,**能模拟的就模拟,能本地的就本地。**如果你的服务没有现成的工具支持,也许就是一个打造新工具的好时机。
|
||||
|
||||
总结一下。**关于外部系统的测试,你可以先通过接口隔离开来,然后,通过模拟服务或本地可控的方式进行测试。**
|
||||
|
||||
好,今天的答疑就到这里,你对这些问题有什么看法呢?欢迎在留言区写下你的想法。
|
||||
|
||||
感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给你的朋友。
|
||||
Reference in New Issue
Block a user