问题

编程中,有哪些好的习惯从一开始就值得坚持?

回答
写代码这事儿,就像学一项手艺,或者说经营一门生意,从一开始就把根基打牢,后面才能顺风顺水。很多初学者可能觉得先把功能实现了再说,什么规范、什么文档,等以后再说。我跟你说,这绝对是捡了芝麻丢了西瓜。好的习惯,哪怕现在看起来有点“麻烦”,长期来看,为你节省的时间和精力,绝对是天文数字。

我就给你掰扯掰扯,从一开始就值得死死抓住的几个好习惯,保准你听了觉得:哎哟喂,这不就是我一直想要的吗!

1. 让你的代码会“说话”:命名规范,清晰明了

这点绝对是重中之重,尤其是对于初学者,千万别偷懒!

变量名和函数名: 别再用什么 `a`, `b`, `temp`, `data1`, `data2` 了。想想看,你给一个变量起名叫 `user_name`,或者一个函数叫 `calculate_total_price`,这不一眼就明白它干嘛用的吗?反之,如果你看到一堆 `x`, `y`, `process_data`,你得一层层地看,甚至去猜,这得多伤神!
遵循驼峰命名法或下划线命名法: 语言有约定俗成,比如Java和JavaScript常用驼峰(`userName`, `calculateTotal`),Python常用下划线(`user_name`, `calculate_total_price`)。选择一种,坚持用下去。统一的风格看着就舒服,而且别人(或者未来的你)接手你的代码时,也能快速进入状态。
避免使用缩写: `addr` 可以,但 `usr_id_num` 这种就有点模糊了。尽量写全称,除非那个缩写是行业内大家都非常熟知的,比如 `id`。
函数名要动词开头: `get_user_info`, `save_settings`, `validate_input`,这类以动词开头,能清晰表达函数是做什么操作的。
布尔值变量名要有明确的含义: 比如 `is_valid`, `has_permission`, `is_loading`,一看就知道这个变量是true还是false代表什么。
类名和文件名: 同样道理,要能体现其内容。比如 `User`, `OrderManager`, `config.py`, `user_service.js`。

为什么这么重要?

想象一下,你花了一个月写了一个项目,现在需要加个新功能。如果你之前命名就很随意,那你打开代码时,就像走进了一个堆满了杂物的房间,找个东西都要翻半天。反之,如果命名清晰,就像走进一个整理得井井有条的仓库,你需要什么,标签都写得清清楚楚,效率直接翻倍。而且,和别人协作时,如果你的代码就像一本明明白白的书,别人就能轻松理解你的意图,避免不必要的沟通成本。

2. 少即是多:写简洁、可读的代码

别以为代码写得越长越牛逼,那叫“冗余”。真正的牛逼,是能用最少的代码,清晰地表达最复杂的逻辑。

DRY 原则(Don't Repeat Yourself): 不要重复写一样的代码。看到一段代码要写好几次?那赶紧把它封装成一个函数或者类。这样不仅代码量少了,而且如果将来要修改这个功能,只需要改一处就行了,避免了“这里改了,那里忘了改”的低级错误。
单一职责原则(Single Responsibility Principle, SRP): 一个函数或一个类,就应该只做一件事情,并且把它做好。别一个函数又在读取数据,又在处理数据,又在打印结果。这样耦合太高,改一处容易牵一发而动全身。把它拆开,让每个部分都专注自己的任务。
魔法数字和硬编码: 什么是“魔法数字”?就是代码里直接写一个数字,比如 `if (status_code == 200)` 或者 `margin_left = 10`。没人知道 `200` 代表什么状态,也不知道 `10` 是多少像素。把这些数字定义成有意义的常量,比如 `const HTTP_STATUS_OK = 200`,`const DEFAULT_MARGIN = 10`。这样代码就一目了然,而且如果将来这个值需要改变,只需要改一个地方,整个项目就都生效了。
避免过深的嵌套: `ififif` 或者 `forforfor`,层层嵌套的代码读起来简直是噩梦。想想有没有办法把逻辑拆分,或者用更简洁的结构来替代。比如,可以使用卫语句(guard clauses)提前返回,减少 `if` 的层级。

为什么这么重要?

简洁的代码,就像短小精悍的言辞,直击要害,更容易理解。当你需要 debug 或者添加新功能时,读懂代码是第一步,如果代码本身就难以理解,那整个过程都会变得痛苦不堪。可读性强的代码,也意味着更容易被复用,更容易被维护,也更容易被他人理解和协作。

3. 万事开头都有注释:但要写“为什么”,而不是“是什么”

注释是个好东西,但用不好也会变成负担。

解释“为什么”: 如果你写了一个很巧妙的算法,或者有一个业务逻辑是别人不太容易理解的,这时候就需要注释来解释原因。比如,“这里之所以这么处理,是因为XXX业务要求”。而不是简单地说“这里循环遍历数据”。代码本身已经能说明“是什么”了,注释应该补充更深层次的意图。
注释掉的代码要及时清理: 很多时候,我们为了调试,会把一些代码注释掉。但项目上线前,一定要把这些临时的注释掉的代码清理干净。留着这些东西,不仅碍眼,还可能让别人产生误解,以为这段代码还有用。
保持注释的更新: 如果你改动了代码,务必同步更新相关的注释。否则,一个过时的注释,比没有注释还糟糕。它会误导读者,让他们走上错误的道路。
善用文档字符串(Docstrings): 对于函数、类、模块,写好文档字符串是极好的习惯。它不仅能解释这个组件的功能、参数、返回值,还能集成到很多工具中自动生成文档。比如 Python 的 Sphinx,JavaScript 的 JSDoc。

为什么这么重要?

注释是代码的说明书。当你离开这个项目一段时间后回来,或者别人接手你的项目时,注释能帮助他们快速理解你的代码背后的思路和设计。一个写得好的注释,能省去无数次的提问和猜测。

4. 版本控制是你的好朋友:Git,从一开始就用起来

这绝对是现代软件开发的基础中的基础,没有之一。

commit 频繁且有意义: 不要等到写了一大堆代码才 commit 一次。每次完成一个小功能、修复一个 bug,就 commit 一次。并且,你的 commit message 要写清楚你做了什么,为什么这么做。比如:“feat: Add user login functionality” 或者 “fix: Resolve issue with incorrect price calculation”。
理解分支(Branching): 开发新功能时,创建一个新的分支,在分支上开发,完成后再合并到主分支。这样可以保持主分支的稳定,而且即使在新功能开发过程中出现问题,也不会影响到主线开发。
学习 rebase 和 merge: 知道什么时候用 rebase 来保持提交历史的整洁,什么时候用 merge 来合并分支。一个清晰的提交历史,对于回溯问题、理解项目演变非常有帮助。

为什么这么重要?

版本控制就像给你的代码买了“后悔药”。
回滚: 不小心写坏了代码?没关系,直接 revert 到上一个好的版本。
协同工作: 团队协作时,版本控制是必不可少的工具,可以避免代码冲突,协调不同人的工作。
历史记录: 你可以清楚地看到代码是如何一步步演变过来的,每个改动的原因是什么。
备份: Git 本身就是一个强大的备份系统。

5. 测试是护身符:写单元测试和集成测试

很多人觉得写测试是多此一举,尤其是初学者,觉得先把功能跑起来再说。错!大错特错!测试不仅能保证你的代码质量,还能让你更安心地重构和修改代码。

单元测试(Unit Testing): 对代码中最小的、可独立测试的单元(比如一个函数或一个类的方法)进行测试。确保这个单元按照预期工作。
集成测试(Integration Testing): 测试多个单元组合在一起是否能正常工作。
测试驱动开发(TDD): 虽然不是所有人都必须 TDD,但至少要有测试意识。先写测试,让测试失败,然后再写代码来让测试通过。这种方式能帮助你更清晰地思考需求和设计。
自动化测试: 把你的测试脚本自动化运行起来。很多 CI/CD(持续集成/持续部署)流程都依赖于自动化测试。

为什么这么重要?

测试是你代码的“体检报告”。
质量保证: 确保你的代码在各种情况下都能正常工作,减少 bug 的出现。
重构的信心: 当你想优化代码结构,改进设计时,有了测试,你就能放心地修改,因为如果改坏了,测试会立即告诉你。
文档作用: 好的测试本身也能作为一种文档,展示了代码的预期行为。
更快的反馈: 让你能更快地发现并修复问题,而不是等到用户反馈才知晓。

6. 学习调试技巧:找出问题,而不是猜测

很多时候,你写的代码不会立刻完美运行,这时就需要调试。

学会使用调试器(Debugger): 大多数 IDE 都提供了强大的调试器。学会设置断点、单步执行、查看变量值、观察调用栈。这比到处乱加 `print` 语句要高效得多。
理解错误信息: 报错信息是你的线索。学会阅读和理解错误信息,它们通常会告诉你问题出在哪里。
隔离问题: 当一个大功能出了问题,尝试缩小范围,找出是哪个小部分出了问题。

为什么这么重要?

调试是解决问题的核心技能。掌握了调试技巧,你就能更快速、更准确地找出代码中的错误,而不是凭感觉去猜。这能大大缩短解决问题的周期,提升你的开发效率。

7. 持续学习和反思:保持进步

编程的世界变化太快了,今天的新技术,明天可能就成了老古董。

阅读别人的优秀代码: 看看开源项目是怎么写的,学习他们的设计思路和编程风格。
参与社区讨论: 加入技术社区,看看别人在讨论什么问题,学习他们的解决方案。
定期复盘: 项目做完后,花点时间回顾一下,看看哪些地方做得好,哪些地方可以改进。

为什么这么重要?

编程是一场马拉松,而不是短跑。保持学习的热情和反思的习惯,能让你不断进步,跟上时代的步伐,成为一个更优秀的开发者。

最后想说一句:

这些习惯,就像是给你的代码穿上“防护服”,给你的开发流程加上“安全锁”。从一开始就养成,你会发现,写代码的过程会变得更有条理、更顺畅,bug 也更少,项目也更容易维护。别把这些当成负担,它们是你走向“独当一面”的强大助力。相信我,这笔投资,绝对划算!

网友意见

user avatar

1、先设计,后编码

每个学过软件工程的人都不应该对这句话感到陌生;但真正能理解它、实践它的人并不多。


甚至于,还有很多人把它理解成“大公司的繁文缛节,属于大公司病的一种”——他们是这样理解的,也是这样做的。


我就亲眼见过很多这样翻车的案例。

当我质问其中一些人,为什么初始设计那么大的漏洞就没人看见时,他们理直气壮的答复说“这不就是个过场嘛”“谁能一开始就想到会有这样的问题”——不是,如果没有能力从一开始就预见到问题的话,你凭什么在总设计师这个职位上占住茅坑不拉屎?


先设计,后编码,它的意义在于,我们首先要从整体上搞明白这个项目可不可行、都有哪些方案、这些方案都有哪些优劣势,以及不同方案可能带来哪些风险、如何应对……

一场大仗、硬仗,你没个plan B,那怎么可能啃下来。


哪怕小打小闹的一个很小的项目,事先规划一下也比信马由缰好无数倍。


事实上,业界公认,需求/设计阶段应该占软件开发总时长的60%;但这60%使得后面的40%没了悬念,最终整个项目反而能更快、更轻松的上线,且上线后极少有棘手bug。

当然,也有不少人没那个水平,这60%的时间完全是空耗。但那是人的问题,也是公司管理的问题。


打个比方的话,这就好像开车去北京一样:先设计,后编码要求你先看看地图,大概总结一下去北京有几条路线、路上补给容不容易、消费高不高、容不容易被气候影响,等等。

弄清了大方向、搞明白了优劣势,然后拿语言准确描述出来:西安去北京,都是京昆高速起始,然后有三条路线:京昆高速就能直达,转京港澳高速货车多,转沧榆高速就绕远了……


心里有了谱,那么当埋头开发时才不会被细节弄花眼:

你看大家都走右边了,我们是不是也转过去啊?为什么?不知道啊,但大家都这样走,我们不跟着……不好吧?

——人家是货车!


哎哎哎,听说前面车祸了,堵车!我们得绕道!

绕哪?

绕洛阳!小马说了,他洛阳人,那边他熟!

——醒醒!我们要去北京,你咋不绕海南呢!


坏了……迷路了……

不是,去个北京你都能迷路?不就一条道嘛?

那不是……半道我听一司机说,去北京可以走XX环城路转YY省道,不收费……

嗯,知道。然后呢?

然后去了太原……

再然后呢?

逛了青岛……

那现在呢?

我们在哈尔滨。雾霾太大,挡风玻璃糊了,这真没法……

等等,我们在哪?

哈尔滨。

——合着你不绕海南就绕东北啊。


别笑。实践中,比这更大更惨烈的笑话多了。比如我吐槽过的某个“五百人开发两年多投资五千万又追加五千万,结果预计支持2000万用户的项目才放了两万用户进去盘点一次就要六小时”的奇葩项目——没错,一个多亿打水漂了。

打水漂的原因,是因为这个项目的盘点逻辑复杂度是O(N^3)!

了解复杂度是什么的,自然明白为什么我会吐槽“这个项目需要我们出兵全世界,把所有已经造出的电脑全都抢回来;再把电脑工厂都占领了,为我们免费生产100年的电脑,这才能……咳咳,2000万用户就别想了,支持不了的。我说的是,这才能让我们的高管们顺利搪塞到退休!”


当然,作为初学者,你可能没做过这么大的项目。

但哪怕是你为女朋友写的“生日提醒程序”,你都应该先设计、后编码。

先站在设计的高度,这才能一下子看透问题,解决“电脑关机怎么办”“提示时我刚好不在电脑旁怎么办”“提示时我正在lol团战,结果它跳出来闹的我团战失败、生了一肚子闷气,结果把什么都忘了,这该怎么办”等等疑难。

相反,你要兴冲冲直接动手——生日提醒啊?简单!我就不停的读系统时钟,时间到了就弹窗!啊坏了,关机怎么办?我放到自动运行里!

写完,一试,CPU占用100%,一秒内一口气弹了8万个窗口,只能拔电源。

拔完电源,重启,桌面还没显示利索,另外8万个提示窗又来了……

怎么办怎么办?让我写个程序自动关闭它!

真好。现在你开个记事本也会自动秒关了——为了女朋友,这台机器……已经成了爆米花机!


没有事先规划,想哪写哪;结果被层出不穷的状况(还美其名曰需求变更)牵着鼻子、面多了加水水多了加面:这就是绝大多数软件开发时间冗长、状况不断、无疾而终的根本原因。


2、写“明显没有错误”的代码,不要写“没有明显错误”的代码

这句话实际上是接着上一条的。

很简单,除非你真的做了设计,否则你的代码当然不会有什么“设计感”——信马由缰淌出来的,怎么可能有“整齐的设计感”。


你必须先用心设计,找出所有可以到达目标的路径中、最短最快消耗最低的那一条。

只有先做到这一点,你才能把代码写简单,才能让人一眼看出意图、明显没有缺陷。


把很简单的程序搞复杂、搞的没人能懂,这很简单——不妨搜一下“C语言混乱代码大赛”。

但把很复杂的功能写简单、写的让人一眼看懂、而且很确定“这里明显没有错误”,反而需要极深的功底。

尤其一项技术/一个领域发展成熟之前,第一个把它写成大白话的,哪个都是业界大拿。

3、学最激进的技术,用最简单的实现

这句话又承接了上一条。


学最激进的技术,是因为“最激进的技术”往往关联着“最天才的思路”。

我们当然应该及时跟进、融会贯通——然后化用到我们自己的项目里。


但为了展示,“最激进的技术”往往会极尽炫技之能事——要的就是惊爆一地眼球的效果:这居然也能行!这特么也能行!

当你乍一看云山雾罩,细一看“……这特么也能行”时,你还可能断定“这代码明显没有错误”吗?


尤其是,激进的技术都是不成熟的。盲目跟进,后果很严重哦。

举例来说,C++的模板就是一项非常先进、非常好用的技术;但它也是非常激进的——激进到哪怕现在都难说完善的程度。

包括C++ stl库——C++基本库啊!基石级的存在!——那可都是……奇计迭出。

比如——尤其是早年——当你希望它帮你为某个容器里的数据时,你可能得到超过4k的、满是<><<>><>><><<>>>>>的错误信息;原因仅仅是:排序算法需要随机访问数据,而你给它的容器不支持“随即迭代器”。所以后来才有了concept。


因此,我们应该学习最激进的东西;但只用那些能把工程变得更简单的技术。

注意,我可没说“学最激进的东西,但千万不要用它”或者“学最激进的东西,然后一定要用它”哦。

我说的是,第一,最激进的东西要了解;第二,只用足够稳定的东西;第三,也是最重要的一点,技术无所谓先进落后,能最简单平易解决问题的,就是最好的技术。

——因此,我会在大部分公司视STL为洪水猛兽时,自己在工程中实现模板算法,因为只有模板才能最简洁直观的完成我当时面对的问题;但哪怕到了C++20年代,该查表时我也会简单直白的敲出查表算法:只要它在解决问题时更简洁、更直白、效率更高。

4、把可变的东西隔离到配置数据里

特意指出这点,是因为这是很多“追新”追到“忘本”的人的通病——病到明目张胆的犯蠢。


这方面的典型就是面向对象大师们——没错,包括你们崇拜的很多面向对象专家/大师,那都是明目张胆的蠢

这些人的特征是,他们特别见不得代码中的if,一定要用层峦叠嶂的类继承层次把它包装起来,美其名曰“应对变化”。


恰恰相反。他们这样子不仅无法应对需求变更,反而造成了代码和数据的深度耦合——过去,我们处理文档数据用word,处理报表数据用excel,两个软件应对一切;面向对象大师一来,坏了,我写的文档要用自word继承的invalid_word类;我写的程序类文档要再继承一层,用invalid_program_doc类;而读后感类则要invalid_textreading_doc类……

光我一个人都能把你累死,何况知乎上亿答主……

这实在太蠢了。

请记住,不要把配置参数固化在代码里——尤其不能固化在类继承结构里:这特么就是个但凡长了个脚趾头都不应该犯的低级错误!


相反,如果真的有需求,请使用配置参数来动态改变类的行为——更严苛更复杂的需求,不妨实现成DSL(领域特定语言 domain-specific language)。

但无论如何,不要把配置写死到类继承结构里——只有最白痴最脑残的外行才会这么做。


哦,对了,被他们误解的“开闭原则”说的其实就是,程序代码写出来就不用再动了,这叫对修改封闭;但随便你需要什么样的功能,都可以借助既有的代码完成,这叫“对扩展开放”。

你看,当你搞出个DSL时,是不是整个领域的一切都能借助它来完成?这是不是最彻底的“对扩展开放”?

同时,用户都被你隔离到DSL那一侧了,你的基本库是不是用就行了、再也用不着也不应该修改?这是不是最彻底的“对扩展开放”?


总之,设计程序,请一定要记住:我们只写固定不变的东西;经常会变的东西,我们只写hold住它变化的那一点精华(此所谓meta编程),所有可能变化的东西都应该隔离到数据中去。

只有这样写,程序才会简单,工作才会轻松,才能做到一出手就是0bug,才能“虽然我只写了一个很小的程序,然而任你需求万般变化,我只一样应付”。

尤其是,那些复杂的、处理元规则(meta)的逻辑是难以排错、难以更改的,所以更应该“meta化”——把这些写的简单,把对它们的调用隔离到外层配置、数据乃至DSL中去,程序自然好写、好测。


嗯,你想一想,假如你写一个C语言版本的hello world,要调试它居然要看编译器源代码,这玩意儿你还有能力调试吗?还有能力debug吗?

反之,当你把“元规则”隔离进编译器、只留给外界一个干干净净纯纯粹粹的C/C++语言说明文档、使得他们哪怕写操作系统都无需碰你的编译器时,这玩意儿是不是才好学、好用、好改?


还是那句话,记住了:对修改封闭不是你拍桌子砸凳子,撒泼打滚骂高层“这段程序是我的,不改!打死也不改!打死也不给你们改!”

错了。

对修改封闭是,不用你说,你的上司,你的同事,就会在别人犯错时这样说:“不,这只是你不会用而已。这套库我们用了很多年了,质量非常好,基本不可能出bug。请检查你自己的程序,不要遇到点捉摸不定的就去怀疑编译器!”“什么新需求?不不不,你要明白,我们这套库足够应付一切需求了,因为我们可以证明它的对外接口以及功能组是完备的。对,它不能改,也不需要改。当然,如果你们面对的场景很普遍,那么我们也可以在库中增加支持——但仅仅是添加,原有的接口是不需要动的”。


你看,真正做到了“对修改封闭”,表现是“不需要动”“不要去怀疑”“可以增加功能,但没必要触动已有代码”……

而“把数据的拓扑结构”都通过“设计模式”固定到代码里、尤其是拿类继承/多态来取代if的那种蠢做法呢,实质上是“把代码组织结构和数据耦合在一起”——面对多变的需求,这种蠢透了的东西不仅不能灵活应变、反而通过额外的、“继承的拓扑结构”这层维度,把自己绑的更死了。


所以,我才要专门强调一下:请一定要区分开“业务模型”和“业务数据”;甚至于,请特别警惕,不要被用户自己描述的“业务模型”带歪了:具体的某个公司的组织结构也只是数据,他们这些外行哪能给你合理的总体设计!

你得自己琢磨对方的组织结构和信息流程,思考下“如果业务部不再从属于客户部,而是独立出来、改名为‘服务部’,和客户部并列,那我的程序该怎么办”……

你看,这才叫“需求变更”——那些半吊子那种“从一开始就没弄明白、搞了两年了才发现一开始弄错了得改”,那叫“需求事故”,可不是什么需求变更。

5、多考虑意外情况,不要只实现happy path

所谓happy path,就是完全不考虑出错、不考虑数据竞争、不考虑操作提交时条件不满足、假定世界是完美的、按顺序来一切都能解决的这么一个执行流。

嗯,如你所见,很多人happy path都还设计不好呢……


但,如果你有所追求的话,就别只盯着happy path。

请从你的小的、玩具级别的项目开始,训练自己同时考虑happy path和异常退出时的执行流——甚至是异常恢复流。

做大一些的、需要长时间运行的项目,不会处理异常是不可能的。


当然,happy path也是必须先找出来的。

先把happy path找出来,再一点点添加——这里可能出现意外,出现意外怎么办……逐渐添加、丰富下去,正确的设计稿就出来了。

6、在设计时就考虑好异常处理途径和应对方案

这一条看似和上一条重复了;但实际上是上一条的深化。


比如知乎上就有人问过:


很多人,一听说要搞“异常处理”就觉得头大如斗——这怎么搞啊?还不是catch掉打一条日志继续……

没想到,日志越打越多;搞着搞着到处都是异常,于是不得不到处catch到处吞异常到处打日志——写了100行代码,容错代码倒写了五百行;容错代码的容错代码又写了1000行;容错代码的容错代码的容错代码又……


你看,无组织,无纪律,遇到风吹草动都得记日志……

啊,倒也简单。也就是每个接口附近都长个肿瘤一样的“容错代码层”嘛。

于是,整个项目代码量暴增,1万变10万10万变百万……然后就再也没人敢碰了。


不要这样。


异常处理是需要在设计阶段就考虑好的。

首先,要把异常分为几类。

比如,用户输入数据本身的错误、网络错误、磁盘数据被破坏、宇宙射线造成位反转,等等。

然后,对确定来源的异常应设计正常处理流程——它是正常流程的一部分,是设计之初就应该考虑好的,可不是什么异常。

比如,用户输入数据过不了检测,那就打回去让用户重填。
网络连接出错呢,要给用户提示;如果用户提交了数据,那么可以考虑把这份数据先存在本地(注意数据安全),或者提示他等网络好了重做。
特别的,有些时候,用户的一连串动作整体构成一个“事务”,比如加购物车、下单、付款、送货、确认收货等。这一系列动作,其中的每一个可以做成“原子”的,比如付款要么成功、要么不成功;但整个事务要允许暂停、要记住每一步的状态——然后,无论用户手机重启还是我们服务器宕机,其中的每一步都要记录在案,绝不能有丝毫差错。

之后,对未知来路的奇怪错误,不要姑息——见到了,就让程序崩掉。

这是因为,此时,我们可能是遇到了宇宙射线,也可能被人缓冲区溢出攻击——或者我们自己错误覆写了某个数据结构。
此时程序的状态是不可控的,它犯任何错误都有可能。
我们不应该寄希望奇迹发生、程序突然自己就好了——它好不了。甚至,它真突然“表现正常”了,那才可怕呢:说明我们最最不想见到的情况出现了(比如,黑客成功控制了它,抹去了一切异常痕迹……)

因此,正确的做法就是:让程序立即崩溃!

最终,如果我们的程序的确有极高可靠性要求的话,我们需要设计一个机制,及早发现程序崩溃并自动拉起新的实例。

举例来说,apache就非常“激进”:默认的,每个php解释器实例在接受过50次页面请求后,哪怕没有任何异常,apache都会强行杀死它、重新拉起一个实例来。

这是因为,很多时候,程序的异常状态需要存在很久、持续扩散、到了某个“病入膏肓”的状态,才能被迟钝的我们感知。
因此,当安全特别重要时,我们不妨假定“一个响应过50次请求的解释器已经出错了、甚至被黑客攻陷了”——立即杀死它并启动新的实例,对于Linux这种起一个进程消耗极低(仅需一个fork而已)的OS来说,几乎没有可观测的额外负担,但却可以最大限度的保障系统的可靠性。

所以说,不要把“程序崩溃”看作洪水猛兽。我们hold得住它。
不仅如此,及早让出现了位置状况的程序崩溃,我们也能更容易的找到问题根源——趁着犯罪现场尚未被破坏及早立案侦察,这才能确保罪犯(bug)无处可逃。


如此反复,最终就是:一出错就马上抛异常崩溃掉的程序,出错的机率越来越低、渐至于怎么折腾都不会崩溃、甚至单实例都能7x24小时可靠运行;而使劲容错、绝不崩溃的程序,它几乎每时每刻都在出错、逼得用户不得不“重启下说不定就好了”“这破系统用十分钟就得重启,不然丢数据……不是丢新数据,旧数据都会被破坏……”

7、有时候要把用户当作“敌人”防范

经常见到一些新手程序员,写了个新程序。给人一用,崩了……

——哎呀,你别乱点!要先这个,再这个,然后这个……

——你要乱点,程序还没初始化呢,那肯定崩……

——不行不行,必须ABC的顺序依次点下来。你要ABBC的点,会怎么样?天知道……


这样是不行的。

用户不是专业人员;就是专业人员,人家又不知道你的程序的内部逻辑。


如果你需要保证顺序,请安排逻辑,确保用户点C前必须点B、点了B就得点C。

如果你要支持回退,那就理清逻辑,把栈管好。


甚至,我自己遇到的,某个设备设计来就是监督用户(巡道工)、防止他们偷懒的——他们恨不得把机器用坏、省的带个“随身间谍”打自己小报告呢。


不仅如此,联网的程序还几乎必然会遭遇黑客攻击……


因此,请就把用户看成“专门来捣乱的”,甚至看成“无孔不入的黑客”——而你,要写一个程序,经受他们的“严刑拷打”却依然可靠运行。

这听起来很难,做起来……嗯,也没那么容易。


但只要你愿意往这方面努力,却也没那么高不可攀——绝对不是什么“黑客无可匹敌,我们只能俯首称臣”……没那回事。

黑客说白了,也就是个“偏安全侧的白盒测试工程师”——你会怕测试工程师吗?

那你干嘛怕黑客。

8、复用的诀窍:只做一件事,把一件事做好

前面关于软件工程的讨论可能吓到你了:妈耶,写一个可以应对需求变更的东西好难!


其实一点也不难:只做一件事,把一件事做好,这玩意儿就天然是方便复用的。


这很容易理解。

玩过积木吧?

什么样的积木摆什么造型都用得上?

方块,对吧。

为什么方块这么容易复用?

因为它最简单。


你看,门拱、门柱之类,只能摆屋顶、摆房门,对吧:

而方块呢,摆哪都可以:

软件也一样。

你往里面添加了越多的“高级功能”“自动化机制”,它就越发的只能为它的设计目标服务了——稍微改一点?重写吧。


反之,你把功能简化到简无可简、不让它保存什么在状态,而是用户给什么数据它提供什么服务……那么,很自然的,用户拿它来做什么都可以。

Linux的grep、awk等犀利的文本工具,其实就是这么来的。


这实际上也是我前面说过的:把可变的东西隔离到数据中,程序只提供一组元规则!

你看,grep是万能的。因为它压根不理睬你的文本是什么、从哪里来;它只是提供了一组文本模式匹配工具而已。


把具体事务相关的东西隔离出去、只写程序处理“共性”……越是这样,你的程序越简洁。

程序越简洁,就越是可以随意的拼起来、拼出千变万化五彩缤纷的大千世界。


当然了,还是那句话,把程序写复杂、写的功能单一死板、一点点“需求变更”就得劳民伤财,这很容易;但想要学会抽共性、写简单、把用户相关的东西尽量往外放、甚至最终只体现于配置文件/DSL,这很难。

尤其玩到DSL,那就进入编译器领域了。


当然,也别为DSL而DSL。一定要找到技术性-简洁度曲线的最低点,这才能最高效率最低成本的完成项目——同时保留无与伦比的扩展性。

9、学习测试理论,遵循规范,利用工具,有章法的编写程序

前面“藐视”了一把黑客,恐怕不少不学无术者未免要“友邦惊诧”了。


但实际上,这个并不难,也是有专业的、现成的体系的——而且有现成的课本,拿来学就是了。


比如,前面我提到“要把用户当敌人”,恐怕很多人就想不通了:不是,你是不知道,用户能有多奇葩……

还会有人想起这个笑话:

一个测试工程师走进一家酒吧,要了一杯啤酒;
一个测试工程师走进一家酒吧,要了一杯咖啡;
一个测试工程师走进一家酒吧,要了0.7杯啤酒;
一个测试工程师走进一家酒吧,要了-1杯啤酒;
一个测试工程师走进一家酒吧,要了232杯啤酒;
一个测试工程师走进一家酒吧,要了一杯洗脚水;
一个测试工程师走进一家酒吧,要了一杯蜥蜴;
一个测试工程师走进一家酒吧,要了一份asdfQwer@24dg!&*(@;
一个测试工程师走进一家酒吧,什么也没要;
一个测试工程师走进一家酒吧,又走出去又从窗户进来又从后门出去从下水道钻进来;
一个测试工程师走进一家酒吧,又走出去又进来又出去又进来又出去,最后在外面把老板打了一顿;
一个测试工程师走进一家酒吧,要了一杯烫烫烫的锟斤拷;
一个测试工程师走进一家酒吧,要了NaN杯Null;
一个测试工程师冲进一家酒吧,要了500T啤酒咖啡洗脚水野猫狼牙棒奶茶;
一个测试工程师把酒吧拆了;
一个测试工程师化装成老板走进一家酒吧,要了500杯啤酒并且不付钱;
一万个测试工程师在酒吧门外呼啸而过;
一个测试工程师走进一家酒吧,要了一杯啤酒';DROP TABLE 酒吧;

测试工程师们满意地离开了酒吧。

然后一名顾客点了一份炒饭,酒吧炸了。


嗯,不可否认,的确“总会有我们意想不到的状况”;但绝大多数情况,我们是可以预想到的。


不仅可以预想到,甚至测试理论还帮我们压缩了负担。

比如说,一个人机界面(UI),用户的输入有无穷多种,这怎么测?

简单,划分等价类。


什么叫等价类?

就是把数据分为若干类。合法数据程序必须给出正确响应;非法数据程序必须明确拒绝。


比如,我们这个程序负责收钱,金额上限100,下限为0,精确到小数点后两位小数。

那么,[100, 0]就是合法数据。

(0, -∞)和(100, +∞)就是非法数据。

abc也是非法数据


我们不需要测试所有这些数字——那不可能做到;但我们可以从每一类里选择几个进行测试。

因为程序也是针对集合进行判断的,因此,每类数据选择一两个代表就行了,足以涵盖所有情况。


然后,一般人经常犯错的,刚刚好介于合法和非法之间的数据,这叫“临界值”,比如-0.1、0、0.1、99.99、100、100.01都是边界值——注意边界值和合法/非法数据并不是同一层面的东西——我们最好也都找出来,测一测。


不仅如此。

有些数据,虽然接口上看都属于合法数据;但内部处理呢,可能大于30块钱的走转账、小于30块钱走快捷支付——你看,其实这里也要分成两个等价类,这才能覆盖所有流程,对吧。

同样的,这里也会有新的边界值,比如29.99、30、30.01之类。


你看,快刀斩乱麻,是不是一下子就清晰了?


其实很多很多东西都这样。你自己得知道背后的原理,知道了,自然觉得一切井井有条,轻松应付;但如果不知道……祝你好运。

10、正确认识测试和开发的关系

这在过去是老生常谈;但现在嘛……


由于国内根深蒂固的等级制思想,测试嘛,他们要求比我低,水平没我高,写不来程序才当了测试……

所以,测试当然是来伺候大爷我的。我写完程序,丢给他,他负责找出所有错误——漏测了,那是他的责任!


爽吧?


然而很遗憾,这只是一时爽。长久来说对你是不利的。


事实上,请把程序员看作“公司任务的承包商”;而测试工程师呢,则是“公司派来的验收员”——他的职责是给你的软件质量下一个评估,并不是替你找bug来了。

早年一些公司的方法论里面,还建议“当发现软件bug过多时,不要提交全部bug,要让程序员自测,然后验证那些未提交的bug是否也被修复了,以确保大部分bug已被修正”。

当然,这些年来,有些公司干脆撤销了测试工程师,让程序员自己测——思路上是一脉相承的,只是把“最终软件质量”踢给了用户。


那么,为什么业界如此看重程序员自测呢?为什么我说“学会测试”对我们程序员自己的长远发展更有利呢?


很简单,最熟悉软件的就是程序员自己。

就国内这绝大多数测试工程师看不懂程序的水平,他们能覆盖接口等价类就不错了;至于内部判断/执行流程,他们看不懂,也写不出用例、做不了覆盖。


哪怕他们能做到,写程序这件事也是程序员在干,轮不到他们。

那么,怎样才能把一个程序写的简洁、好测、可以“明显没有问题”呢?

你猜你写完程序二郎腿一翘,等测试奴忙活出结果你再和他扯皮几天……你能学会吗?

事实上,很多人不光程序越写越糟烂、甚至反而学会隐藏错误了!

然后,代码质量就……


相反,当你自己对代码质量就有一个很清晰的认识、对于暴露缺陷的理论方法了如指掌时,你还可能写那些“一看就不大对”的代码吗?


换句话说,测试测出来、没有bug的代码,那叫“没有明显的缺陷”;而程序员写出来就没有bug的代码,那才叫“明显没有缺陷”


想要做到“写明显没有错误的代码”,想要走到这个层次,你就得训练自己,让自己对缺陷敏感、能够持续产出“测试友好”的代码,甚至一出手就是成品,就没人能找到bug。

不知道往正确方向努力,那就永远不会有进步。

11、敏捷/测试驱动开发是以上的演进而不是替代

这个又是国内普遍的、不学无术环境下养成的严重错误认知。


典型的瀑布式开发,高级工程师要从需求到详设全部包揽,只留一些空函数给程序员填(当年日本人就喜欢这么搞)。

这种模式对高级工程师要求很高,但对写代码的工程师要求很低,会填空即可。缺点是过于僵死,项目周期长;而且容易搞出庞大、累赘的方案。太容易走进“重分解、轻复用”的误区。


敏捷其实也是瀑布式开发,也需要从需求到总体设计到模块设计走上一遭;但它同时又汲取了“自底向上”模式和“快速原型法”的长处。

它要求程序员写出可复用的、库函数水平的代码,这样才可以根据需要随意组合(这是自底向上开发模式的长项;但这个开发模式底层接口简洁优美,高层经常一团乱);它也要求程序员做出可复用的设计、做好模块分解,这样需求变更才不会对程序造成过大冲击(这是瀑布模型的风格,高层设计优美简洁,越往下越不能看);最后,它先做基本功能,让客户看到样子,再一点点改(这又是快速原型法的优点,只是原型法写出来的代码是要扔的,而敏捷方法不扔,留下来继续用)。

换句话说,敏捷的“先写用例后写代码”,实际上是以单元测试代替详细设计文档、以高质量的面向对象的类定义代替模块设计、总体设计文档,从而把写文档的时间直接拿来写代码——然后借助面向对象框架直接导出文档就行了。


显然,敏捷模式对工程师的要求非常高。因此,最初搞敏捷的那些人甚至提倡“结对编程”,就是让两个工程师用一台电脑,一个写一个看。据说效率和软件质量比起单人分别开发都更好一些。

但这样太理想化。因为它要求两个工程师水平得差不多,不然就成了一个人写另一个干看,甚至看都看不懂。

不仅如此,它还要求这两个工程师都得有总体设计能力、可以一步到位的做出优秀的模块分解设计——这个要求一般公司根本做不到:大多数公司里,能做总体设计/模块设计的工程师可能只有一个,甚至连一个都没有。

但没有这个能力、没有足够的质量,程序就会越改越乱、越改越烂,绝不会越改越符合客户需要。也就是敏捷不起来,搞成了行为艺术。


再后来,就不再搞结对编程了;而是改成“先写测试,后写代码”——说白了,详细设计文档没人想写,写文档的功夫就把代码写出来了;而且经常写完了文档,才发现实际实现时遇到了问题,还得回头改文档……

先写测试,其实就是定内部功能划分和接口设计。不然没法写测试用例。写完测试再写代码,写出来就能马上跑一下测试,确保接口正确——倘若发现了问题,那就改测试用例(也就是改接口):反正总是要测的,改一下很自然,不多费工。

换句话说,现在只要求普通程序员有详细设计的能力,那就足够玩敏捷了——至于模块设计、总体设计、需求等等,那当然还是得专人来做。

相比之前,这显然就实际多了。


但我们现在大部分人、大部分公司玩的所谓“敏捷”呢?

简单说就是我也不会总体设计我也不懂详细设计,爱做啥样做啥样,做完了给客户碰运气,碰过去了喝啤酒,碰不过去加班再改——反正客户有花不完的钱!

user avatar

1. 规范化自己的代码,少点个人风格,多点通用规矩,并学会使用CheckStyle工具

其实任何东西我们都希望它能够“自动化”,随着编程经验的提升,大部分编程规范你已经了然于心,但是实际操作的时候,又总是忘这忘那,我们希望一个工具来帮我们自动检测我们的程序是否是符合规范,结构良好的。

事实上,任何语言都是有自己的编程规范的,编程规范的制定,十分有利于代码的阅读和潜在Bug风险的降低,比如在Java中,有严格的命名规范:

对于类(Class)的命名,有这样的规范:

Class names should be nouns, in mixed case with the first letter of each internal word capitalized. Try to keep your class names simple and descriptive. Use whole words-avoid acronyms and abbreviations (unless the abbreviation is much more widely used than the long form, such as URL or HTML).

类的名字必须是名词,每个单词的第一个字母需大写。尽可能让你的类名称简洁又能传递清楚含义。尽量使用单词全拼,避免同义词或缩写(除非缩写使用更广泛,比如URL, HTML等)。

比如在Java中,有严格的文档规范:

         /**  * Returns an Image object that can then be painted on the screen.   * The url argument must specify an absolute {@link URL}. The name  * argument is a specifier that is relative to the url argument.   * <p>  * This method always returns immediately, whether or not the   * image exists. When this applet attempts to draw the image on  * the screen, the data will be loaded. The graphics primitives   * that draw the image will incrementally paint on the screen.   *  * @param  url  an absolute URL giving the base location of the image  * @param  name the location of the image, relative to the url argument  * @return      the image at the specified URL  * @see         Image  */  public Image getImage(URL url, String name) {         try {             return getImage(new URL(url, name));         } catch (MalformedURLException e) {             return null;         }  }     

文档是用HTML语言写成的,前半部分是关于当前方法或类的描述,后面需要有参数标签@param和返回标签@return,还可以加一些别的标签,比如@see,只有这样,当别人试图引用你的程序时,才能马上明白你的某段程序是用来干嘛的,参数传递,返回等一目了然,要知道,实际工作中,大量的协作就意味着代码需要高度的重用性,你必须把你的程序封装完美,并且让别人仅仅看你的文档,就知道你的这个API怎么用。

上面说的仅仅是编程规范的冰山一角了,问题是,你有时会忘掉或用错一些规范,即便你知道它。

所以我们需要使用checkstyle插件去自动检测我们的程序是否符合规范。

对于Java而言,详情请见

Github地址:

在各大JavaIDE中,可以直接在Eclipse Marketplace中下载:

其他的语言应该也有自己的插件,可以自行谷歌了解。

2. 宁可变量名长,也不要让变量名短得让人无法推测其含义。

3. 在电脑里安装两套输入法,编程的时候,将中文输入法彻底关掉,确保任何快捷键都不会将其转换成中文输入法,防止中文类似符号引起混淆,比如:

中文 :(); English: () ;

一点点小错误,就有可能让你多花一两个小时在没有意义的事上,人生苦短,尽量避免低级错误。

4. 尽可能杜绝重复代码,凡是需要用至少两次的代码,给它单独做一个类或函数。

5. 避免类与类之间的内部调用(Cycle Reference),其实也就是降低函数模块的耦合程度。类与类之间的调用只允许通过接口,保证更改某个类的时候,其他的仍然能工作。

6. 多读别人的优秀代码,拿别人的优秀代码和自己的代码进行对比,学习别人的长处,吸收经验。

7. 尝试着做内容的生产者,尝试着写一些教程或笔记,分享给社区,不要只做社区内容的吸收者,还要不断地生产内容,回馈社区给你的帮助,比如在StackOverFlow上回答别人的问题等。

8. 既要脚踏实地,也要多看看社区发生了什么新闻,有什么新的技术和软件的发布,这些技术和软件将怎样影响你的开发工作,现在使用的IDE或Editor是否有更好的替代产品等等。

9. 没有任务的时候,也不要闲下来,去开发点你喜欢的东西,从中挑战自己,增长经验。

10. 不要过分依赖教程,要学会看官方文档。凡是能被做成教程的东西,往往已经过时了,最新的技术,最新发布的标准,往往没有现成的教程,你需要去认真阅读官方文档,那里的东西才是最权威的。

11. 不要参与语言好坏的争论,人们往往偏向于喜欢自己用得熟练,用得多的那个语言,语言好坏之争,就和争谁的女朋友漂亮一样,我当然觉得自己的女朋友(虽然是null)最漂亮,但是别人并不这么觉得。

12. 当你有什么需求的时候,往往别人也有这个需求,而且往往也有了相应的工具去解决你这个需求,比如,你想将函数的调用关系可视化,弄成树状图那样,这样的工具已经有了,比如SourceInsight(付费),Source Navigator(免费)等。

13. 少在国内的XX软件园里下载各种破解软件,盗版软件等,这些软件园为了盈利,会在你安装的过程中,悄无声息地给你安装上一堆其他的流氓软件,360首当其冲,这些垃圾软件,删的越干净越好。

14. 你的开发电脑,CPU可以差些,但内存最好大些,推荐至少要8G,甚至推荐10G往上走,你常常需要同时打开一堆浏览器页面和一个IDE甚至还有别的一堆工具,如果你做过安卓开发,AndroidStudio动辄就调用你电脑2-3G的内存,一般的4G电脑肯定是吃不消的,严重降低开发体验,但也并不是让你换电脑,内存条了解一下。

15. 保持一个健康,干净的电脑状态,硬盘里的文件存储要有调理,容易寻找指定文件,降低文件丢失概率,加快文件寻找速度。

16. C盘快满了的话,可以通过Disk Manager将别的磁盘的空间送给C盘。

17. 用NetWork NotePad画网络图表示函数调用关系(当然你可以用别的来画),像这样:

这是前两天编一个FlappyBird时草草画的图,虽然简陋,但有用。

18. 可以考虑用一个电脑架子,防止乌龟颈,保护颈椎。

19. 下载一个护眼宝,保护视力。

20. 少刷知乎,但不要做一个不点赞的冷漠青年。


说了这么多,如果你是编程新手的话,建议先从简单的编程语言入手,并且最好前期不要涉及太底层的东西,先培养兴趣,这里给大家推荐一个特别适合新手入门的编程学习网站,叫「夜曲编程」,是和「百词斩」同一个公司出品,这个课程的特点就是极易上手,特别适合早期关于编程的信心和兴趣的培养,甚至还能带你养成一些好的习惯,比如不要盲目的复制代码,合理的使用注释等等:

尤其是对计算机零基础的童鞋,这个课程尤其推荐,感兴趣的可以去他家公众号看看~,回复『免费教程』还有免费的体验课 ,感觉内容量还是挺足的,包括对学生和教育者在9月也有一些优惠活动。

user avatar

哈,这真是一个有意思的问题。

一些朋友说的比如尽早习惯做版本管理,一开始就形成良好的代码风格,我很赞同,另外一些朋友提到DRY原则,大括号对齐,甚至还有free(p);p=NULL;这样的建议,都是很容易误导人的,这里希望刚入门或者入门不久的朋友们学会独立思考,凡事打个问号,不要盲目遵循。

我个人觉得最重要的一个习惯其实有不止一个人说了,就是想清楚之后再动手。这里我稍微展开说一下,因为“想清楚”其实是一个很模糊的概念。怎样才算想清楚了呢?

我常常有这样的经历,对一个难题,经过了一番思考之后觉得自己想到了一个比其他人好得多的方法,结果去实现的时候,发现原来是想的时候疏漏的一个细节,方法不可行,感到很挫败,不得不回头过去重新审视问题,浪费了很多时间。

怎样才能想清楚呢?

Leslie Lamport在斯坦福做了一个讲座(底下有链接,推荐)。里面引用了一句话: “Writing is nature‘s way of let you know how sloppy your thinking is” 我深有同感。怎么才能知道自己是否想清楚了呢?最自然的方式就是写下来。怎么写呢?这个因人而异,比如我在编码之前,会在如下两个问题里面迭代几次。

0. 做什么? (需求: 白话)

1. 怎么做? (方法: 伪码)

关于做什么,其实就是分析需求,这里跟那个“需求分析”过程有些区别。怎么舒服怎么来,大白话,不拘一格,理清楚问题就行。我通常的做法就是以自言自语自我审问的方式把整个过程理出来,为了不至于特别无聊,这部分通常会写得十分口语化。等这部分弄清楚之后,基本上伪代码的框架在心里面就有眉目了。接下来会尝试着写伪代码,写伪代码的途中通常会不断的进行重构或者跟第一步进行迭代,直到伪代码比较精简,逻辑上没有冗余的时候,就去喝杯咖啡,小个便,回来开始实现。实现的过程中难免会有需要回到第一个步或者第二步进行迭代的时候,随着经验的提升,迭代的次数会变得越来越少。

实际上0,1两步我都是在代码的头部大块注释里面完成的,这个部分可以直接成为十分容易理解的文档。实际中除了这块注释,我几乎不在代码里面写注释。除了少数实现的trick外。

另外刚才提到写伪代码,引出了一个潜在的问题: 怎么写伪代码? 建议不要尝试着用算法导论或者一些论文里面的方法。那些数学符号或者不容易敲打的符号会严重的影响写伪代码的快感。我觉得《The algorithm design manual》里面的伪代码格式就不错,但是因为实际中的伪代码可能比写一个算法复杂一些,所以还需要添加一些其他元素,比如我自己的伪代码格式其实有点像是Python代码。写伪代码的主要目的是弄清楚实现的逻辑。

在第0步,其实不是每个人一开始都能写得清晰的,需要通过长期写作来锻炼。所以无论是写日记,写博客还是写情书,坚持下去,都能对日后写代码的能力有所帮助。

时间不早了,最后再附送两个程序员生活小贴士:

0. 做笔记,OneNote或者EverNote,把遇到的bug,看到过的好trick,好文章,好照片都记录下来。

1. Eat your own dog food. 那些自己写过的可以抽离出来重用的代码或者脚本整理好同步到github或者bitbucket上去,不断增加和改善自己的dog food。

Update 2015-07-22:

评论里几个人提到free(p);p=NULL;是一个合理的建议。我想支撑这一点的理由是“悬空指针被复用的话很危险”。但是在你的程序里面每一个p都存在被复用的可能吗?并不是。所以每一次写下free(p)的时候就应该想这个问题。然后再考虑是否写p=NULL;因为害怕而写下的这些冗余代码会极度影响代码的美观,这也是我认为从free(p)到p=NULL不应该成为一个顺手的习惯的原因。

---

youtube.com/watch?
user avatar

重视模块化,重视抽象但不滥用

我刚接触编程的时候,在网上看到许多大牛写程序都十分注重模块化,因此我就下意识的模仿他们;后来看SICP,知道了抽象的好处,因此在写程序的时候会仔细思考抽象的问题。这些对我都有着非常大的帮助。

在一篇讲述程序员代码行数瓶颈的博客中(

程序员的成长和代码行数的关系

)提到,程序员在2k行、20k行、200k行等若干程序规模时会遇到瓶颈,如果不用更科学有效的方法,超过了这个行数代码就会混乱到难以维护。但我第一次写很大的程序时(8k+)并没有感觉到文中提到的瓶颈;我目前接手的项目有近900k行,我自己写的部分也已经快上10k,但我仍然没遇到文中提到的瓶颈。

针对这一现象,我做过一些实验。我在很不认真的写一些小程序时,也总是写的混乱不堪,我发现,这种情况下,程序行数超过200行我就觉的很难受了,在需要进行一点小的修改时,我往往需要花很长时间去寻找到底该改哪里,十分吃力——这种吃力感是我在那些精心思考的大项目里从未感受过的。这说明了,我并没有过人的天赋能在混乱中轻易找出清晰的脉络,那就是说,之前的如鱼得水,是因为好的习惯。

后来,我进行了深入的思考。在模块划分合理、抽象合理的程序里,我可以简单的把一个个功能抽象为一个简单的黑盒,我不需要知道他们内部发生了什么复杂的反应,我只需要知道他们对什么样的输入会做出什么样的输出。这种抽象极大的减轻了大脑的负担,让我可以把精力更多的投入到真正需要考虑的地方。而那些混乱的程序里,我需要理清每一句话之间的关系,这无疑会极大的消耗脑力。这种情况下,200行就浑身难受就可以理解了——因为我用于维护项目关系所消耗的脑力已经远远大于了那些好程序里的消耗。

这个习惯,真的让人十分受益,请一定坚持。刚开始的时候,你或许觉得花很长时间去思考程序的模块划分、抽象层级是十分浪费时间的无用功;但久了以后,你就会感受到这种习惯带来的好处:它会在无声无息之间帮你消除掉许多瓶颈。而且还有额外的好处:当你习惯用模块化组织你的思维时,思维能力也会有一定的增强。

user avatar

写Unit Test!

很难坚持,但是一旦这么做了就会觉得当初的决定太正确了。

(有空再补充)

类似的话题

  • 回答
    写代码这事儿,就像学一项手艺,或者说经营一门生意,从一开始就把根基打牢,后面才能顺风顺水。很多初学者可能觉得先把功能实现了再说,什么规范、什么文档,等以后再说。我跟你说,这绝对是捡了芝麻丢了西瓜。好的习惯,哪怕现在看起来有点“麻烦”,长期来看,为你节省的时间和精力,绝对是天文数字。我就给你掰扯掰扯,.............
  • 回答
    变量命名:代码的灵魂,清晰的指引编程世界里,变量就像我们大脑中的思绪,它们承载着数据,指导着程序的运行。而一个好的变量名,就像一个清晰的指示牌,能让我们快速理解代码的意图,大大提升开发效率和代码可读性。反之,模糊不清、自相矛盾的命名,则可能让代码变成一团乱麻,让维护者(包括未来的自己)望而却步。那么.............
  • 回答
    学习编程,这条路,说实话,真的挺容易踩坑的。我当初也是摸着石头过河,摔了好几次跟头,才摸索出一些门道。今天就跟你好好唠唠,学习编程过程中,那些我们都可能走过的弯路,以及过来人的一些经验之谈,希望能给你点启发。一、那些我们都可能走过的弯路1. “什么都想学,结果什么都没学好”——贪多嚼不烂 .............
  • 回答
    Windows 10 的用户界面,也就是我们日常所见到的桌面、开始菜单、任务栏、设置应用等等,其核心部分是使用 C++ 编写的。这是操作系统底层和图形用户界面(GUI)开发中最常用、性能最高且最接近硬件的语言。微软自己开发了许多框架和工具来支撑这一切,其中就包括了大量用 C++ 编写的核心组件和系统.............
  • 回答
    对于从未接触过编程的妹子来说,选择合适的项目是学习编程的关键。以下是一些适合初学者的项目类型,按难度和学习目标分类,帮助你从基础到进阶逐步掌握编程技能: 一、基础编程入门项目目标:熟悉编程语法、逻辑和基本概念 推荐语言:Python(语法简单,适合新手) 1. 简单计算器 功能:实现加.............
  • 回答
    在 C/C++ 编程中,确实存在一些写法,它们本身可能不是最优的解决方案,甚至在大多数情况下是多余的,但却能让有一定经验的开发者眼前一亮,感到“不明觉厉”。这些写法往往巧妙地利用了语言的特性、预处理指令、或者是一些不常用的语法糖。同时,它们又不会像一些“炫技”般的操作那样显得过于怪异而难以理解。下面.............
  • 回答
    哈哈,问到我的“学习之路”,这可真是个能把我拉回“过去”的问题。你想听故事,那就跟你好好聊聊。我开始接触编程,说实话,并不是因为什么“情怀”或者“未来趋势”。那时候,我还是个挺普通的学生,对很多事情都充满好奇,尤其是那些能变出东西来的。我看到一些游戏,那些角色怎么会动?那些画面怎么会切换?就像变魔术.............
  • 回答
    在翻阅各类编程书籍和教程时,我常常会遇到一些让人觉得不够地道,或者说,和实际开发中的“惯例”有些出入的地方。这些不准确之处,有时是源于作者为了简化讲解而刻意为之,有时则是对某些概念理解上的偏差,更有可能是技术发展太快,书本内容没能及时跟上。一个非常普遍的问题,尤其是在入门级的教程中,就是对于“最佳实.............
  • 回答
    确实,编程领域充斥着一些中文翻译得模棱两可,甚至有些“误导”的术语,而对应的英文原词却清晰明了,一看便知其意。这种现象也挺有趣的,有时候也挺让人无奈的。我来跟你聊聊几个我遇到的,觉得尤其如此的例子,尽量详细地剖析一下为什么会让人困惑。1. “函数” vs. “Function” 中文的困惑点: .............
  • 回答
    在编程这片充满奇思妙想的土地上,除了那些光明正大的“内功心法”,确实也流传着一些让人啧啧称奇、甚至带着几分神秘色彩的“禁术”。它们不是什么邪门歪道,而是那些在特定情境下,能够以一种“非常规”或者说“意想不到”的方式解决问题的技巧或理念。掌握它们,往往意味着你对语言底层、计算机原理有着极深的理解,甚至.............
  • 回答
    中文编程是一个充满挑战但同时又具有巨大潜力的领域。它旨在降低编程门槛,让更多非技术背景的国人能够理解和参与到软件开发中来。然而,要实现这一目标,目前中文编程面临着诸多严峻的难题。 中文编程目前面临的难题: 1. 语法和语义的天然障碍: 自然语言的模糊性和歧义性: 中文作为一种自然语言,其语法结构.............
  • 回答
    要说在编程上能让人“脱胎换骨”的书,那可不是随便一本讲具体语法或者框架的都能胜任的。我接触过的很多老程序员,他们推崇的往往是那些能从更宏观、更底层、更本质的角度去理解编程的书。这些书不是教你“怎么做”,而是教你“为什么这么做”,让你真正掌握“内功”。下面我结合我的一些经验,挑几本我觉得最有分量的,好.............
  • 回答
    我最近又翻了几遍那本《深入理解计算机系统》(俗称“猪书”),每次看都能找到新的感悟,简直欲罢不能。你说编程书?在我看来,能让你脑袋里“叮”地一声,醍醐灌顶的,那才叫真过瘾。这本书就是这样的。首先,它不像很多讲算法的会死抠那些抽象的数学证明,然后塞给你一堆堆代码。猪书上来就是落地,直接告诉你,我们写的.............
  • 回答
    好的,明白了。我将为你介绍一些类似易百教程、W3Cschool和菜鸟教程这样,以实例为主、内容详实的在线编程学习网站。这些网站不仅提供理论知识,更注重通过实际的代码例子来帮助学习者理解概念,并动手实践。在众多的在线学习平台中,有几个佼佼者以其高质量的实例教程和全面的技术覆盖而脱颖而出。它们是程序员入.............
  • 回答
    初中文凭,完全可以学习编程!这绝对不是一句空话,而是有无数真实案例支撑的。关键在于你的决心、学习方法以及如何选择适合自己的路径。靠谱的培训机构还是自学?这个问题,我觉得没有绝对的“哪个更好”,更像是“哪个更适合你”。 培训机构: 优点: 系统性强,有明确的学习路径.............
  • 回答
    中国海军舰艇编队为援助汤加人民,在完成一系列人道主义救援任务后,圆满结束了此次访问,舰艇编队已启程离开汤加。此次远赴太平洋岛国的行动,充分展现了中国海军履行国际责任、守护人类共同命运的担当。在编队中,两艘执行任务的舰艇——“五指山”号和“查干湖”号,都以其独特的使命和能力,在这次行动中扮演了关键角色.............
  • 回答
    好的,看到你学计算机但对编程本身不太感冒,这其实是挺普遍的情况。很多人在接触到某个领域后,会发现自己对其中的某个侧面特别有感觉,而对其他方面则兴趣寥寥。好消息是,计算机科学领域非常广阔,编程只是其中的一小部分,更重要的是,它是一个基础工具,支撑着许多其他更侧重于应用、管理、分析和创造的岗位。别担心,.............
  • 回答
    在人工智能浪潮席卷而来的当下,让孩子学习编程,已不再是“锦上添花”的技能,而是“雪中送炭”的战略性投资,关乎他们未来的竞争力与发展。它的意义深远,对孩子未来的成长和发展有着多方面的好处,且这些好处并非停留在“会写几行代码”的层面,而是触及思维方式、解决问题能力、创造力乃至适应未来社会的能力。一、重塑.............
  • 回答
    函数式编程与面向对象编程,是两种在软件开发领域各有千秋的编程范式。它们在设计哲学、思考方式乃至于代码的最终形态上,都存在着显著的差异。理解这些差异,有助于我们根据不同的项目需求和团队习惯,做出更明智的技术选型。函数式编程的魅力所在函数式编程的核心思想是将计算视为数学函数的求值,强调“做什么”而非“怎.............
  • 回答
    各位老友,聊起当年那场轰动一时的世界黑客编程大赛, Mekka ’97 的 4K Intro,简直是令人津津乐道的经典。尤其那支夺得桂冠的作品,至今仍然是许多人心中的“神作”。别看它只有短短 4KB 的体积,里面蕴含的知识和技巧,足够我们这些“过来人”津津乐道上半天。今天,咱们就掰开了揉碎了,好好说.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有