你好,我是华仔。

如果说IT技术领域有哪个说法最深入人心,那一定是“基础很重要”;而如果说有哪个说法让很多人花费了大量时间去学习,却没什么效果的话,那么多半也是这句话。

我相信你曾经被人谆谆教诲过:做技术,基础很重要,一定要打好基础,比如说数据结构和算法、操作系统、编译原理等等;而且很多公司面试的时候,也采用了“面试造航母,工作拧螺丝”的方式,对基础能力的考察远远超过实际工作需要。

结果,很多人费了很大的力气来提升所谓的“基础能力”,但是却发现根本看不到提升效果,工作中也用不上,白白浪费时间和精力。

难道说“基础很重要”这个说法不对吗?其实这个说法本身没有问题,但是它模糊太笼统了,很难准确地理解,再加上一些口口相传的经验误导,搞得很多人都掉到坑里去了。

这一讲,我来跟你聊聊到底什么才是“基础”,怎么提升基础技术才能事半功倍。

典型的错误观点

基础能力确实很重要,但是对于什么才是“基础”,业界并没有统一的定义。不过,有几个错误的观点流传很广,误导了很多人,其中最典型的就是以下三个:

1. 基础 = 底层

有些人以为越底层的东西越基础,比如操作系统内核(管控程序的运行),编译原理(所有编程语言的基础),CPU的指令和内存(程序运行的基础)……毕竟从字面意思来理解,底层的东西当然是基础了,而且是越底层越重要,因为越底层越通用。

2. 基础 = 源码

有些人喜欢“Show me the code”,认为只有源码才是最基础的东西,源码面前没秘密,要学基础就一定要去看源码,要自己能写出来才算真正掌握了“基础能力”。

3. 基础 = 不变

有些人认为不变的东西才是基础,比如数学、算法和数据结构、计算机组成原理、汇编语言、甚至包括离散数学和逻辑电路等,把这些学好了,以后无论做什么都用得上。

很多人抱着这样的想法去提升基础,结果却没什么效果。

我有个同事花了6个月时间去研究编译原理,感觉没什么收获,然后找我来讨论原因。

我也有位朋友花了大量的时间来看Linux内核源码,看完好像知道了一些源码,但线上出了问题之后,连Linux定位工具都不会用。

也有很多技术人员用了很多时间来背算法和数据结构的源码,但在实际工作中,要么不知道什么时候用什么算法,要么就滥用算法,明明一个很简单的逻辑也要硬套一个算法。

核心就是工作相关

要想打好基础能力,首先要明确什么才是真正的“基础能力”

我的观点是“基础能力是指工作任务相关的基础能力,不是整个计算机技术的基础能力”,核心就是“工作相关”,千万不要单纯照搬别人口中的基础能力。

基于这个观点,我们来澄清一下前面提到的几个错误观点。

1. 基础!= 底层

如果底层技术和当前的工作内容没有关系,那就不是工作要求的基础能力。

比如CPU指令和内存寻址,对于做嵌入式开发来说是基础,而对于做Android/iOS业务开发来说就不是基础了。

2. 基础!= 源码

如果当前的工作并不需要我们去修改其源码或者理解其源码细节,那就不是工作要求的基础能力。

比如Linux内核源码和Hotspot虚拟机源码,对于做虚拟机开发来说肯定是基础,但是对于Java业务开发来说就不是基础了。

3. 基础!= 不变

不变的东西确实应用很广,但是随着技术的发展,不变的东西越来越稳定,封装也越来越抽象,基本上就可以认为不再需要关注它了。

这就像电一样,我们天天用,电学的原理,你只要上过中学基本都知道。但是我相信,现在没有人在使用电器的时候,还要去翻一翻物理课本吧。

很多人为了证明“基础很重要”,都会举建房子的例子,因为地基是房子的基础。

但其实认真思考一下,就算是建房子,打地基的方式也是不断变化的,古人用夯土打地基,后来用石头打地基,现在用钢筋水泥打地基,而且工人在用钢筋水泥打地基的时候,也不需要知道“如何制造水泥”“如何炼钢”这样的基础知识。

麻省理工大学以前有一门非常火的课程,叫“计算机程序的构造和解释”(SICP,Structure and Interpretation of Computer Programs),但后来他们停止了这门课,给出的原因如下(Why MIT stopped teaching SICP):

They felt that the SICP curriculum no longer prepared engineers for what engineering is like today.
Sussman said that in the 80s and 90s, engineers built complex systems by combining simple and well-understood parts. The goal of SICP was to provide the abstraction language for reasoning about such systems.
Today, this is no longer the case. Sussman pointed out that engineers now routinely write code for complicated hardware that they don’t fully understand (and often can’t understand because of trade secrecy.)
The same is true at the software level, since programming environments consist of gigantic libraries with enormous functionality.

简单来说,就是SICP课程不是为今天的程序员准备的,而是为20世纪80~90年代的程序员准备的。

这是因为那个时候的程序员是通过组合简单和深刻理解的部件(其实就是指从底层开始构建)来构建复杂系统,而现在的程序员在复杂的商业硬件和大型的开发库上面来构建复杂系统,就算程序员想了解这些底层硬件和开发库,也可能因为商业秘密等原因无法做到。

举例说明

按照工作相关这个原则,我举两个常见的例子对比说明一下。

1. Java业务开发 vs AJDK开发

它们都是Java相关的开发,我们假设一个是用Java来在Linux平台上基于Spring Boot框架完成业务开发,另一个是要负责阿里的AJDK(基于Hotspot实现,目前已经开源)开发,那么它们的基础能力差异如下表所示:

2. Android业务开发 vs Android动态化框架

它们都是Android相关的开发,我们假设一个是做业务开发,一个是开发动态化框架,那么它们的基础能力差异如下表所示:

细化基础范围:技能图谱

明确了“工作相关”这个原则之后,提升基础的第一步,就是使用技能图谱的方式,从以下4个维度来细化基础能力的范围。

  1. 工具:工作中常用的工具,比如IDE、编程语言、问题定位工具和版本管理工具等。
  2. 生态:系统或者产品运行时依赖的所有组件或者系统,比如第三方库、中间件、数据库、文件系统和游戏引擎等。
  3. 容器:系统或者产品在哪里运行,比如Android、iOS、Linux、浏览器和云服务器等。
  4. 原理:需要掌握的原理知识,常见的有计算机网络和数据结构等。

我们以前端开发为例,基础能力的范围如下图所示:

注:

  1. 上图仅为示例,不代表完整的前端领域基础能力范围。
  2. 图中没有涵盖“JavaScript编程语言”,因为这个可以说是核心能力,不是基础能力。

有了技能图谱之后,我们就能够大致地了解每个技术领域的基础能力到底包括哪些了。

提升技术的技巧

明确了基础能力的定义和范围,我们就可以把有限的时间和精力用在更有价值的地方,避免眉毛胡子一把抓,从而实现投入产出效率的最大化。

但是,单个基础技术怎么学,这也很关键,否则学习还是可能事倍功半。

常见学习误区

常见的学习误区有两个。第一个是认为,既然是基础技术,那肯定是掌握得越深越好,比如数据结构和算法、计算机网络和操作系统这些,几乎是所有程序员的基础,所以每一项都应该深入了解。

但是这样做还是会导致你浪费很多时间和精力。

以数据结构和算法为例,很多人学习的时候都采用了背代码的方式,认为只有自己能手写这些代码,才算是真正的掌握。

而且,有些面试官在面试的时候喜欢让应聘者写简单的算法代码,进一步强化了这样的认知。

我就曾经跟几个这样的面试官聊过,对话过程几乎一模一样,很有意思:

问:“为什么你们要用这种方式来判断应聘者的水平?”

答:“如果一个程序员连个简单的算法都写不出,那就说明他肯定不合格!”

问:“那你们要自己修改算法和写数据结构吗?”

答:“怎么可能?直接用Java库里面的,自己写的质量怎么跟Java库的比啊?”

问:“一般你们考什么算法和数据结构?”

答:“冒泡啊、快排啊、链表、字符串之类的?”

问:“那为什么你们不要求手写B+树,不手写ConcurrentHashMap这些呢?这些难度更高。”

答:这个要求有点高啊,现场写不完,其实我自己也写不出……

在上面的对话中,我们可以看到,其实这种面试方式就属于“面试造航母,工作拧螺丝”,不能判断应聘者水平高低,只能反映出他们面试准备程度的高低;而且能考的也就是这几个简单的数据结构和算法题目,因为面试官自己也只能看懂这几个。

第二个误区是认为,要完全掌握基础,一定要掌握源码

这个观点更加容易导致你投入大量时间却没什么收获。尤其是Linux内核源码、JVM虚拟机源码和MySQL源码这些,如果你不具备深厚的C/C++的开发功力,基本上连看都看不懂,更不用说考虑代码规模和复杂度了。

即便是Netty这些代码相对少一些的开源项目,就算你拥有很强的Java开发技术,要想每一行代码都了解,也要花非常多的时间的。

因为一个成熟的开源项目,都是几十个人用了很多年的时间慢慢积累的,你一个人想一下子就全部搞懂所有代码,这是不现实的。而如果你把时间浪费在这个地方,用来提升其他更有用的技术的时间就没有了。

如何判断学习深度?

所以说,就算是同一个基础技术,不同的技术人员学习的深度也是不同的。核心的原则还是之前提到的“工作相关”,根据工作内容来决定基础技术的学习深度。

下面,我举几个常见的例子来说明。

1. 数据结构和算法

对于绝大部分开发人员来说,主要是熟悉数据结构和算法的原理、优缺点与应用场景,还有自己所用的编程语言提供的算法和数据结构。

而对于中间件开发的技术人员来说,在做极致的性能优化的时候,Java的ConcurrentHashMap之类的并发数据结构,就需要掌握算法的原理和代码实现细节了。

2. 计算机网络

对于绝大部分开发人员来说,能够熟练掌握抓包工具抓取TCP/IP包,并且能够看懂包信息,定位网络问题就行了。

而对于运维人员来说,抓包、路由协议、组网配置等就需要深入掌握了。

3. 操作系统

对于绝大部分开发人员来说,掌握基本的操作系统原理和概念,能够使用操作系统提供的工具来定位程序问题就行了。

而对于驱动开发、内核模块开发的技术人员来说,操作系统原理、实现机制和代码都需要深入掌握。

如何让理解更加深入?

明确学习深度之后,因为基础知识点比较多,看起来比较散,所以你可能学了很多知识,但是不知道它们之间的关联关系,理解不够全面和深入。

应对这个问题办法就是第19讲中介绍的“链式学习法”,通过领域分层将基础技术和顶层的实用技术关联起来,形成系统化的理解,这样能够理解得更深,记得更牢固。

基础积累会不会浪费?

看到这里,你可能会有疑问:判断基础能力范围和基础技术学习深度的原则都是“工作相关”,那么如果工作发生变化,岂不是很多基础技术的积累都白费了?

这里就要看所谓的“变”具体是怎么变。

如果是前后两个工作的领域基本一致,那么基础技术的积累基本上是可以通用的。比如我曾经从PHP服务端开发转为Java服务端开发,在数据结构和算法、计算机网络、数据库和操作系统方面的积累完全可以通用。

但如果前后两个工作领域差异很大,那么基础技术的积累确实可能无法通用。比如我的一位同事从Android开发转为服务端后台开发,虽然数据结构和算法、计算机网络可以通用,但是SQLite数据库和Android操作系统这些就不能通用了。

所以跨领域转岗一定要慎重,要转的话就尽早转,越晚损失越大。

我的实际经历

我刚去UC的时候,是用C/C++做中间件,对高性能和网络都有比较高的要求。于是我深入地学习了CPU和网络的一些基础知识,最典型的就是SMP架构的CPU 的False Sharing(伪共享)问题。

这个知识点理论上属于计算机组成原理,但是计算机组成原理一般只会写CPU有L1/L2/L3 Cache,很少提到多核CPU的的Cache Line(缓存行)对齐会导致False Sharing问题,并且对性能有很大影响。MySQL也被这个问题给坑过,而Disruptor的高性能则是采用padding避免了这个坑。

你看到这里,可能会急着想去看看我说的False Sharing到底是什么。别急,这个基础知识点在我后来负责业务开发的时候就没用了,因为接触不到这个深度。

但是做业务开发的时候,MySQL的索引原理和Elasticsearch的倒排索引这些基础理论就很有用了,因为你要设计合理的索引和存储方案。不过这个时候,你也不需要把B+ tree的数据结构写出来,只需要知道原理,就可以设计合理的索引和存储方案了。

小结

现在,我们回顾一下这一讲的重点。

  1. 基础能力是指工作任务相关的基础能力,不是整个计算机技术的基础能力。基础不等于底层,不等于源码,也不等于不变。
  2. 提升基础的第一步,就是使用技能图谱的方式,从工具、生态、容器和原理这4个维度细化基础能力的范围。
  3. 提升基础技术的技巧包括:根据工作内容来决定基础技术的学习深度;通过链式学习法将基础技术和实际用到的技术系统串起来;跨领域转岗要慎重,要转的话就尽早转。

思考题

这就是今天的全部内容,留一道课后思考题给你吧。按照这一讲的内容,你能够整理出你当前工作岗位要求基础技术包括哪些,以及你需要学到怎样的深度吗?

欢迎你把答案写到留言区,和我一起讨论。相信经过深度思考的回答,也会让你对知识的理解更加深刻。

评论