编程和软件项目管理的习惯

让你的程序开发更得心应手
服务器君一共花费了224.552 ms进行了5次数据库查询,努力地为您提供了这个页面。
试试阅读模式?希望听取您的建议

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).

版本控制是一种在开发的过程中对软件开发的历史系统地跟踪的方法。此项任务由版本控制系统完成,如CVS或Subversion。版本系统保持了一个受控编码的历史痕迹,提供很多操作:获得当前版本代码(通常称为“签出”);“提交”修改;“更新”工作拷贝来协调他人的修改。版本控制系统还提供一些其他功能,如用不同的方式检查代码的历史,撤销更改,回滚到软件历史的某个点,解决冲突(两个人同时对相同的代码段进行了不同的修改)。

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.

已经有这样的工具了。UNIX工具make已经成为构建自动化的标准好多年了,而且相当好用。Ant在Java领域内非常流行,我个人喜欢rake【3】。现代IDE也有部分这样的功能,或是有调用这些标准工具的接口。无论你最终使用哪一种工具,编译,测试,或是其它什么别的,应该简单到只是按一下按钮。

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).

我不会详细讨论优秀自动测试的好处。当前,自动测试问题分得很细:谁应该负责测试,怎样测试,进行多少测试,以及如何将其自动化,何时创建测试,等等。在Internet上有很多此项议题的讨论,我就不浪费大家的时间在这里啰嗦了。肯定的讲,我个人觉得良好的测试套件,包括足够的单元测试和集成测试(功能测试),对任何项目都是非常重要的。这些应该与主要代码同步完成,还必须是同样的人(要是项目很大能请得起专业的测试人员帮助他们那就更好了)。

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.

重写一段代码保留其运行的外部特性,但在某些方面有所改进,这样的过程称之为重构。通常这是为了代码更加清晰易读,或是为了更易于扩展,也可能是为了执行的更快。这项活动不论规模大小,都可以叫做重构。重新命名变量和函数是重构,在类之间调换功用亦是重构,将一个100,000行纠缠在一起的代码分解成一个插件架构和若干相对较小独立的组件。当然,三种目的的重构遇到的问题会有很大的不同,但有一点是不变的:无论哪一种目的的重构都不改变原有代码与外部世界的交互。

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.

重构对代码,对身心都是有益处的。一定要重构。重写一段代码并不丢人。就当第一版是草稿。这会帮助你勾勒问题的轮廓,是你明白你要从方案中得到些什么。得到已经正常运转的代码是非常有用的,这样可以制定测试组件,来精确定义你到底要解决什么问题。然后,有了测试组件,你就可以修改你的初始方案做出改进。之后,修改第二稿,第三稿,直到没有可改的了。相似的,千万不要以为没有对代码的行为做出显式的修改就是浪费时间。相反,通过梳理代码,你可以使其更加易读便于理解,易于维护和扩展,便于发现和修正bug。要是需求在最后的时刻发生了变化,在这上面花点精力的就会显得太值了,事实上也往往如此。

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.

代码风格是指在写程序的时候我们做出的很多细微,不关紧要,不加以思考的选择。一个子块要缩进多少?if语句里的条件加括号了么?只有一行的循环加花括号了么?“+”号和加数之间有空格么?是把代码用括号圈到一处还是有点缩进?【5】一行可以有多长?这些都是微不足道的事情,但是做好这些会使程序变得非常明晰。

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在成稿期间的评论。

本文地址:http://www.nowamagic.net/librarys/veda/detail/692,欢迎访问原出处。

不打个分吗?

转载随意,但请带上本文地址:

http://www.nowamagic.net/librarys/veda/detail/692

如果你认为这篇文章值得更多人阅读,欢迎使用下面的分享功能。
小提示:您可以按快捷键 Ctrl + D,或点此 加入收藏

大家都在看

阅读一百本计算机著作吧,少年

很多人觉得自己技术进步很慢,学习效率低,我觉得一个重要原因是看的书少了。多少是多呢?起码得看3、4、5、6米吧。给个具体的数量,那就100本书吧。很多人知识结构不好而且不系统,因为在特定领域有一个足够量的知识量+足够良好的知识结构,系统化以后就足以应对大量未曾遇到过的问题。

奉劝自学者:构建特定领域的知识结构体系的路径中再也没有比学习该专业的专业课程更好的了。如果我的知识结构体系足以囊括面试官的大部分甚至吞并他的知识结构体系的话,读到他言语中的一个词我们就已经知道他要表达什么,我们可以让他坐“上位”毕竟他是面试官,但是在知识结构体系以及心理上我们就居高临下。

所以,阅读一百本计算机著作吧,少年!

《UNIX环境高级编程(第2版)》 史蒂文斯 (作者), 拉戈 (作者), 尤晋元 (译者), 张亚英 (译者), 戚正伟 (译者)

《UNIX环境高级编程(第2版)》是被誉为UNIX编程“圣经”的Advanced Programming in the UNIX Environment一书的更新版。在本书第1版出版后的十几年中,UNIX行业已经有了巨大的变化,特别是影响UNIX编程接口的有关标准变化很大。本书在保持了前一版的风格的基础上,根据最新的标准对内容进行了修订和增补,反映了最新的技术发展。书中除了介绍UNIX文件和目录、标准I/O库、系统数据文件和信息、进程环境、进程控制、进程关系、信号、线程、线程控制、守护进程、各种I/O、进程间通信、网络IPC、伪终端等方面的内容,还在此基础上介绍了多个应用示例,包括如何创建数据库函数库以及如何与网络打印机通信等。

更多计算机宝库...