服务器君一共花费了180.389 ms进行了4次数据库查询,努力地为您提供了这个页面。

Over the years, I have discovered many things about the art of programming. Some I later discovered to be wrong, others I forgot and then rediscovered, others yet were discoveries of the extent of what I must yet discover. I also developed a set of habits in the domain of administering programming projects, whether they be my own, or by a small group, or larger, corporate affairs. These habits are, on the one hand, essential to the success of any software (that is not so small or so lucky that it manages regardless by sheer chance), on the other hand sufficiently easy to describe as to fit in a single essay, and on the third hand not known and loved as universally as they should be. Therefore, I wrote that single essay, and you are now reading it.


This document is organized as a collection of individual, bite-sized pieces of advice, grouped about six general topics. One reason for this is that they interact with each other, enough that it is impossible to write them down linearly, without forward references. The other reason is to allow you to jump around, to skip the bits you already know, to revisit bits you want to think about again, or to just run down the list without having to sift through the explanations.


Version Control 版本控制

Version Control is a means to systematically track the history of a piece of software as it is being developed. This task is carried out by a thing called a version control system, such as CVS or Subversion. Said thing maintains, explicitly or otherwise, a history of the code being controlled, and provides operations to acquire (usually called check out) the current version of the code, to check in or commit one's modifications to it, and to update one's working copy to incorporate changes made by others. The version control system also provides functions for examining the history of the code in various ways, for undoing various changes made and/or returning to previous points in the software's history, and for resolving conflicts (two people changing the same code at the same time in different ways).


Anyone who has ever hit the "Undo" button in a text editor or recovered a backup file knows the wonders of having the computers remember things that are not currently relevant or immediately apparent. Since code is more complicated than normal documents, this is even more important in software. And if you haven't had the pleasure of developing software with others, take my word for it that all the potential for confusion and data loss is multiplied by the presence of more than one person. For these reasons, version control is an invaluable tool in the process of producing software, and it becomes even better if used well.


So, how do you use version control well?


  1. Use a good version control system. My current favorite is Subversion — it's effective, it's Free, it's fairly popular and getting more so, and it has few defects that are effectively corrected by any other system. I will not claim it's perfect, but it's a fine default choice until you know better.
  2. 选用一个优秀的版本控制系统 我现在非常中意Subversion,它不但高效,而且免费。它现在非常流行,并且会变得更加流行。它几乎不干扰其他开发过程。我并不是说它是完美的,但在你找到更好的之前确实是一个不错的选择。

  3. Version from the start. If there is anything about a project worth recording in any more permanent form than a scrap of paper, it's worth versioning.Corollary: If you have already started a project without version control, set it up and check it in right now.
  4. 一开始就要版本化 要是在项目中有些东西要保存在比草稿纸更加正式一点的地方,那么绝对要将其版本化。 推论:要是你已经开始了一个没有版本控制的项目,马上建立一个,并提交项目。

  5. Version even if you work alone. Yourself three weeks from now is a surprisingly different person from yourself today. Following good version control practice will make your intentions clearer as you carry them out, and will be invaluable if you ever forget anything about what you were doing (which, believe me, you will). Besides, you get great backups for free, [1] so there's really nothing to lose, and everything to gain.
  6. 就算你单干也要版本化 三个星期后的你将大大有别于今天的你。努力实践版本控制使得你实现你的意图的时候非常清晰,要是你忘记了手头做的什么事情(相信我,你会忘的),那它可有大用处了。此外,你还免费的做了一个备份,【1】看来这么做一点损失都没有,好处倒不少。

  7. Version everything humans create. Code (of course), test cases, build scripts, documentation, to-do lists, explanations, presentations, ideas, requirements — anything to do with the project that is created by human minds should be in the version control system unless and until you find a good reason to put it somewhere else.
  8. 只要是人类的智慧就要版本化 代码(肯定是人类的智慧啦),测试用例,编译脚本,文档,任务列表,解释说明,演讲稿,想法,需求—只要是经过人的大脑创造出来的一切,都应该记录在版本控制系统里,除非你有更好的理由将它们放到别处。

  9. Do not version computer-generated files. Doing so only introduces another way the state of the project could become inconsistent (e.g., if someone checks in a source file and forgets to regenerate some dependent generated file). Much better to ask the version control system to ignore those generated files, and just regenerate them whenever they are needed. But do version every original human-created thing needed to generate those files, including the commands that carry out the generation process.
  10. 计算机生成的文件就不必了,这样做只能导致项目出现不一致(如有人提交了源文件,却忘记生成了有依赖关系的衍生文件)。比较好的做法是让版本控制系统忽略这些衍生文件,需要的时候再生成就是了。但是万万要把能衍生文件的人类原创途径记录在案,包括执行生成过程的命令之类的。

  11. Write good log messages. Every good version control system will ask you for a log message with every commit, whose purpose is to explain what you were doing when you checked that code in. Do not blow these off, but write them, and write them well.
  12. 做好日志 好的版本控制系统都会在每一次更改的时候让你留下日志,目的是解释一下你对提交的代码都做了些什么。千万别忽略这一步,一定要写,并且好好的写。

    • You are writing for yourself as much as for others. Being conscientious about writing good log messages will force you to think clearly about your designs, be honest about your hacks, and remain aware of what you are doing, as well as informing anyone (including your own future self) who wants to know what was going on in the head of whoever wrote the code they are now staring at.
    • 不光是为别人而写也是为自己,认真细致记录日志会迫使你梳理你的设计,看清问题所在,认清正在做的事情,也会使得想知道细节的人(同样包括未来某时的你)与代码的作者有个交流。

    • Write what you did (and when needed why), not so much how. If the "how" is interesting or not clear from the change itself write it, of course, but a lot of the time the code will reveal the "how" well enough. If anything is unclear, it is likely to be your intentions.
    • 把“做了什么”记下来(必要时补充“原因”),而不是“怎么做的”。要是“怎么做的”真的那么令人感兴趣,而且从修改本身很难去理解,当然有必要记一记,但是通常代码本身已经很能说明“怎么做的”了。要是真的有什么地方不清楚的话,那一定是你的思路。

    • Describe everything you did. The version control system can help you figure out what changes you made; do try to talk about all of them. Corollary: Try not to do things that are so complicated that you won't be able to explain them. Break such activities up into multiple smaller steps and check them in one by one.
    • 描述点滴所做 版本控制系统能帮助你找到你所做的更改,要试着将所有的修改都详细的告诉系统。推论:不要做自己都解释不清的事情。要将其分割成很多比较小的步骤,然后一个一个的来。

  13. Never break the build. Every time you check in, the system should be functional. Someone updating to that point should be able to compile it (if applicable) and run the test suite, and have the test suite pass. Checking in errors is extremely rude to everyone you are working with (including your aforementioned future self) because it becomes impossible for them to know whether the system is broken because of something they did or because you checked in something bad. [2] Corollary: If you do break the build, apologize, and your top priority is to fix it.
  14. 不能搞破坏 每一次你提交了代码,系统应该是好用的。也就是说,其他人此时更新代码后应该能够编译(可能的话),执行测试套件,并通过测试。将错误提交了是对与你协同的人(还是包括未来的你)极大的不尊重。这会让他们搞不清楚到底是因为自己的问题还是你提交的一些垃圾导致了系统不能运转)。【2】 推论:要是你真的搞破坏了,要道歉,并立刻修复。

  15. Commit small, semantic changes. Ideally, every commit should consist of a single act, and every log message should amount to a paragraph with a single topic sentence. As a good rule of thumb for deciding whether two related things are one or two semantic changes, ask yourself whether someone might want to undo one of them without undoing the other. If the answer is "yes", commit them separately.
  16. 修改要小到原子 理想的情形下,每一次修改只包含一个意图,每条日志只是说明一个问题的单句段落。这有一条傻瓜法则用以判断两个相互联系的事情到底是一个还是两个原子修改:问问自己会不会有人只想撤销其中一个而保留另一个。如果答案是肯定的,就要分开来提交。

  17. Don't keep uncommitted changes for long. The longer something goes uncommitted, the more likely you are to forget what you were doing, to introduce bugs, to collide with others working on something related. If you haven't committed your changes, you are not done for the day, unless you have a very good reason not to check whatever it is in.
  18. 不要改而不交 拖延修改的提交时间越长,你越容易忘了自己都做了什么,越容易产生BUG,也越容易与其它人的相关工作不协调。要是你没有提交修改,其实一天的活儿就不算干完,除非你有更好的理由。

Build System 构建系统

A build system is a collection of automations of the tasks of software development. The most common such task, and the one from which these systems get their name, is compiling the code. Running the test suite against the code is also something the build system will allow one to do, as are other things like generating browsable documentation from code comments, putting together tarballs for release, etc. Automating these tasks saves an insane amount of human time and effort, and prevents errors that arise from these tasks being done incorrectly or neglected through laziness.


The world has tools for this. The UNIX utility make has been the standard in build automation for eons, and definitely does the job well. ant is popular in the Java world, and I personally like rake. [3] Modern IDEs may also provide some of this functionality, or hooks for calling out to these standard tools. Whichever tool you end up using, compiling, testing, and whatever else may be applicable should happen at the push of a simple button.


Build system advice: 构建系统建议:

  • Use a real build tool. Learning a whole new tool for this task of building can be daunting, but it is worth your while. Your project will outgrow a trivial shell script in no time, whereas no project is too small for the standard tools, and very few projects are too big. [4]
  • 使用真正的构建工具 学习一个全新的工具是有些令人却步,但却很值得。简单的脚本总是能搞定你的项目。不会有项目小到不适合应用这些标准工具,只有极少数特大号的项目不适合。【4】

  • Automate everything. Compiling and testing should be automated right from the start. Generation of documentation or code, cleanup, installation, etc, as soon as you start doing it. In general, any task that will get done more than once is a good candidate for automation.
  • 使一切自动化 编译和测试应该从一开始就精心地自动化了。同样当你开始生成文档或者代码,做清理,安装等等的时候,也要使其自动完成。总的来说,要干第二次的任务最好就要自动化。

  • Communicate the automation clearly. In particular, there should be a well-defined "build" that people should not break when they commit. Typically this will consist of successful compilation and execution of the test suite, but in any case, make it clear what command runs it, make it clear that it should always work, and ensure that it is obvious whether or not it worked in any given run.
  • 自动过程要明晰 尤其要注意,当大家提交东西的时候,应该有一个精心定义的“构建”,一种大家都不应该破坏的“构建”。通常,这会包括成功的编译结果和测试结果,但是,一定要说清楚哪些命令用来执行,保证其总是能干活。并确保在任意一次执行都能清楚地知道它是否运转正常。

Automated Test Suite 自动化的测试套件

A test suite is a collection of tests whose purpose is to verify that code works. It is automated if it can be executed, and if its results can be evaluated, entirely by computer.


Tests can be categorized according to the amount of code they exercise: A unit test verifies the functionality of a single software component, such as a function or a class. An integration test verifies that several components, possibly the entire system, work together properly. A functional test verifies that the system (typically all or much of it) behaves correctly at a reasonably high level. These last two definitions overlap, though with somewhat different flavors, and in my experience, there is no industry-wide consensus on which is which. The terms are often used to distinguish them from unit tests.


I will not dwell on the merits of good automated tests. At present, the industry is divided as to who should be testing software, how, how much, how automated the tests should be, when they should be written, etc. This subject sees plenty of discussion on the Internet, and I will not waste your time by repeating it. Suffice it to say that I personally believe that a good test suite, consisting of adequate coverage both by unit and functional/integration tests, is an essential piece of any software project, and that it should be written at the same time and by the same people as the main code (though help from dedicated testers is a nice bonus, for projects that are large enough to afford them).


The world has tools for writing test suites: Java has JUnit, Python has PyUnit, Ruby has Test::Unit, etc. Practically every programming language has a testing library in the xUnit style, and most offer alternatives. Do not fear this profusion of options: the presence and execution of a test suite is far more important than which particular testing library powers it.

已经有一些用于编写测试组件的工具了,Java有JUnit,Python 有PyUnit, Ruby有Test::Unit, 等等实际上每一种编程语言都有xUnit风格的测试库,绝大部分还提供别的方法。不要被这么多选择吓着,是否使用测试组件比到底用哪种组件重要的多。

How, then, does one create a good test suite?


  1. The test suite should be checked in. The tests are as much a part of the code as the stuff that runs in production, if not more. Version them. They should be shared, backed up, and tracked like all the rest of it, and everyone should run the same suite.
  2. 要提交组件 要知道测试程序也是代码的一部分,和那些在产品中真正运行的东西一样重要。将其版本化。像其它部分一样,共享,备份,跟踪都要进行。另外,所有人都要使用相同的组件。

  3. The test suite should be automatic. There should be one clear button (such as the command make test) that executes all the tests and reports the results.
  4. 测试组件要自动化 应该有一个清晰的按钮(比如说‘make test’命令)可以执行所有的测试,并得出结果报告。

  5. The test suite should be unambiguous. After running the tests, it should be obvious whether they passed or failed, and if any failed, which ones. There should be absolutely no human judgment involved in deciding whether something is a success or a failure. The tests should not produce any output that might obscure the report from the framework.
  6. 测试组件应该是清晰明确的 运行了测试之后,通没通过应该一目了然,如果没通过,那一部分没通过也应该显而易见。绝对不要参砸任何的人为判定到底是通过了还是没通过。测试也不要产生任何可能会使报告费解的输出。

  7. The tests should always pass. Checking in something that fails tests constitutes a build breakage, and should never be done (or if done accidentally, should be immediately fixed). If you know the code is right and the test is wrong, fix the test. If you need to check in but some test is failing for a stupid reason that you don't have time to debug immediately, remove it from the suite temporarily, but solve that problem as soon as possible.
  8. 一定要通过测试 提交未通过测试的东西无异于对“构建”进行破坏,是要绝对禁止的(如果不巧发生了,要立即修复)。如果你知道代码是正确无误的,是测试出了问题,那就要修改测试。如果你的确要提交,但是有些测试就是毫无道理的通不过,你也没时间立即debug,那么暂时将其从组件中去掉,过后要马上弄好。

  9. Run the tests often. When you update, before you commit, during development. Running the test suite (or at least a relevant portion of it) should be part of your development cycle.
  10. 经常测试 修改的时候要测,提交前要测,开发期间要测。运行测试套件(至少也是其中的相当的一部分)应该成为你开发周期的一部分。

  11. Write the tests first. No, really. When fixing a bug, first write a test that fails because of it, then fix the bug. When the test passes, you got it. When adding a feature, first write tests that require it. This helps you understand what the feature ought to do, and to know when you got it right.
  12. 测试先行 不要惊讶,我是说真的。当修正bug的时候,先要写一个能针对它的测试,然后再修复bug。要是测试通过了,就成了。添加新的功能的时候,先写好针对它的测试。不仅会帮助你理解这个功能到底应该干什么,还会在干成了的时候通知你。

  13. Tests are executable documentation. Unlike normal documentation, they never lie, because everyone runs them and they always pass. If you think a piece of code is confusing, write unit tests for it. If you write real documentation, write tests that verify every statement you make in it.
  14. 测试是可以执行的文档 与普通文档不同,测试从不说谎,因为人人都运行还总得通过。如果你觉得一段代码难于理解,那么就写一个单元测试。如果你写了实际的文档,写写测试来验证一下其中的陈述。

  15. Test everything. If it's not blindingly obvious that a piece of code works just by looking at it, write tests that check that it works. You'll be surprised at how many bugs you catch, and how many you prevent from ever forming.
  16. 依靠阅读来测试代码能否工作显然是行不通的,所以还是写测试来检验到底是否正常运转。你将会惊讶地发现找到很多bug,也会惊讶于避免了相当多的bug产生。

  17. Only test what actually matters. It's all too easy to write tests of the form "when I run this program on this input, it should produce these 10,000 lines of output that I have in this file." While often better than no test at all, this is a terrible kind of test, because alongside the things that actually matter, it also tests a million irrelevant details (like floating point round-off). Updating these kinds of tests to reflect desired change in the program is either painful or prone to allowing the entry of bugs.
  18. 只做有效的测试 很容易写如下的测试:“当我运行这个程序时给这个输入,就会产生10,000行我这个文件里的输出”。通常这样做比没有好,但绝对是一个糟糕的测试,因为除了真正起作用的东西外,也测试了许许多多无关的细节(如浮点舍入误差)。改进类似的测试来适应程序中的修改,要么极其痛苦,要么很可能引入bug.

Code Reviews 代码审核

Code review is the process and practice of reading over code written by someone else in order to detect errors, suggest improvements, etc. It goes a long way towards cleaner, better designed, and generally superior code. A second pair of eyes and a second perspective on the problem at hand are extremely helpful for a clearer, better solution. Code reviews also help programmers educate each other about useful techniques, methods, styles, etc. As soon as there is more than one person working on a project, start reviewing one-another's code. Ideally, every line of code should be read by at least two people: its author, and a reviewer.


There are many choices to be made about code reviewing practice. When to review code? how thoroughly to review it? who should do the reviewing? In the limit of a large project worked on full time by many people, every piece of code should be reviewed as soon as (or before, if there is good tool support for this) it is checked in. Every piece of code should be reviewed by someone who knows the code around it, and can understand its effects (and, importantly, any mistakes it might be making). In this scenario, the review should be prompt (within a day or two), and the author should try not to change the same code further until the review is done, to avoid muddling the process.


Smaller projects with fewer people will necessarily not work this way. If there isn't much code, it evolves quickly, and can't afford the delay introduced by reviewing every checkin. Nevertheless, code reviews are good for the code and for the people writing it (since, among other things, they have the effect that at least two people understand any given piece of code). Use whatever pattern fits the project, but get into the habit of reviewing code and asking others to review yours.


If you are the reviewer: 如果你是审核人:

  1. Be prompt. If someone asked you for a code review, either do it or let them know you won't (and redirect to a more qualified reviewer) quickly. Don't make the author wait.
  2. 及时 如若有人请你审核一段代码,要么马上开始,要么痛快的告诉人家不行(并转交给更有资历的审核人)。千万不要让人家等。

  3. Be respectful. The objective of the review is to ensure code quality, not figure out who is smarter than whom. As the reviewer, you have a great deal of power — do not abuse it.
  4. 尊重 代码审核的目的是确保代码的质量,不是为了显示出谁比谁高明。作为审核人,你的确有很大的权力,但是不能乱用。

  5. Be thorough. If you don't understand something, it's either not coded clearly enough or not commented thoroughly enough, or both. Ask the author to clarify (not only to you personally — in the code). If something seems wrong, it probably is, or if it's right but looks wrong, it's confusing. Bring it up.
  6. 详尽 若是有些东西你搞不清楚,要么是代码写的就不够清楚,要么是注释不够,也可能二者兼有。请作者澄清(不单单是对你私下里的解释,要写进代码里)。要是什么不对劲,这很可能,或者是对的但看上去是错的,或是晦涩的,都要告诉作者使其修改。

  7. Enforce policies. If your project has policies or conventions (code style, naming conventions, testing, etc), ask the author to correct any violations. This may seem like nitpicking in some cases, but those policies and conventions were presumably established for a good reason, so they should be upheld.
  8. 执行规定 要是项目中有些规定或规范(代码风格,命名规范,测试等),发现不符的地方一定要让作者修改。有些时候这样做显得吹毛求疵,但是定下这些规定和规范是有缘由的,所以应该遵守。

If you are the author whose code is being reviewed: 如果你是被审核代码的作者:

  1. Be respectful. The code reviewer is your friend, and is giving you good advice. If you disagree with any of it, that may be a good subject for a constructive discussion. If the reviewer misunderstood something in your code, you probably didn't code it clearly enough.
  2. 尊重 审核人是你的朋友,给你有价值的建议。如果你有些不同见解,那么这绝对是建设性谈话的好议题。要是审核人对你的代码有误解,那么极有可能是你没有把代码写清楚。

  3. Don't take criticism personally. Code reviews are about improving code, not about bruising (or boosting) egos. A review will necessarily focus on the things you got wrong, because that's where the improvement can happen. The criticism in any decent code review will all be constructive (including, possibly, the constructive request to think more deeply about some aspect of the code), and if it's not, that may be a good thing to have a polite conversation about with the reviewer.
  4. 不要以为批评是针对你本人 代码审核是要改进代码,不是要刺伤(或者是提升)你的自尊心。审核之所以一定要关注你做错的地方,是因为那就是要改进的地方。对不良代码的批评都是建设性的(很可能包括积极的建议从某方面深入思考),要是并非如此,也许找审核人礼貌的谈一谈是个不错的选择。

  5. Get reviews early and often. Few things in software are as obnoxious as having to review an enormous pile of new code — except writing one and having a reviewer discover that you did something stupid in all of it. This goes hand in hand with making and checking in small changes one at a time. Even if you have to do what seems to be a little extra work to break your piece into multiple working, reviewable chunks, the mistakes saved will pay you back a hundred-fold. Ten 100-line code reviews are much nicer to do than one 900-line one, and doing it that way will save a great deal of work finding and fixing bugs.
  6. 尽早尽量获得审核 审核一大堆刚写的代码是非常非常讨厌的,只有一种例外,审核人发现在整个部分你都在犯傻。这个过程需要一点一点修改提交。即使你认为要多做一点儿,将工作拆分成易于审核的小块,这样会避免很多错误,以使你得到数倍的回报。十个100行的代码审核要比一个900行的来得轻松得多,拆分着做会节省很多寻找修正bug的工作量。

Refactoring 重构

Refactoring is the process of rewriting a piece of code so as to leave its execution behavior intact, but improve it in some other way. Usually the purpose is to make the code clearer and more readable, or more extensible, or perhaps faster. Activities of this sort can be called refactoring at any size scale. Renaming a variable or a function is a refactoring, as is shifting around responsibilities between classes, as is splitting a 100,000-line ball of tangled dependencies into a comprehensible plugin architecture with comparatively small, independent components. Of course, the abstraction barriers that are preserved in these three scenarios are vastly different, but the theme of reworking the guts of some code without affecting its interactions with the outside world runs through all three.


Refactoring is good for the code and for the soul. Do it. There is no shame or insult in rewriting a piece of code. Think of the first version as a draft. It helps you sketch out what the problem is like, and what sorts of things you need from a solution. The existence of working code is a wonderful thing for setting up a test suite that defines what the problem you want to solve is, exactly. And then, that in hand, you can modify that initial solution and improve it. And then improve the second draft, and the third, as long as there is room for it to improve. Likewise, do not think you are wasting time if you are not changing the externally visible behavior of the code. On the contrary, by making code cleaner, you make it easier to read and comprehend, easier to maintain and expand, easier to detect and correct bugs. This investment of energy before the deadline pays off immensely when the desiderata change in the eleventh hour, as they are all too prone to do.


So, how to refactor: 那么,怎么重构呢:

  1. Do refactor. I cannot stress the value of this enough.
  2. 重构 其价值如何强调都不过分。

  3. Do not mix refactoring with actual change. The statement that the behavior of the code is not supposed to be different before you started and after you finished is an immensely powerful tool in verifying and validating your work. To the extent possible, check in your refactorings as such, one by one, and check in your actual changes as separate commits.
  4. 不要将重构与实质性的修改混为一谈 代码的行为在你重构前后应该是一致的,相同的。这也是检查与验证你是不是进行重构的极其有效的手段。尽可能将你的重构一个一个的提交,而将实质性的修改分开提交。

  5. Test what you refactor. With all but the simplest of refactorings, if it's complicated enough to refactor, it's complicated enough to be worth testing. Conveniently, by the definition of refactoring, there is some boundary at which the code's behavior does not change. If your test suite already has sufficient tests at that boundary, great. If not, write some, and check them in before you start. It's a great way to make sure your refactoring doesn't break anything.
  6. 测试重构的代码 除了最简单重构外,值得重构的代码,也只得测试。相应地,由重构的定义可知重构应该在不改变代码行为的框架内进行。若是测试套件已经有了足够的边界条件测试,很好。要是没有,做一些,并在你开始重构之前弄好它们。这是一个很好的方法可以使你的重构不拖累其它部分。

  7. Refactor in bite-size pieces. It's all too easy to say "I need to rewrite this program" and go out and try to redo it all in one fell swoop. It never works. You never get it to be the same, and then you wonder whether the difference is a bug or a feature, and what did the old code do about this anyway, etc, etc, etc. Disaster. Especially with refactoring, when the objective is, in a sense, to stay in the same place, it is always possible to break the journey down into steps small enough to validate.
  8. 一小块一小块的来 信誓旦旦地说“我要重写这个程序”,然后埋头就干,企图一下子把所有都重做一遍。说说很容易,但绝对行不通。这样绝对不会得到看上去一样的程序,你就开始想是个bug呢,还是原本特性如此呢,原来的代码到底对此事如何处理呢,等等。绝对是灾难。在重构过程中,当目标总是停滞不前,我们总是可是把事情分成一小段一小段的,以便查验。

  9. Don't be afraid of stupid-looking intermediate states. If, for example, you want to change an interface, first add support for the new one, then convert all the clients to using it one by one, and then delete the old version. There is no reason to do this all at once — spread it out over several commits if it's too big. Sure you've got some intermediate states where the code looks stupid, and some clients are doing it one way and some the other, but the program works, the tests pass, so you can verify that what you've done so far didn't break anything unexpected. Every refactoring can be decomposed like this. Look for that decomposition, and avail yourself of it to reduce your work to manageable pieces.
  10. 别害怕丑陋的中间产物 举例来说,如果你想修改一个接口,首先为新的接口提供支持,然后将所有客户端逐个转换,然后去掉旧版本的接口。要是修改过于庞大的话,不要一蹴而就,将其拆分成几个小部分完成。可以肯定当你修改到一半时,代码看起来会很难看,有些客户端用新方法,有些用旧方式,但是程序依旧正常工作,测试也正常通过,你好检验你的修改没有引入任何不希望的破坏。重构都可以像这样来拆分。尝试拆分,尽量将工作化成可以度量的小块儿。

  11. Don't get stuck in stupid intermediate states. If you started a chain of refactorings, finish it. The point is to make the code cleaner, but the intermediate states will typically have all the old nastiness plus the intended solution mixed together. Don't let the program live in that kind of state for long.
  12. 不要卡在中间 要是开始了进行重构,那就进行到底。关键是代码要变清晰,但是干了一半儿的活儿通常会是旧的混乱加上新的方案混合在一起。绝对不要让程序长时间出于这种状态。

  13. Refactor to pave the way for actual change. How often have you started doing X only to discover that you need to change Y for it to work, and to change Y the way you want, you first need to rearrange Z, and got lost and made a huge mess? We've all been there, but to go there less often, predict that you'll need this, then refactor Z first, test it, check it in, then refactor Y, check that in, and then finally do X. You'll make nicer code and fewer mistakes, and even if do you mess something up, you'll be closer to a working state that you can go back to.
  14. 通过重构为实质性修改铺垫 你是不是经常开始做X的时候发现必须要先改Y才能行,而在改Y的时候,你又发现得先改Z,最后呢你困惑了,结果是一团糟。我们都有过类似的经历,只是相对少一些而已,预先估计都需要改什么,然后先重构,测试,提交Z,接着是Y,最后是X。这样会得到更好的代码,减少错误,即使真的有东西弄乱了,你也更容易让其步入正轨。

  15. Don't be afraid to delete code. Just because someone (possibly you) spent a long time writing it doesn't mean it's the right way to do the job. The code helped to define what the job is and what challenges arise in doing it, and its author's work is respected in the continued use of that knowledge. Even the knowledge that the job does not need to be done is useful, and sometimes worth all the effort spent writing code to do it. On the other hand, dead, unused code only clutters a program and makes it harder to read and understand, so delete it. If later you decide you needed it after all, the version control system will have it safe and sound for you to recover.
  16. 别怕删代码 人们(很可能是你)花了很多的时间去写并不意味着是代码一定能很好地完成既定任务。这些代码帮助定义了任务是什么,完成此项任务会遇到怎样的困难,这些都对日后的工作很有用,因此其作者的工作很值得尊敬。即使是知道了不用这么做也是很有帮助的,有时候很值得投入全部力量去写代码做这项似乎无意义的事。另一方面,无效的,废弃的代码只会使程序变得拥挤不堪,难于阅读和理解,所以还是删掉的好。要是后来你又最终决定用了,版本控制系统会将其完好无损的还给你。

  17. Don't comment out code you mean to delete. I have known people to comment out blocks of code they were rewriting and check them in commented out. Don't do this. Commented blocks of code are not only unhelpful but actively confusing. If you mean it as a backup and an opportunity to return to the old state, remember that the version control system does that better. If you mean it as an explanation of what the new code is supposed to do, write that explanation in English instead. Whatever your purpose, whole blocks of commented code are not the best way to achieve it.
  18. 不要把应删掉的代码注释掉了事 我知道有些人重写提交代码的时候,把旧代码注释掉。千万不要这样。将代码注释掉不仅没有任何帮助而且非常容易造成混乱。如果你是想有一个备份用来恢复,那么记住版本控制系统做这个更加在行。如果你是想解释新代码应该做什么,那么还是用英语解释更合适。不管你的目的是什么,注释代码都不是个好主意。

Code Style 代码风格

The phrase code style refers to the myriad of tiny, irrelevant choices we make almost without thinking when we code. How much do we indent a subblock? Do we put parentheses around the condition of an if statement? Do we put braces around one-line for loops? Do we put spaces between the + operator and the summands? Do we compactify long-range close parentheses or indentation-match them? [5] How long can a line get before we break it? These are little things, but doing them consistently makes a program appreciably more legible.


The world has tools for this. There are programs that will look over source code and verify that it obeys some style criteria, and programs that will even modify the source code to obey such criteria. I have never used them myself, nor been in projects that used them in earnest, so I cannot comment on their worth, but I would be remiss if I did not mention their existence. The advice below applies regardless of the use of such a program.


  1. Have a code style. All the little things that the compiler or interpreter doesn't care about should be done the same way throughout a project. It is not necessary to write it down at first, but do make sure that everyone knows it. As the project grows, a written document will become useful.
  2. 找到一种代码风格 不被编译器或解释器重视的微小部分应该至始至终的保持一致。没必要一开始就将其写下来,但是一定要确保每一个人都知道。随着项目的增长,要形成书面的文档。

  3. Agree on the code style. Some polite, constructive discussions during the project's infancy, when it's still small enough that someone can go and carry out a change of style, could be quite helpful. There is no reason to keep doing something the way someone accidentally started doing it, if some alternative is clearly better. On the other hand, don't waste too much time. Consciously make any decisions that need making, and get on with the project.
  4. 认同这种代码风格 在项目初期,因为项目太小了一个人就可能改变风格,这时礼貌的和富有建设性的讨论就很有好处了。要是有明显更好的途径,绝对没有理由坚持一开始的老路。另一方面,不要在此方面浪费太多时间。继续做要做的决定,并在项目中实施。

  5. Respect the language's code style. If the community around the programming language(s) you are using has any code style conventions, follow them. Doing this offers the same benefits across projects as does having a code style within one project.
  6. 尊重编程语言的代码风格 要是你所使用的编程语言的社区已经有某种代码风格习惯,一定要遵循。这么做会使得项目之间相互收益,就像同一种代码风格会对一个项目本身有好处一样。

  7. Obey the code style. Even if you personally like your open curly braces on the same line, if the project has decided to put them on their own line, put them there. Consistency, and the readability it creates, are more important than personal taste.
  8. 遵守这个代码风格 即使你个人偏好在同一行开始一个花括号,要是项目要求其独立成行,还是按着做吧。一致性,和由此而来的稳定性比个人的口味更加重要。

  9. Maintain local consistency. If some file or component you are changing does not obey the global code style, somebody really ought to convert it, but if you don't have time for that now, obey the style prevalent around the change you are making. Local consistency is more important than global.
  10. 保持局部一致 如果你要修改的文件或组件不符合全局的代码风格,应该有人将其转换,但是如果你当时没有这个时间,那么遵循你要修改代码周围的代码风格。局部一致比全局一致更加重要。

Parting Words 结束语

If any of this is new to you, I encourage you to incorporate it into your work. It may take a little while to get used to, but trust me, it's worth it. While you're figuring any of these things out, I encourage you to err on the side of overdoing rather than underdoing. By definition, not knowing about a principle constitutes a massive underdoing of it, so you haven't bracketed the problem until you find yourself in an excess of a good thing. When you know both that it is good and that you have too much, then you can trust your judgment on how much is just enough.


Notes 注释

  1. If your working copy dies, you have the repository whence to get another working copy. If your repository dies, then even if you haven't backed it up, you have a working copy (or several) from which to restore the current state and continue. It is, however, a good idea to back up the repository, so that you will be able to restore the history of the project.
  2. 要是你的工作拷贝完蛋了,你还有版本库,所以你还是能弄到一份工作拷贝;要是你的版本库完蛋了,就算你没有备份它,你还是有一份(或多份)工作拷贝,你还是可以恢复当前状态,并接着干活。不过呢,备份版本库才是好的方式,这样你就可以连同项目的历史都能恢复出来。

  3. In many programming environments it will be possible to check in experimental code in such a way that no one except the author is affected by it. Such code is not part of the build, so checking in a broken experiment does not, by definition, break the build. While this sort of thing may be necessary in some situations, I caution against abusing it — by avoiding the discipline of maintaining the build, you also avoid that discipline's benefits.
  4. 许多编程环境是允许提交试验性代码的,这种代码除了提交者本人外不会影响任何人。这样的代码不属于构建,所以就算提交了残缺的试验性代码也不能破坏构建本身。当然有些情形下这样做是必须的,但是我必须要警告大家不要滥用:避免构建框架的束缚的同时,也丧失了构建框架的好处。

  5. rake is like make, except it uses Ruby as the build specification language instead of the ad-hoc language that make interprets, and I find that that advantage outweighs rake's comparative youth and consequent lack of some sophisticated features.
  6. rake和make差不多,它用Ruby作为描述语言,比较新,但有些复杂的功能尚不具备。

  7. If, nondenominational deity forbid, your project should outgrow even the tools' capacity, then you are justified in hiring build engineers to come up with a better, tailored solution, but that is a problem that strikes corporations, not people.
  8. 要是你的项目增长的比工具的能力还迅速,那么你正应该考虑雇佣一名专门的构建工程师来定制一个适用的系统,不过那是公司的事儿,不是个人的。

  9. This is the code style issue that programmers of Lisp-like languages argue about, instead of the slew of those akin to the preceding three that plague their colleagues working with other tongues.
  10. 这种风格问题是Lisp程序员提出来的,并困扰了很多其他语言程序员很长时间。

Thanks to Gregory Marton, Tanya Khovanova, and Arthur Gleckler for commentary during the evolution of this.

感谢Gregory Marton, Tanya Khovanova和Arthur Gleckler在成稿期间的评论。





小提示:您可以按快捷键 Ctrl + D,或点此 加入收藏






《高性能JavaScript》 Nicholas C. Zakas (作者), 赵泽欣 (合著者), 丁琛 (译者)

《高性能JavaScript》揭示的技术和策略能帮助你在开发过程中消除性能瓶颈。你将会了解如何提升各方面的性能,包括代码的加载、运行、DOM 交互、页面生存周期等。雅虎的前端工程师 Nicholas C. Zakas 和其他五位 JavaScript 专家介绍了页面代码加载的最佳方法和编程技巧,来帮助你编写更为高效和快速的代码。你还会了解到构建和部署文件到生产环境的最佳实践,以及有助于定位线上问题的工具。如果你使用 JavaScript 构建交互丰富的 Web 应用,那么 JavaScript 代码可能是造成你的Web应用速度变慢的主要原因。