怎么样持续提升自己的编程能力?
说一个场景,你大概率经历过。
凌晨一点,宿舍其他人都睡了,你戴着耳机对着屏幕,终于把一个困了你两天的 bug 修好了。那一刻你往椅背上一靠,摘下眼镜揉了揉眼睛,心里有一种很安静的满足感 ------ 不是狂喜,就是一种 "我搞定了" 的笃定。然后你保存,提交,关掉电脑。躺在床上的时候忽然想起来,其实这个 bug 的原因很简单,你只是在一个地方少写了一行边界判断。你花了两天时间,只是因为你当时不知道这件事。
编程能力的提升,大概就是不断缩短这种 "不知道" 到 "知道" 之间的距离。但难的地方在于 ------ 你永远不知道自己还有多少 "不知道"。你以为自己站在五楼,推开一扇窗,发现这栋楼有五十层。你以为自己快到山顶了,转过一个弯,看见前面还有连绵的山脊,消失在云雾里。
这种感觉,既让人沮丧,又让人着迷。
我讲讲自己这两年多摸索出来的一些东西。不一定对,但至少是真实的。
先回答一个前置问题:为什么很多人 一直在学 但进步很慢
我观察过身边不少同学,包括我自己一段时间的状态,发现了一个共同的陷阱 ------
用 "输入" 代替 "练习"。
看教程、看视频、看书、看别人的代码 ------ 这些都是输入。输入很重要,但输入本身不产生能力。你可以看一百个游泳教学视频,下了水该呛还是呛。
我大一的时候疯狂狂看技术博客,收藏夹里存了上千篇文章。每天都觉得自己 "学了很多"。但有一次参加一个编程比赛,题目不难,我坐在那里半个小时写不出来,手指放在键盘上就是不知道从哪里开始。
那天晚上回宿舍的路上,天很冷,我把手插在口袋里走了很久。不是难过,是一种更复杂的东西 ------ 我意识到自己一直在用 "学习的姿态" 逃避练习的痛苦。
看教程是舒服的,因为你一直在 "懂"。写代码是痛苦的,因为你一直在 "不会"。人本能地会逃避那种 "我不会" 的挫败感,所以你不自觉地把时间花在了让自己感觉良好但实际上没什么用的事情上。
后来我逼自己定了个规则:每看一个小时的技术内容,必须写至少半个小时的代码来练。不是跟着教程抄,是关掉教程,凭自己的理解从零写。写不出来的地方,那才是你真正需要学的地方。
这个习惯可能是我这两年做过的最正确的决定之一。
第一件事:找到你的 "刻意练习" 场域
"刻意练习" 这个词被说烂了,但大部分人理解的刻意练习是错的。刻意练习不是 "重复做你已经会的事情"。你刷了三百道 LeetCode 简单题,编程能力不会有本质提升。因为你只是在重复已经掌握的技能,大脑不需要建立新的神经通路。
真正的刻意练习是持续做那些 "刚好超出你当前能力边缘" 的事情。你够一够能摸到,但必须踮脚 ------ 这个区间才是成长发生的地方。
具体怎么找这个区间?几个对我有效的方法 ------
写项目,写真实的项目
不是那种 Todo List、计算器、天气 App 这种教程项目。是你自己真正想用、真正有需求的东西。
这个项目没有任何教程教你怎么做。我需要自己想数据怎么存、接口怎么设计、命令行参数怎么解析、怎么生成对比图表。每一步都是现学现用 ------ 学 SQLite、学 ClickHouse、学 matplotlib 的非交互模式。
整个过程很慢。写了大概三个星期,中间推翻重构了两次。但写完之后,我对 Python 的项目组织、数据库操作、CLI 设计的理解,比之前看十篇教程都深。
因为你在解决一个真实的、边界模糊的、没有标准答案的问题。这跟做作业是完全不同的心智活动。
读一个开源项目的源码,然后给它贡献代码
这是另一个非常好的练习场。不需要一上来就给 Linux 内核提 PR。找一个你正在用的、中等规模的项目 ------star 数在几百到几千之间的那种,通常维护者比较友好,代码库也不会大到读不完。
先从 issue 列表里找一个标记了good first issue的任务。通常是修一个小 bug 或者加一个小功能。为了完成这个任务,你需要:读懂项目的代码结构、理解它的设计约定、跑通测试流程、写出符合项目风格的代码、提交 PR 接受 review。
这个过程里你学到的东西 ------ 怎么读大型代码库、怎么遵循已有的架构设计、怎么写出 "符合上下文" 的代码 ------ 这些在任何课程里都学不到。
我去年给一个 PyTorch 生态的工具库提过一个 PR,改了一个数据加载的性能问题。改动本身不大,大概四五十行代码。但为了搞清楚应该怎么改,我把相关模块的源码翻来覆去读了好几遍,光理解它的多进程数据管道就花了两个晚上。PR 提交之后,maintainer 给了好几轮 review 意见,有些地方他建议的写法我完全没想过。比如一个地方我用了try\-except来处理异常,他说这里应该用contextmanager来确保资源释放 ------ 然后贴了一段代码示例,我看完之后愣了几秒,心想这才是写代码啊。
那种被更强的人 review 的体验,是独自学习永远给不了你的。
第二件事:建立「反思 - 记录」的闭环
练习产生经验,但经验不会自动变成能力。中间需要一个环节 ------ 反思。
我从下学期开始养成一个习惯:每周五花半个小时写一段 "编程周记"。不用写很多,就回答三个问题:
-
这周写代码时遇到的最难的问题是什么?我怎么解决的?
-
有没有什么地方我觉得自己写得不够好?如果重来我会怎么改?
-
有没有看到别人写的代码让我觉得 "原来可以这样" 的?
这个笔记本现在已经积累了一百多条了。偶尔翻回去看,能很清楚地看到自己的成长轨迹 ------ 半年前觉得很难的东西,现在已经变成了直觉;半年前犯过的错误,现在不用想就能绕过去。更重要的是,它帮我识别出了自己的能力盲区。
比如翻了几周的记录之后,我发现一个模式:我遇到的大部分难题都跟并发有关 ------ 竞态条件、死锁、多进程数据共享。这说明并发编程是我的薄弱环节。于是我专门花了两周时间系统学了 Python 的multiprocessing和threading,读了concurrent\.futures的源码,写了几个小的并发程序来练手。
如果没有这个笔记本,我可能永远意识不到这个盲区。因为每次遇到并发问题我都是 "查一下 StackOverflow 然后糊弄过去",没有哪一次痛到让我专门去学。但当你看到同一类问题反复出现的时候,你就知道该系统性地补课了。
成长有时候不是向前跑,是停下来看看自己的脚印。
第三件事:读代码,像读文学一样读
这个说法可能有点矫情,但我是认真的。
好的代码是有美感的。就像好的文章一样,它有节奏 ------ 复杂的逻辑被拆成几个步骤,每个步骤刚好是你能一口气理解的长度。它有命名的美感 ------ 变量名准确到你不需要注释就知道它是什么。它有结构的美感 ------ 模块之间的边界清晰,职责分明,你能感受到写代码的人在动手之前想清楚了整体的骨架。
我推荐几个我读过觉得写得漂亮的项目,值得反复看:
-
requests****(Python 的 HTTP 库):Kenneth Reitz 写的代码有一种罕见的优雅。API 设计得像自然语言一样直觉,内部实现也极其干净。它是 "什么叫好的 API 设计" 的教科书。 -
redis****(C 语言写的):antirez 的代码风格简洁到极致。每个函数都很短,命名极其清晰,注释写在最有价值的地方。如果你想知道 "C 语言也能写得这么好读",去看 redis。 -
flask****(Python Web 框架):早期版本的 flask 代码量很小,但架构设计极其精巧。特别是它的上下文管理和蓝图系统,用很少的代码实现了很强的扩展性。 -
pytorch 的 autograd 模块:计算图的自动微分实现。代码不长,但信息密度极高。读完你会对 "什么是计算图""反向传播到底怎么实现的" 有完全不同层次的理解。
读源码的时候我有个习惯 ------不只是读 "它写了什么",还要想 "它为什么这么写" 以及 "它选择不做什么"。
那些没有被写出来的代码,有时候比写出来的更有意思。为什么这个函数不处理这种异常情况?为什么这里不做缓存?为什么这个类不提供这个接口?
每一个 "没有" 的背后,都是作者做的一个设计决策。理解这些决策,比理解代码本身更有价值。
这有点像读小说。江南写龙族,很多最动人的东西不在他写了什么,在他没写什么。路明非在某些时刻的沉默,给梨衣某个没说出口的句子 ------ 留白才是最重的笔墨。
好的代码也是这样。克制本身就是一种能力。
第四件事:跟人交流,尤其是跟比你强的人
我知道很多程序员 ------ 包括我自己 ------ 天然倾向于独处。一个人对着屏幕,安静地写代码,这是舒适区。但编程能力的提升,有很多时候是社交行为。
Code review 是最高效的学习方式之一。不是你 review 别人,是让别人 review 你。当一个更有经验的人看着你的代码说 "这里为什么不用生成器?""这个异常处理的粒度太粗了""这个变量的生命周期太长了"------ 每一条这样的意见都是一次浓缩的经验传递。
如果你周围没有 code review 的文化,去开源社区提 PR 就是一种 code review。维护者给你的 review 意见,可能是你在任何课程里都买不到的个性化教学。
上周跟一个朋友吃饭,他随口提了一句他们项目里用了 "事件溯源" 模式来做数据一致性。我当时不知道这是什么,回去查了一下,发现是一个很有意思的架构思想 ------ 不存储当前状态,只存储所有状态变更的事件序列,当前状态通过重放事件来重建。一顿饭,一个关键词,引出了我两天的学习。这种事情发生过很多次。懂你上下文的人,有时候一句话就能帮你捅破那层窗户纸。
人是最好的搜索引擎。算法再好,也不知道你此刻卡在哪里。
第五件事:学会 "慢下来"
这个可能是最反直觉的一条。
我们这个行业有一种隐性的焦虑 ------ 技术迭代太快了。上个月还在学 LangChain,这个月就出了 LlamaIndex;你刚搞懂 LoRA,QLoRA 就来了;论文看不完,框架学不完,新概念记不完。
这种焦虑会驱动你做一件很糟糕的事情:什么都学一点,什么都不学深。
你的收藏夹越来越长,你的技能树越来越宽,但每一根枝条都只有几厘米长。遇到真正需要用的时候,没有任何一根能撑住你。我有一段时间就是这种状态。每天刷 Twitter 看到新东西就焦虑,觉得自己落后了,赶紧去学。学了一天发现又有更新的东西出来了,再追。像是在追一列不断加速的火车。
后来是我老师的一句话把我拉回来的。他说:"你不需要跟上所有东西。你只需要在一个方向上扎得足够深,深到别人遇到这个方向的问题时会来找你。"
这句话我想了很久。他说得对。你看那些真正厉害的程序员 ------ 不是什么都会的人,而是在某个领域深入到一定程度的人。Linus Torvalds 不需要会写 JavaScript,Guido van Rossum 不需要懂深度 Cuda 编程。但在他们各自的领域里,他们的理解深度是普通人望尘莫及的。
广度让你有话题聊,深度让你有价值。
所以我现在的策略是:用 20% 的时间保持广度(每周扫一扫领域内的新东西,知道它们存在就行),用 80% 的时间深耕一两个方向 。
具体到操作层面:
-
选一门语言,用到真正深入。不是 "会用" 的程度,是理解它的设计哲学和底层机制的程度。你用 Python,就去搞清楚 GIL 是怎么回事、描述符协议是什么、import 系统的查找顺序、asyncio 的事件循环怎么跑的。这些东西日常开发不一定用到,但当你需要它们的时候 ------debug 一个诡异的性能问题、设计一个高并发的服务 ------ 它们就是你的底牌。
-
选一个领域,持续跟进。不管是分布式系统、图形学、编译器、还是我做的 AI 方向。订阅这个领域的核心会议和期刊,关注几个核心的开源项目,跟几个在这个方向的活跃开发者或者学者。半年之后,你会发现自己对这个领域的 "直觉" 变强了 ------ 看到一个新论文,你能很快判断它是真有料还是新瓶装旧酒;遇到一个问题,你脑子里会自动浮现几个可能的方案。
这种直觉是最值钱的东西,但它只能用时间换,没有捷径。
第六件事:接受 "高原期"
最后说一个很少有人提但每个人都会遇到的事。
编程能力的提升不是线性的,它更像爬山 ------ 一段时间你进步很快,觉得自己每天都在变强;然后突然进入一个平台期,怎么练都觉得没长进,写的代码还是那样,解决问题的速度也没变快。这个时候最容易放弃,或者焦虑到去追新的东西,觉得 "是不是我学的方向不对"。
我经历过两次比较明显的高原期:
第一次是大一。我已经能熟练地写各种课设和小项目了,LeetCode 中等题基本都能做出来,但就是觉得自己 "就这样了"。代码能跑,也说不上哪里不好,但总是觉得缺点什么 ------ 像是一个人在学写字,笔笔画都对,就是没有力道。那段时间很闷,每天都在写代码,但感觉像在原地踏步。
后来是一件偶然的事情打破了这个僵局。我去网上听了一门系统设计课,课上老师让大家分析 Redis 的架构。我第一次从 "系统" 的角度看一个软件 ------ 不是看单个的函数怎么写,而是看数据流动、模块划分、容错机制、性能与简洁性之间的取舍。那门课像是给我换了一副眼镜。我突然意识到,我之前一直在 "代码" 的层面努力,但真正的瓶颈在 "设计" 的层面。我的单个函数写得不差,但我不知道怎么把十几个模块组织成一个协调的整体。高原期不是你不够努力,是你当前的认知框架已经触顶了,你需要一个新的视角来打开上面的空间。
第二次高原期是今年。代码上也没什么突破。有一段时间我每天到课室 就打开 VSCode,对着屏幕发呆,不知道该写什么。那段时间我做了一件事后来证明很有用的事 ------我停下了学新东西,转而去重构自己以前的项目 。把从前写的代码翻出来,用我现在的标准重新审视。该拆的函数拆开,该抽的抽象提出来,该补的测试补上。这个过程里没有任何 "新知识" 的摄入,但我对已有知识的理解变深了很多。
有时候,进步不是往前冲,是回头把走过的路夯实。
上个月底下了一场小雨,没打伞,沿着校园里那条种满银杏的路慢慢走。树叶还没全黄,绿色和金色交杂在一起,雨水顺着叶脉滑下来,落在地上。那一刻我忽然想起了刚学编程时候的自己。那个对着黑色终端窗口,看到自己的第一个 Hello World 被打印出来就兴奋得不行的人。那时候什么都不懂,什么都不会,但什么都想试试。
现在我懂得多了一些。能写更复杂的代码,能设计更合理的系统,能读懂那些曾经觉得天书一样的论文和源码。但有些东西好像也在变多的过程中悄悄变少了 ------ 我不太敢随便写代码了,因为我知道什么是 "好",就很难忍受自己写出 "不好" 的东西。
这大概就是成长最诡谲的一件事:你获得了判断力,同时也获得了犹豫。你的天花板被抬高了,但你脚下的地面好像也在变得更硬。
不过我最近慢慢想通了。编程能力的提升并不像爬一座山,有明确的顶峰可以到达。它更像是在一片没有边界的海上航行。你永远到不了尽头,但每一天,你的船都可以比昨天稍微快一些,你辨认风向的能力都可以比昨天稍微强一点,你在夜里凭星辰定位的直觉都可以比昨天稍微准一点。
而那些你会在某个意想不到的时刻发现:你已经走了很远了。不是因为你做了什么惊天动地的事情,只是因为你每天都在写,每天都在想,每天都没有停。
所以如果你现在觉得自己进步很慢,觉得看不到终点,觉得身边的人好像都比你强 ------没关系。
把今天的代码写完。明天再写明天的。
这条路上最不需要的就是着急。