在啃掉一本本计算机经典书籍和写下大量代码以后。
疫情原因回不去学校,作为一个马上毕业,即将入职腾讯的大四生,分享一下自己的学习历程吧。
本人在大学之前从未接触过编程,最开始的编程学习还是在高考完后,从书店买了本C Primer Plus,然后暑假开始啃,前前后后也就看了几十页。
大一上的时候,来到了华中师范大学,还没有转专业到计算机,一直在自学C语言和看一些计算机入门书籍(编码、计算机科学概论)。当时也很迷茫,不知道以后道路如何,所以也学了一些杂七杂八的东西(前端 python啥的),所幸的是,当时坚持把C Primer Plus结结实实地精读了一遍,而且几乎练习题都做了,算是比较好的开端。
大一下,转专业到计算机了,开始自学数据结构,算法和C++,部分看完了 数据结构与算法分析,并且把书上的数据结构实现了一遍,记得当时五月份给自己的flag是看完C++ Primer,然后每天上课看,晚饭吃完后也跑去七号楼刷书,最后囫囵吞枣似的看完了大部分。
大一暑假,txr大佬 @杏仁糖给我说他面试通过了华科的联创团队Unique Studio,而且给我说他们团队都特别厉害,有些人在军训的时候就把C++ Primer给蹲着看完了,当时十分钦佩,幻想也能够进入贵团队。于是打算在大二上的时候,去报名他们的秋招。所以,那个暑假在学校自学,呆了五十多天。最初,拿起一本APUE,看了一章后感觉看不懂又放下了。然后,又拿起一本红色封面的算法第四版,这本书看完了,并且用C++把上面的算法都实现了一遍。另外,听知乎大佬说CSAPP是必看的神书,当时也懵懵懂懂地看了前面三章,做了lab。为了学习linux,还看了一本叫linux命令行大全的书。武汉的夏天很热,只能寂寞待在宿舍的我,打开了LeetCode的世界,写了一百多道题。
大二上,十月,忐忑迎来了联创的面试,前面两轮都过了,直到其特色的“熬夜测试”环节,因为实力不足+精力不足,测试败北,没有通过。清晨七点,在回学校的公交车上思考自己不足,总结是知识体系仍然不够完全,而且深度也不够。后来这个学期有点“误入歧途”的意思,入了Machine Learning的坑,记得当时花了好几个月刷完西瓜书和吴恩达机器学习课程的讲义(真佩服当时的毅力,都是英文和公式推导),后来想了想,ML/DL这个东西有点玄学,于是毅然决定成为一名做工程的程序员。总的来说,虽然这个学期的时间都投入到ML的学习中,没有学习工程方面的东西,只有用python写了一点好玩的爬虫,但是给了我以后坚定走工程方向的决心吧。
大二下,这个学期是打下计算机知识基础的关键时期。从知乎找了一系列书单,看完了CSAPP(做了lab,为了bomb lab还通宵了,强迫症想让自己把炸弹都拆了)、半本算法导论、effective C++、计算机网络系统方法(前三章)、部分TCP/IP详解等,做了一些项目,比如正则引擎(这个是参考了轮子哥的教程)等等,尝试写JSON库(未遂)。其实,因为学校只是一个普通211,找到好工作的学长学姐先例很少,一直是没有信心自己能去大厂,直到当时了解到myk @孟永康学长在春招收割了一系列offer后,才有了些许信心,相信自己如果能够像他那样努力,那么也能够找到好工作(感谢myk学长那时的鼓励和指导)。所以,定下了一年把自己水平提升到能够在大三下春招的时候拿下大厂offer的目标。
大二暑假和大三上也一直为这个目标奋斗,补上了APUE、部分UNP、深度探索C++对象模型、STL源码剖析、操作系统概念、操作系统真象还原、Linux多线程服务端编程、部分C++ template、部分C++ Concurrency in Action等书籍。然后,也一直没有放弃刷题,LeetCode写到了三百多道。同时也做了一些项目,跟着操作系统真象还原写的操作系统、简单的协程库,阅读了一些优秀的开源项目,SGI STL、muduo、libgo、libco等。中途还对haskell产生过兴趣,打印了一本Learn Yourself Haskell For Great Good,看完后感觉坑太深,还是专注找工作吧,haskell对我一直都有很强的吸引力,但是这种也只能作为爱好吧。另外,编译器也是有这种魔力,看了部分编译原理(龙书)、部分现代编译原理(虎书)和部分Engineering a Compiler,多次尝试写自己的编译器,多次未遂(不过现在有空了,在补上之前的烂尾项目)。
时间到了12月份,9号楼某个自习室内,txr大佬一直鼓励我去投简历找实习,本来我一直畏畏缩缩,对自己不太有信心,被鼓励后想着投就投吧。当时投了字节跳动和momenta的实习,面试都比较顺利,于是在大三上就在字节跳动开始了愉快的实习,比自己的计划早了大半年。当时为了准备面试,看了好多牛客网的面经,查漏补缺式地补齐自己的知识盲区。
大三下,基本都是在实习,学习了很多工程实践上的东西,接触了go的技术栈,并且对devops和软件工程方面有了认知,包括代码管理、发布流程、微服务啥的。然后实习的时候骑驴找马,找到了腾讯的暑期实习。因为这个时候没有了找工作的压力,所以开始学一些自己感兴趣的东西,包括Rust(至今水平还是不太行)、分布式系统(DDIA、MIT6.824等),零零散散学了点东西。
大三暑假,腾讯实习了两个月,开始学习kubernetes、各种中间件等实际业务用到的东西,这个时候就感觉拥有到扎实的计算机基础是最重要的东西。
最后,顺利拿到了腾讯转正和字节跳动的秋招offer,两家都给的比较高,选择了工作体验更喜欢的腾讯哈哈。
总的来说,学习计算机是需要积累的,花一两年时间啃下那些厚厚的经典书籍后,才能构建自己的知识体系,然后阅读大量优秀源码,做一些有趣的项目,编程能力就能突飞猛进啦。
现在回头来看,大学时光真是如此宝贵,这是人生中为数不多可以静心学习的一段时间,可以不用考虑任何其他事情,每天学习十多个小时。
回顾一下,我的技术能力(不仅仅是编程,而是解决问题的能力)的进步大约有几个重要的节点:
1. 刚入行时的入门练习题
这个是当年狼厂网页搜索部门的传统,不知道现在还有没有,入职第一个任务是完成两道练习题。一题是在Linux上用纯C(不许用C++,没有stl可用)完成一个多线程的网页抓取器,另一题是同样的环境语言完成2G大小的query(搜索查询词)的top100提取,有时间要求。
入厂之前我在Linux上没有写过代码。
两天时间从Linux基本命令的熟悉,vim gcc gdb的使用学习,从malloc和free开始搞内存管理,从0开始码hashtable(还得自己写hash函数),从socket开始实现http client和各种协议码解析,使用pthread多线程和信号量互斥同步,基本把操作系统计算机网络复习了一遍。这两天把我四年本科三年研究生没搞扎实的工程基础全部补上了。结论:有目的的练习,尤其是一个完整的应用问题的解决,是学习的不二法门。
2. 接手一个完整的模块(子系统)
搞完入门练习,leader过来跟我说:小胡,现在有一个重要的模块交给你,赶紧熟悉一下,然后完成如下功能升级。
这个模块是前厂存储网页的核心模块,在当年内存4g的奔腾主机上单机存储几千万网页,几台机器存下了当时整个中文互联网。支持高性能的随机存取和顺序读,可以说把机器性能压榨到了极致。3w行,纯C。
啃了几天终于搞明白了结构,(多年以后我还得感谢source insight),同时也对写这个模块的大牛佩服的五体投地,为了压榨内存把每一个bit都物尽其用,各子模块之间的分工又是那么的优雅。
然后是上手改,看懂了之后功能升级很简单,只改了十几行代码,但上线的时候真是手发抖!
后来才知道这个模块好几个前任都没成功接下来就被fire了….
后来又经历了若干次升级,解决各种诡异bug(搞过高并发存储系统的应该知道坑有多深),编码和解决问题的能力突飞猛进。
结论: 学习系统设计的最佳途径是看一个优秀设计的源码,检验成果的方式是改造它应用于你的实际场景。
3. 接手一个完整的系统
搞定存储模块之后,我的下一个任务是升级喝扩展一个完整的抓取系统。
和入门练习做的抓取器不同,这个完整的抓取系统(又称spider)是工业级的,需要每天完成千万级的抓取量,还需要考虑并发压力控制,网页更新调度,垃圾网页处理,去重等等诸多现实的工程问题。
大大小小十来个模块,十来万行代码,大部分是C,还有接近一万行的bash脚本(用bash实现分布式的网页去重处理你信?)
这时会发现很多模块内部都有不少实现不尽如人意的地方,但是由于借口定义的好,模块直接容错性强,整个系统还是work的。
这促使我思考系统级的架构,最需要关注的重点是什么,良好的结构远胜于细节的雕琢。
大约小半年后,我对这个系统基本做到出任何问题能立刻反映到是那个模块出的问题,对问题的分析定位能力又上了一个台阶。
结论: 理解了接口定义和系统结构重于实现细节,就迈出了架构师的第一步
4. 设计一个完整的子系统
此时大概工作快两年了,原来的抓取系统有一个很大的问题,就是积累的网页数太多,更新资源分配不过来,导致系统中大量网页在互联网上已经404但仍然会进到线上被检索出来。我们称之为死链接问题
通过对死链的规律分析,我发现互联网上大部分死链存在站点或目录级聚集的现象,这个其实很好理解,一个网站无力维护了,自然就全部挂掉,目录级很可能是网站改版了,或者一个子频道关闭了。利用这个规律,我们可以大幅度降低死链检测的资源耗费。
在这个认识的基础上,我设计了一个独立的死链检测系统。上线效果很不错,检查死链的流量开销降低到原来10%,网页库中死链还下降了。
结论: 架构师首选要解决的是待解决问题的精确描述,和对问题域的分布规律的挖掘,然后才是结构设计。
5. 设计一个新的系统
工作第四,五年间,hadoop在业界逐渐流行起来,基于Google三件套的设计,当年的hadoop最上层的table还很不完善,但是mapreduce和hdfs已经很可以用了。 如何利用分布式基础框架改造系统,让系统更健壮(以及永更廉价的硬件給公司省钱),成了当时的一个重要问题。
整个抓取系统和建库系统的分布式改造,相当于重新设计一个新的大系统。需要考虑方方面面,如何逐步升级兼容原有系统?如何保证功能的完整性?原有设计中有一些不合理的地方,如何利用这次迁移同步改造?
主导完这些工作后,系统架构方面再也没有遇到搞不定的问题。
结论: 好的架构师需要在合适的时机解决重要的问题,业务发展才能给你这样的机会。上面5个节点,大致是T3 T4 T5 T6 T8的水平。
大学时浑水摸鱼,毕业后大约半年,在网络上寻找自学资料时,发现了GitHub和一些程序员论坛,自学了py,然后打开了新世界的大门。
包括购买了梯子,独自啃下两套纯英文官方文档,在GitHub上友好亲切的交流,刷题,看最新的软件工程实践书,等等断断续续的收到一些赞,写一些可能对大家有帮助的东西吧:
1、提高知识最好的途径是读书,包括但不限于计算机四大名著《操作系统》《计算机网络》《数据结构》《计算机组成原理》。经典书籍不仅仅有完善有效的知识,更重要的是这些书里的计算机经典思想,一直到现在都没有过时,十分的有用。
2、Github是个好网站,值得习惯性的到上面搜索需要的东西。比如awesome系列和一些质量很高的资料整理项目(整理常见的面试、刷题、常见工具链的git repo)
3、v2ex和知乎都是很好的网站(尽管审核方面被吐槽很多),论坛上有很多很厉害的大佬,关注认真答题大佬的动态对开拓知识领域是有一些帮助的。
4、善用搜索引擎(专指Google),最好能把自己最常用的工具的官方文档啃一遍。主要是习惯英文文档的表达方式+系统的认识自己所用的工具,对自己写文档也是有很多帮助的。
5、因个人有长期的抑郁经历,所以对心理方面关注得也比较多,十分推荐学习《哈弗大学幸福课》,观看地址https://www.bilibili.com/video/BV1Gs411o71d?p=1。这门课的综合内容十分丰富,实践性很强,能够有效的提高生活、职场中的软技能(沟通能力、抗压能力等)(这门课另外一个很厉害的地方在于,仅仅是上课的方式,也能提高你的逻辑思维能力,毕竟据说是哈弗学生选修最多的课,课程质量极其的高)。
6、有一个我自己打算做但实践得还不太好得地方:看经典开源项目得源码和架构。只看过一些小的玩票性质的开源项目,也学到了不少东西,大型的经典的开源项目还没能看(STL、Git等),这些项目对我来说主要是太庞大,时间上有些不够用。
7、最近在坚持做Leetcode每日一题,也推荐大家一起刷呀。保持手感,很多题看起来简单,但评论区也能看到有趣的知识。
8、关于职业规划这种事情,我以前是没有的(主要还是抑郁+低自尊影响的吧)。现在获取的信息太多太多,没有职业规划很容易被带偏。我是在做了长期的心理咨询+《哈弗大学幸福课》,工作、生活上都有一些经历了之后才慢慢想清楚自己将来的生活与工作要怎么打算。这种事情想清楚过后,做自己想做的事情目标会比较清晰,不会得过且过。感谢阅读,祝好。
相比于很多技能,编程有一个突出的特点,那就是很难“熟能生巧”。
也就是说,作为一名程序员,不思考,不总结,写再多的程序,吃再多的亏,你可也无法成长为一名高水平的程序员。道理并不难理解。
编程不是体力劳动,而恰恰是少有的,一种近乎纯粹的脑力劳动。所以,也并不存在所谓的肌肉记忆。而且,因为it技术的日新月异,很难有一门具体的编程语法能通过熟练而积攒下来。而贯穿整个程序员职业生涯,能够沉淀下来,愈久弥香的东西,一定是超越具体语言,形而上的东西。而这些东西,除了学习前人的经验,首先必须是在大量实践之后,遭遇了困境,主动求索才能得到的。
例如,相对于具体的编程语言,设计模式是一个更高层次的东西,重要性不言而喻。
但是,这并不意味着,人一开始就应该去阅读那些经典的教程(例如GOF的《设计模式》,《Head First设计模式》),从而掌握设计模式。事实上,如果没有实践的历练,以及之后的求索,就是给你一个有经验丰富的老师来为你传授设计模式,你也无法真正掌握,正应了那句箴言:“不是你自己悟出的道理,给你,你也握不住”。
因此,学习设计模式的正确之道,就是在大量实践之后,遭遇了现实的困境,有了自己的朦胧思考之后,再来阅读经典教材,然后才会有“原来如此”的感悟。
而且这个过程还是反复的。是一个不断实践,不断思索,不断领悟的过程,也根本不存在一个所谓的“顿悟”点。
而应对本问题,对编程来说,也根本不存在一个清晰的时间点,人就突然领悟了某个“绝世秘籍”,从而水平大增。
而且,不仅设计模式如此,再往上的架构设计等依然如此。总体来说,编程水平的提高,伴随程序员整个职业生涯,都是一个延绵不绝,不进则退的过程。这既是程序员职业的痛苦所在,也是它的魅力所在。
自学编程的二把刀强答一波
我学习的路子更像学院派,从linux和C语言学起,看操作系统原理、数据结构、算法导论。确实感觉自己有这么几个质变的飞跃点。
1. 当我在linux的终端第一次成功编译和运行C语言版的hello world时。为了做到这一步,我克服了很多困难,首先是理解linux下命令的运行原理,了解什么是path,然后理解了linux上的那些命令和我写的hello world从本质上讲并没有区别。为了编译,我安装gcc,明白了包管理相关的概念。之后这个经历很快帮我理解了第三方库的概念、版本管理等问题,让我在之后快速扩展打下了坚实的基础。从0到1是最难的,为了实现这一步,我实际花费了将近一个月的时间。
2. 在《程序员的自我修养》里看到这么一句话时:抽象级别再高的编程语言,最终都要转化为二进制洪流,运行在电路板上。可以说是醍醐灌顶,让我在之后写代码的过程中,总是忍不住去追问这些代码最终可能转化为什么样的二进制流,怎么运行在电路板上。这之后促进我初步了解了计算机的硬件结构,了解了一行代码从写出来、编译后、运行起来,又怎样透过网络传输,以及最后显示在屏幕上经历的全过程,对我之后的帮助很大。这之后我又有机会研究了无线网的各种传输协议,无线路由参数详解www.history-of-my-life.com
那时候就似乎看到自己写的代码从文本转化为二进制,从二进制转化为电磁波,突破了那个机箱和屏幕,弥漫于我们所在的空间之中,成了可感受的实物。对于理工男来说,没有比这个更浪漫的事情了。感受写代码的浪漫,是促进我不断前行的重要原因。
3. 自学结束,开始步入工作时。第一次接触一个实际的项目时,才发现书中的东西过于基础(基础很美也很重要,我仍然喜欢不断探究基础的东西),而现在各种基础工作其实有大量的框架和第三方库做了,我们不需要处理,这样从一些基础的东西脱离出来,快速搭建项目,一些对工作更重要的技能开始凸显,比如说数据库、缓存、测试等等。这时候从掌握大量基础概念,到快速完成一个项目,并产生实际的价值,这又是一次质的飞跃。
当然,学习很难一帆风顺,我和初入程序员届的师弟师妹聊天,和已经大厂P7T3聊天,发现编程的路就像人生,每个阶段都有自己的追求,也有自己的茫然,然而更多的时候,没有做不到,只有不愿意去做,只要你有坚定的目标,持之以恒地去做,终究没有什么能拦住你的脚步,即使某一段时间再痛苦,也总能柳暗花明,到达新的一个阶段,那一瞬间的美好,就是对自己最大的肯定和奖赏。
没有觉得自己编程能力突飞猛进过,但是有过一个转折点,作为程序猿的水平突飞猛进过。
那个转折点就是,我本来在一个十多个人的大组里面当个小透明,突然一两个月内组里面所有比我资深的人都走光了,只剩我和另外两个新来的。瞬间,我半路接手了十来个项目。
那个时候我还很年轻,不太能接受失败。于是我就硬抗下所有项目。本来,我只用写写文档写写代码。那段时间我得搞清楚那一大堆项目的前因后果,为什么要做这些项目,已经做了哪些,项目还有哪些stakeholder,为什么当时要这么设计,我如果看不惯还有没有改的余地,我要和哪些人沟通这些项目,我需要什么样的资源来做这些项目,我如何培训那两个新来的一起把这些项目做完,等等等等,做了很多“基本程序猿”以外的事情。
总之,我被赶鸭子上架当了一回“leader”,而且是一次性十来个项目。虽然我写代码的功夫没啥变化(一直不算差),但是那段时间软技能得到了极大提高,对整个行业的认识也有不少提升。
PS. 当时我也思考过要不要跟随组的大部队一起跑路,后来觉得一次性拿到这么多项目更能锻炼自己,真要跑路做完了再跑。