什么才算是真正的编程能力？
在我看来，耐心不急躁、注重细节、逻辑思维缜密、有很好的大局观和整理收纳思维，我觉得这才是真正的编程能力。 我曾经的一个误区就是，以为算法和数据结构是编程的精髓。 从我自学CS转行的那一刻起，网上几乎所有内容都一直在强调算法的重要性，刷题进大厂已经成为了人人皆知的敲门砖，各类公众号培训班和什么经验宝典也是层出不穷。算法和数据结构，是编程所必要的基础和逻辑思维，但我真心觉得，它一直以来都被人们过度的神化了，有些人甚至觉得只有玩竞赛出身的人才算得上是编程大佬，对于缺乏算法数据结构知识的程序员不屑一顾。的确，算法的重要性不言而喻，但我觉得这种过度崇尚，带起的这波节奏，实在有些扭曲。后来慢慢地，我自己做的项目多了，看的代码多了，才逐渐意识到，真正的编程能力和算法思维是两回事，很多非常聪明算法能力强的人，写出来的代码完全是惨不忍睹。 真正的编程能力，我觉得是一种优秀的建筑工程思维，而这其实是比算法能力更加稀缺的。在我看来，一个真正优秀的程序员，应该具备以下特点。 首先，函数和变量等命名非常规范，且命名的词汇极其准确，一眼就可以看出意图是什么，而不是各种命名起的很烂，再加上一堆啰嗦的无用注释，让你陷入莫名其妙的混乱。一个好的项目结构，打开文件夹看到一列文件名，你就应该很快反应过来大概的层次是怎样的，打开脚本，内部的注释也应该尽量少和简洁，文件开头给整体思路做大段注释，剩下的只给比较晦涩的部分做解释。不仅如此，还包括你的代码缩进，美观，括号的位置和每行长度等等，都是值得好好写的。 其次，代码结构的整理非常干净整洁有逻辑，有很强的化繁为简的能力，能把复杂逻辑清晰的拆分成多个独立的模块，而不是一个main函数或者class里塞一堆代码。事实上，你的程序分几个脚本文件，分几个class，怎么分，互相之间如何关联，谁管理谁，是非常有讲究的，需要把不同模块的分工和联系思考的十分透彻，而不是滥用一堆抽象类和接口，不是什么都做成class。类似的，定义函数的目的也远不只是为了避免代码重复，更加是为了可拓展性和分层清晰，有些代码即便只出现一次，也应该封装成函数，但可能适合放到其他脚本里，有些哪怕出现多次，也不应该封装成函数，要封只封成inline。某些代码为什么要封装成一个函数，为什么不再细分成两个函数，为什么要设计这几个参数，为什么这个局部变量不用模板类做，为什么这两个class要分开，而不是一个class，等等，许多问题都是非常考验思维能力的。如果只关注运行结果和速度，如何分类归纳可能并没有什么区别，不需要去考虑维护性和modularity之类的因素，但如果想要提高工程思维能力，就必须同时关注细节与整体布局，比如同样是A依赖于B和C，你既可以在A中去创建B和C，也可以单独创建B和C然后以参数形式传给A，效果相同，但对于流程规划和单元测试性来说，两者是有质的区别的。 再比如，在兼顾效率的同时，代码要尽可能简洁没有冗余，而不是想到什么思路了马上写什么，一行能做好的事情分成N行跳来跳去去做。同样的一个逻辑，有的人20行就解决了，有的人怎么写都要上百行，虽然达成的结果是一样的，但从阅读的角度，100行的人思路就是一团浆糊，想到哪儿写到哪儿，从不做整理和归纳。与其说这是一种思维的差异，我觉得其实是和性格习惯有一定关系，尤其是细心程度。同样是寻找一条路线到达目的地，有的人会思考有多少种不同的路径并考虑最优，久而久之，这种意识会得到很大程度的强化，之前的思考也会不断转化成经验，成为后来的功底基础，还有的人，发现了一条路径就不会再多想了，无意识的忽略其他可能性。比如说吧，很常见的场景，函数f()在每次调用时需要不断更新一个全局变量x的值，而这个更新又依赖于x的固定初始值，很多人的第一反应，马上会再定义一个全局变量initial_x来保存x的初始值，讲道理，这样做没任何毛病，也不涉及任何思维或效率上的问题，但把initial_x定义为f()内部的static const变量显然是更合理的做法，所以说区别只是思考细心的程度和习惯。很少有教程会告诉你，良好的编程风格要把代码的scope控制的尽可能小，细心的话会自然发现这样的好处，只是很多人并没有这种意识。 还有很多，例如对于程序架构的设计和思考，往往比算法更能反应一个人的工程思维，在动手之前，想清楚哪些部分需要独立解耦，哪些部分有前后关系依赖，是很少人能够做好的。多数人并没有一个high-level的大局观逻辑思维，想不清楚怎样分离数据和逻辑本身，不知道数据该从哪儿接入，这种都是需要不断的实战经验来锻炼的，但前提是要有这个意识。对于一个稍大点的复合项目，你能想明白哪些地方用C++，哪些地方嵌入Lua，哪些辅助模块用Python做，远远比算法难的多。对于一个有扩展性的规模项目，你能知道在用一个独立的第三方库时，需要对第三方库再做一层封装，而不是直接在自己代码里调用库函数，不让外部API污染自己的主程序，都是很重要的全局思维能力。没有这种收纳整理意识，不仅后期维护扩展是十分困难的（if at all possible），别人review代码也会很痛苦，最地狱的场景可能并不是全部代码挤在一个main函数里，而是本来应该自成一体的模块，逻辑松塌地被拆分成N个函数，分散在不同文件里互相调，然后许多个这样的模块合在一起，互相include来import去，读代码还没有切换文件头疼，于是为了让程序work，就再加上各种godlike的分支判断和宏定义，一个3000行代码的项目不知不觉就上万行了。 其实一开始大家学编程，都是没有这种思维意识的，也很难有人去教你，找不到什么这方面的教程，因为这种能力稀少，很吃经验，而且不像代码规范一样有标准答案可以遵循，很多时候是完全自由的，所以老师自己写的代码，这方面往往也质量很低。空谈这种思维，我们很难给出绝对的对错，分不清黑白，但是当我们看到不同人写的同一个项目时，互相比较一下哪个搭的好，通常还是都能达成共识的。一开始没有这种思维很正常，问题是不能没有在这方面进步的意识，所以我觉得不应该只铺天盖地的一味强调算法的重要性，也应该多一些声音来让这种编程能力进入人们的视线，否则这方面的思维永远得不到进步。学习用spring框架容易，学习怎么设计和思考spring框架完全不同。学习任何一个第三方API怎么调用很容易，学习这个API是怎么思考创造的，难得多，但往往不重要，也没有必要，会用就可以了。然而容易被忽视的是，学习一个API在成熟稳健的现实大项目中是怎么用的，既重要又必要，你要思考怎么把API组织起来整合进你的项目，重新理成你的程序需要的形状，考虑在什么时间在哪儿去调用它，这和查文档学API是完全不同的。 我平时在Github上学习别人代码的时候，最不解的就是，为什么有如此多的人混用缩进，为什么那么多的代码要用8个空格甚至16个空格的缩进，难道别人用的都是4K屏幕嘛？后来我才发现，是因为很多人喜欢用不同IDE的默认配置，各种IDE的设置从来不会主动去调，传来传去到了git上面，各种tab自动转换，就变得惨不忍睹了。我认真思考了一下，一方面原因，人的本性就是懒的，另一方面原因，可能还是因为工资给的太低了吧，在996中与现实对抗，确实没有心思去注重这些细节，用996个空格缩进都不过分。然而哪怕是996，007，格式上的不规范我还是极力反对的，包括改无意义的变量名什么的也是，一看明显就是态度不认真，这种情况下，正确的做法是在代码中增加大量的逻辑分支和循环，并配合大量的嵌入式goto语句一起食用，从根本上解放思维的跳跃自由，要知道，无论编程语言未来如何发展，goto才是程序的精髓！！！（手动挂个狗头） 很多人都会更强调抽象能力，解决实际问题能力，算法和数据结构能力等等，确实那些也很重要，但我真的希望，能有更多人去多学学设计模式，看一看clean code这类的书籍，而不是把精力都花在死磕leetcode上，做的项目效果看着不错只是一方面，性能调优也只是一方面，内部代码整理的有条不紊层次清晰也是非常值得注重的。自己写代码舒服很容易，让其他阅读者或者是半年后的自己看舒服却并不简单，编程能力的好坏，我个人觉得是无法自己评价的，而是应该由他人来决定，人们一般只关心自己的程序能不能work，速度怎么样，不会去管自己写了多少anti-pattern的代码，如果缺乏这种意识，写了再多年的代码都不会有这方面思维的提高。人人都知道要做profiling保证performance，都知道robust的代码比精简更能解决问题，都知道要尽量减少操作的overhead，但这些都是局部的，如果能更多的在意一下code bloat，减少clutter，对思维能力会有很大的提升，而且这些脑内框架是可以一劳永逸在未来复用的经验思维。 有些程序员虽然并不擅长深奥的算法，更没有聪明到可以在竞赛限时的情况下刷难题的水平，然而这并不妨碍他们全局思维意识很好，样样东西都在大脑里有清晰的抽屉。一个好将军不只是一骑当千PK能力爆表，还要在身处细节之中同时靠意识思维掌握着大局，后者往往是更难锻炼的。机器学习大佬、算法工程师什么的固然厉害，不过我认为优秀项目的架构师更加是无比稀缺的神人，他们熟悉几十种编程语言各自的特性和应用场景，从服务器到后端底层，到各种外部模块库和中间件，再一直到网页前端，虽然不会特别细节，但样样都能懂到核心层面，深谙各自的分工和关联搭配设计思维，几乎没有知识盲点，这才是细思极恐。如果能让这样的人掌握了业务逻辑，项目初期就得到指点，对于项目本身和它下面的每个程序员来说，都是非常幸运的。 我认为的高手程序员是，从头开始搭建一个项目时，会先创建一堆脚本，把所有脚本的接口、依赖、类、类成员和方法声明写出来，把所有的成员类型、函数参数、signature和继承关系等全部定下来，但不去写任何的具体实现，除了main函数以外，其他所有的函数体都应该是空的，或者只包含一些注释，然后再反复review推敲几遍，确认了OK再开始写真正的实现代码，如果中途遇到问题了，再重新推敲，进行一些简单的重构和微调。当然，对于一个现实中复杂成熟的大型项目，很多模块环环相扣，每个都需要长时间的深思熟虑，也几乎没有人能够做到这个标准，因为健壮性往往是在不断迭代更新的过程中总结摸索出来的，但至少我觉得，对于每个阶段性的版本来说，是应该做到这一点的，一边标注着一些临时代码的声明，一边对下一版本的修改重构计划做一些注释，以设计计划为主，以代码实现为辅。我的目标，就是希望未来可以锻炼出这种长远性的计划思维，同时在面对每个阶段性版本的时候，可以在只有骨架的情况下把当前临时版本搭出来。 然而，我的水平还比较稚嫩，每个版本开始时，我会努力试图去先把所有的定义声明写出来，不涉及任何具体实现，可是很多时候还是思路理不清，有时候，如果不先去动手把一个class实现出来的话，就想不透它到底需要什么，也想不透其他模块应该怎么用它，更想不通多个模块一起交互应该长什么样，所以需要不断试错。同时，这也更加让我难以理清版本与版本之间的层次，做不出一个迭代计划，只能靠写大量的临时垃圾代码摸索，再反复修改。说真的，对于编程，解决每阶段现实问题的能力固然很重要，但这种有层次的长远计划性更加重要，而且特别的难，难的不是一点点，这种难和实现算法的难完全不是一回事。我认为，一个经验丰富的优秀程序员，和一个缺少经验的新手程序员，差距并不是算法实现的水平，而就是在于这种计划性思维，以及对“边做边想”的依赖程度，初入江湖时，缺乏思维条理的我们看不到工程的全貌，也只能依赖于边做边想，等到我们成为高手时，就不再那么需要边做边想，即使一行代码不敲，也应该可以在脑内想象中可视化出工程的短期概貌，实现的过程中再不断为下阶段版本做计划。 总而言之，说来说去，真正的编程能力，是要常年慢慢培养的，而且很耗耐心和头发，需要专注力，还是比较累的。这种全局思维能力的稀缺，不仅是因为难得，同样也是由于人们关注的过少，难以定性等因素。然而最重要的一点就是，编程能力是无法脱离现实背景来谈的，在内卷的漩涡中心处，面向工资编程就已经很不错了，再要求我这个那个，就是你的不对了。 补充下吧，有人提到代码规范，这个和规范还真不一样，虽然也有些重合。代码规范有许多涉及整体布局的概念，然而更多的还是强调具体的细节写法，更倾向于一种既定的规则。最主要的，我觉得是应该倡导这种思维意识，在写代码的过程中去主动摸索，也不是说一开始就做各种要求，毕竟，clean code这类思维极度依赖于经验，是要靠长年时间和思考堆积的，想要提高，首先你得开始写起来，而且要多写大量的垃圾代码，这样你才会发现什么地方可以改进，怎么做更好，到底是哪些方面思维有欠缺。而很多时候我们之所以无法提高这种水平，是因为完全忽视了这方面，写的东西全是一次性的，不管是好是烂，只要解决了眼前的问题，当时理解了，就继续下去，未来很可能永远都不会再回来看一眼，没有打磨的机会。个人觉得，随着计算机性能的不断发展提升，随着技术和轮子的不断增加、开源与多样化，几十年后的未来，对程序速度的要求可能会变得越来越次要，反而是面对汪洋大海般的优秀工具，如何培养抽象的筛选与拆分能力，如何恰到好处的整合与运用它们，会逐渐成为关键重点。