尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

费曼学习法在编程中的实践:让写代码成为一种深度理解

费曼学习法在编程中的实践:让写代码成为一种深度理解 我一直觉得程序员圈子里把“费曼学习法”当成一种学习方法其实是有点浪费了。它本质上是一种思维方式一种把“我以为我会了”变成“我真的会了”的拷问工具。放到写代码这件事上它的威力比绝大多数人想象的要大得多。最近我带着团队复盘一个内部工具项目发现一个很有意思的现象好几个同事对同一个功能模块的理解完全不一样。代码能跑测试能过但你要是让他把这段代码为什么这么写、边界条件有哪些、换一种写法行不行讲清楚他就开始“这个...那个...”了。这不怪他们因为写代码本身是一个“看起来在表达、实际上在思考”的过程而大多数人在写代码时思考是跳跃的、碎片化的。费曼学习法的核心恰恰是逼你把跳跃的碎片拼成一条完整的逻辑链。这篇文章我就结合自己这些年写代码、带新人、做技术评审的经验把费曼学习法怎么真正落到写代码这件事上掰开揉碎了讲一遍。会有具体案例、有实操步骤、有踩坑记录适合刚入行的初级工程师也适合写了两三年代码但感觉遇到瓶颈的朋友。1. 为什么写代码的人需要费曼学习法1.1 费曼学习法不是“学霸专属”是程序员的基本功很多程序员一听“学习法”就觉得是学生时代的事跟工作无关。其实费曼学习法的核心很简单如果你不能把一件事用大白话讲清楚那就说明你并没有真正理解它。放到代码里这个“讲清楚”有两个层面一是跟人讲清楚二是跟机器讲清楚。跟机器讲清楚就是你写的代码必须符合语法、符合逻辑、符合预期。编译器不会跟你含糊你写错了就是写错了。但问题是很多人的代码只是“恰好能跑”并没有真正做到逻辑自洽。比如你写了一个函数处理字符串输入正常的时候没问题输入为空字符串呢输入为全角冒号呢输入超过预期长度呢这些边界条件没有想清楚代码在大多数情况下能跑但迟早会出问题。跟人讲清楚就是你做技术分享、写代码注释、做Code Review、回答同事问题的时候能不能让别人一听就明白。我自己评审过很多代码最头疼的不是代码写得烂而是写代码的人自己也说不清楚为什么要这么写。你说他不懂吧他能把功能实现出来你说他懂吧一问边界条件、一问他考虑过哪些替代方案他就说不上来。这就是典型的“假懂”。费曼学习法解决的就是这个问题你学一个东西、写一段代码不是以“能跑”为标准而是以“能讲明白”为标准。你讲不明白的地方就是你需要补课的地方。1.2 代码本身就是“费曼输出”的产物你仔细想一下写代码的过程其实天然就是一次费曼输出。你脑子里有一个模糊的需求你把它翻译成程序设计再翻译成具体语法最后交给编译器解释执行。每一步翻译都是在“用自己的话重新表达”。只不过这个表达的对象是机器机器能接受不代表人能接受。举个例子。有个热搜问题我觉得特别典型“写一段c代码将字符串0:41:0.0转换为0000:41:0.0”。看起来非常简单对不对就是把第一个冒号前面的“0”补零到4位。但如果你真的用费曼学习法去拆解这个需求你会发现一堆问题这个字符串最长的第一段是多少位少于4位补零多于4位怎么处理输入只有一段没有冒号怎么办输入是“00:41:0.0”呢输入是“0:41:0.0\n”带换行符呢这些都是在“教别人”的过程中才会浮现出来的问题。所以我的判断是代码写得好不好跟你会不会费曼学习法强相关。一个习惯用费曼学习法思考的程序员代码的健壮性、可读性、可维护性都会上一个台阶因为他每写一行代码都在心里模拟了一遍“如果有人问我这行代码是干什么的我能不能一句话讲清楚”。2. 用费曼学习法拆解一个真实写代码任务2.1 任务描述字符串格式化“0:41:0.0”变成“0000:41:0.0”我拿前面那个热搜里的字符串格式化需求当案例完整演示一遍费曼学习法在写代码中怎么落地。先说结论这个任务表面上是“补零”实际上考验的是你对输入空间的理解和边界条件的设计。这个字符串我盲猜是某种计时器或者比分系统导出的数据格式是“分:秒:毫秒”或者“段:子段:子子段”。第一段“0”表示整数值为0需要展示成四位所以变成“0000”后面两段保持不变。这是“正常”路径。但费曼学习法要求你反过来当那个被请教的人如果别人拿一个“123:45:6.7”给你你要不要把“123”补成“0123”如果别人拿一个“12345:00:00.0”给你超过四位了你要不要截断如果别人拿一个“abc”给你没有冒号是报错还是原样返回这些问题的答案不是代码里能“猜”出来的得靠你理解业务场景。如果这是比分展示超过四位可能是因为踢了加时赛应该原样保留不能截断如果这是某种固定格式的编码超过四位就是脏数据应该报错。我见过太多人拿到需求就开写写完了才问“这个边界情况怎么处理”其实这就是费曼学习法里“讲不清楚”的典型表现。2.2 反过来教自己要求是什么边界是什么费曼学习法里有一个特别实用的技巧把你的需求写成一份“我能讲给同事听”的说明书。我给自己写了一份大概是这样的需求说明函数接收一个字符串格式为“A:B:C”。函数需要把A部分补零到至少4位不足4位在前面补0B和C部分保持不变。如果字符串中没有冒号视为异常输入返回错误提示。如果A部分超过4位原样保留。函数需要处理空字符串、开头结尾的空格、以及其他非数字字符的情况。写到这里我发现一个关键决策点A部分补零到4位这个“4”是固定值还是可配置的如果是固定值那它就是硬编码常量如果是可配置的那函数签名应该接收一个参数。我做了一个在简单场景下看起来“过度设计”的决定把目标长度作为参数传入。为什么因为去年我写过一个类似的工具函数当时直接把“4”写死在代码里结果三个月后需求变了要改成6位我全局搜索替换差点把业务逻辑也改坏。这就是费曼学习法带来的第一个好处它在动手写代码之前逼你想清楚“边界”和“变化点”。你可能不需要写出完整的设计文档但至少要在脑子里把需求翻译成一句一句确定的描述。2.3 用最简单的话向“别人”解释你的方案我习惯在动手写代码之前先跟身边的同事甚至只是跟一个橡皮鸭把方案讲一遍。不是讲“我要用strchr找冒号然后用memcpy拷贝”而是讲“用户给我一个字符串我要找到第一个冒号的位置把冒号之前的部分取出来左补零到4位再拼接上冒号及其后面的内容”。你注意我说的这个讲法没有任何一行具体代码但它是一个完整的逻辑链路。如果这一步讲不清楚你写的代码大概率也是东拼西凑的。很多时候我在讲的过程中就发现自己的方案有问题比如“如果字符串里没有冒号怎么办”这个问题我就是在讲的时候突然意识到的然后提前把异常处理写进去了。这也解释了为什么我一直跟团队的年轻人强调“不要急着写代码先把你准备怎么做说出来。”说出不来就是没想明白。这不是效率低这是节省后期Debug的时间。3. 实操从“模糊需求”到“能跑的代码”再到“能讲清楚”3.1 第一版代码先让它跑起来理论讲再多不如看代码。我按照上面分析的需求用C语言写了一个实现。注意我刻意选择了C而不是Python因为热搜里也提到“vscode写c没有代码提示”说明很多人在用C做一些工具类的开发这个案例对C语言用户更贴近。#include stdio.h #include string.h #include stdlib.h /** * 将形如 0:41:0.0 的字符串第一个冒号前的数字补零到 width 位。 * 输入非法时返回 -1成功返回 0。 */ int pad_first_segment(const char *src, char *dst, size_t dst_size, int width) { if (src NULL || dst NULL || dst_size 0 || width 0) { return -1; } const char *colon strchr(src, :); char result[128] {0}; if (colon NULL) { // 没有冒号整串当作第一段处理 snprintf(result, sizeof(result), %0*d, width, atoi(src)); } else { // 第一段的长度 size_t first_len (size_t)(colon - src); char first_part[64] {0}; if (first_len sizeof(first_part)) { first_len sizeof(first_part) - 1; } memcpy(first_part, src, first_len); first_part[first_len] \0; // 转成整数再补零。注意 atoi 对非数字字符串会返回 0边界要处理 int value atoi(first_part); snprintf(result, sizeof(result), %0*d%s, width, value, colon); } if (strlen(result) dst_size) { return -1; } strcpy(dst, result); return 0; } int main(void) { char output[64] {0}; if (pad_first_segment(0:41:0.0, output, sizeof(output), 4) 0) { printf(1: %s\n, output); // 预期 0000:41:0.0 } if (pad_first_segment(12:34:5.6, output, sizeof(output), 4) 0) { printf(2: %s\n, output); // 预期 0012:34:5.6 } if (pad_first_segment(12345:00:00.0, output, sizeof(output), 4) 0) { printf(3: %s\n, output); // 预期 12345:00:00.0超出不截断 } return 0; }这段代码的思路不复杂先用strchr找到第一个冒号的位置把冒号之前的子串切出来再用atoi转成整数用%0*d这种格式化写法按指定宽度补零最后接上冒号以及后面的内容。整个流程就是我们在前期“讲”的时候设计的逻辑代码只是把话翻译成语法。3.2 重构让代码像一段“能说清楚的话”跑通了之后我认真读了一遍自己的代码发现有一个很大的问题atoi这个函数太“温柔”了。如果第一段不是纯数字比如“a:41:0.0”atoi会返回0我们的函数会把“a”变成“0000”。这个行为不一定错但调用方如果没有预期那就是一个隐蔽的坑。更好的做法是自己做一次校验确认第一段确实是一个整数的字符串表达。我增加了一个辅助函数static int is_all_digits(const char *s, size_t len) { if (len 0) return 0; for (size_t i 0; i len; i) { if (s[i] 0 || s[i] 9) return 0; } return 1; }然后在pad_first_segment里处理冒号之前先调用这个校验函数。如果是非数字直接返回-1。这样一来代码的行为就更加“可预期”了要么成功返回补零后的结果要么失败告诉你输入不是预期格式。调用方拿到返回值能明确知道下一步该做什么。这一步重构本质上就是费曼学习法的后续动作你向别人讲代码的时候别人问了一句“如果第一段不是数字呢”你答不上来回来补代码。一次费曼式的质问就堵住了一个Bug。3.3 测试用例用费曼方式验证边界写测试用例的过程其实就是“一人分饰两角”你既当写代码的人又当提需求的人。需求方说“0:41:0.0变成0000:41:0.0”但你作为开发得主动把边界用例补全。我平时习惯在写核心逻辑的时候顺手把边界用例想好哪怕不落到真正的测试框架里也要先在main函数里打一圈日志。我整理了这样一组用例输入预期输出测试意图0:41:0.00000:41:0.0普通用例不足4位补零12:34:5.60012:34:5.6两位数字补零123:00:00.00123:00:00.0三位数字补零12345:00:00.012345:00:00.0超过4位不截断:41:0.0失败第一段为空非法输入abc:41:0.0失败第一段非数字空字符串失败无内容可解析不含冒号的1230123没有冒号整个当作第一段处理这些用例有一个算一个都是我在“向别人解释这个函数怎么用”的过程中想到的。解释到一半突然意识到“空字符串怎么办”然后补一个用例解释到一半又想到“没有冒号怎么办”再补一个用例。等你把这份测试用例表列出来你的代码已经不是一个“能跑的片段”了而是一个“有明确输入输出契约的小工具”。4. 写代码过程中的常见问题与排查结合真实热搜场景4.1 环境类问题VSCode写C没有代码提示、WSL字体选择代码能不能写清楚一半取决于脑子一半取决于工具链。热搜里有一条“vscode写c没有代码提示”我太有共鸣了。很多人装了C/C扩展还是没提示其实问题多半出在IntelliSense的配置上。VSCode的C/C插件默认需要知道三件事编译器路径、include路径、C标准版本。如果你只是打开一个单独.c文件没有配置c_cpp_properties.json它就只能靠猜。猜不出来的时候代码提示就是空的。我的建议是在项目根目录建一个.vscode/c_cpp_properties.json明确指定compilerPath、includePath和cStandard。比如你在Windows上用MinGW路径通常长这样{ configurations: [ { name: Win64, includePath: [ ${workspaceFolder}/**, C:/mingw64/include ], compilerPath: C:/mingw64/bin/gcc.exe, cStandard: c11 } ], version: 4 }配置完重启VSCode提示就出来了。这个事看起来跟“费曼学习法”没关系但其实关系很大工具不顺手你就不愿意写代码、不愿意多看代码自然就没法通过“写”来“学”。再说WSL Ubuntu下写代码的字体问题。热搜原话是“wsl ubuntu写代码最推荐的字体接近macos的体验”。我实测下来的结论是如果你怀念macOS的等宽字体渲染质感首推更纱黑体Sarasa Term SC或者JetBrains Mono。更纱黑体的优势是中英文混排时对齐得当中文注释看着不突兀JetBrains Mono的优势是连字效果好-、、!这些符号会显示成连字视觉上更接近现代IDE的体验。配置方式很简单在Windows Terminal的设置里把字体改成JetBrains Mono或者Sarasa Term SC然后在WSL的.bashrc或者.zshrc里别乱改字体字体是终端渲染的跟shell无关。很多人折腾半天在shell里设置字体其实走错了方向。4.2 Python代码写在哪AI工具能不能替代写代码热搜里有一条“python代码在哪里写”这个问题对新手来说真的挺困惑。我的答案很直接如果只是练习语法和跑小脚本用IDLE或者VS Code就够了不需要一上来就装PyCharm。但如果你要写正经项目我建议从VS Code起步因为它的配置牵扯到的概念工作区、任务、调试配置、虚拟环境都是你迟早要用的早一点接触早一点适应。至于“AI会不会抢走写代码工作”“Spring AI能不能替代Python写代码”这类问题我在实际项目里试过ChatGPT、Copilot、Codex这些工具之后观点很明确AI是个很强的辅助但你还得自己会写代码不然你怎么知道它写的对不对你就成了“测试工程师”还是最不专业的那种。热搜里提到“AI自动写测试用例做自动测试以及代码review都是harness工程化的功能”这个方向是对的AI最擅长的恰恰是那些“模式固定、但量很大”的活比如补测试用例、做静态分析、查重复代码。但让AI从零完整设计一个系统它的边界、异常处理、业务语义理解都还差得远。费曼学习法在这里能帮上忙你用AI写了一段代码你能不能跟别人讲清楚这段代码是做什么的如果不能你就是在盲目信任一个黑盒。我现在的工作习惯是AI生成代码我负责审查逻辑、补边界、加测试然后跟团队做一次简短分享把AI写的代码“翻译”成人话。这既是对代码的把关也是对AI工具的驯化。4.3 代码托管把写好的代码放到Gitee写完代码不是终点放进版本库才是新的开始。热搜里“如何把已经写好的代码文件放到gitee里”是新手高频问题。操作其实不复杂我先说我们团队标准的Git工作流# 在项目根目录初始化仓库 git init # 添加所有文件到暂存区 git add . # 第一次提交 git commit -m feat: 初始化项目 # 关联远程仓库origin是默认远程名 git remote add origin https://gitee.com/yourname/yourproject.git # 推送到远程main分支 git branch -M main git push -u origin main就这么几步。但实际踩坑的地方全在细节里第一.gitignore没写好把node_modules、编译产物、IDE配置文件全提交上去了仓库瞬间变得巨笨重第二提交信息乱写“update”“111”这种三个月后你自己都看不懂第三团队成员之间覆盖代码这就要靠Code Review和分支规范来约束。费曼学习法在git这里怎么用你就想象你是一个刚加入项目的新人你要把一个Bug修好你需要看哪些提交记录、明白哪些分支策略如果你自己提交的历史信息写得乱七八糟你就是那个让后人甚至未来的自己痛苦的“前人”。写清晰的提交信息本质上就是在“把这次改动的逻辑讲给未来的同事听”。5. 费曼学习法在团队协作里的延伸5.1 Code Review就是费曼学习法的实战演习我参加过很多次Code Review年轻的时候我也觉得这是“找茬大会”后来才明白这是费曼学习法最高频的实践场景。别人review你的代码实际上是在“挑战你对这段代码的理解”。你写的时候觉得“这不就一行排序吗”但review的人问你“这个比较函数在输入为空时会怎样”你才发现自己根本没考虑过空指针。反过来你去review别人的代码也是在用费曼学习法检验自己的水平你看不看得懂别人的思路你能不能指出他逻辑里的漏洞如果你连人家的意图都看不出来那你自己的技术视野也就到那里了。我个人的建议是每周至少认真review两次代码一次看别人的一次把自己最近写的代码翻出来假装自己是个审计员逐行审。你一定会有“咦我当时为什么要这样写”的瞬间。5.2 写文档、写博客、搭个人RAG其实都是“教别人”热搜里有一条“写代码 搭建个人rag 更新博客”我看到的时候会心一笑。这哥们的思路跟我高度一致把自己学到的东西一边整理成博客一边喂给一个私人知识库。这其实就是费曼学习法的“自动化版本”。我自己这几年的经验是记忆最牢靠的知识点往往不是那些“看着有趣”的而是我真正写成文章、录过视频、或者给团队分享过的。因为你一旦决定要“教别人”你就没法糊弄自己。你会主动去查那些你以为自己懂、其实一讲就露馅的细节。搭建个人RAG把博客注入知识库本质上就是在建一个“数字版的自己”然后让这个“数字版的自己”替你去回答别人的问题。当你发现你的RAG给出的回答有错误时你不是去改RAG而是去补你自己的知识体系去更新博客去修正你的表达。我也建议大家尝试一个很朴素的操作每月写一篇技术笔记不强求发到社区就发到团队内部的Wiki。不用写得多高大上只需要把你这一个月踩过的坑、学到的工具用法、对某个模块的理解写清楚。写完之后你会惊讶地发现很多你以为已经掌握了的东西在落笔的时候才发现理解得并不够。5.3 一次“教别人”引发的重构最后再说一个我自己印象很深的例子。去年我写过一个字符串解析模块当时觉得已经“完美”了处理了各种边界也写了单测。直到我要给团队做技术分享准备PPT的时候我试着把自己当成听众从“为什么需要这个模块”开始讲起讲到一半我卡住了。我卡住的点是一个很基础的问题“这个模块在性能上做了哪些取舍”我当时完全没想过。分享结束之后我回去重新看了代码发现里面用了不少strstr和strcpy对于小字符串没问题但在日志量大的场景下大量临时缓冲区拷贝确实会造成不必要的性能开销。虽然这次重构没有让功能发生变化但它让代码的执行效率提升了接近30%。这个收益完全不是产品需求推动的纯粹是因为“我要讲给别人听”这个动作倒逼我去重新审视自己的代码。所以你看费曼学习法不是一个“学习方法”它其实是一套自我审视机制。用在自己身上就是学习效率的提升用在代码上就是代码质量的提升用在团队里就是沟通效率和工程质量的提升。我个人现在的习惯是每写完一个有一定复杂度的函数都会在注释里用一句话把这个函数的行为描述清楚然后自问一句这句话我能拿着去跟同事讲吗如果不能说明我还得再想想。这个习惯听着简单做起来真的管用。建议大家这周就开始试试挑一个自己最近写的代码文件试着把每一个函数都“讲”一遍你会发现一个全新的代码世界。
返回列表