Code Simplicity
https://www.codesimplicity.com/
这是一本很短的书,讲的是如何做好软件:要求软件开发者有正确的思维方式和行为方式,最终体现在代码上就是代码要简单。豆瓣上的评价不是特别高 https://book.douban.com/subject/7159029/ 觉得里面车轱辘话很多,我觉得如果降低这本书的期望的话,其实这本书还蛮不错的,有些观点其实可以引发更加深入的思考,而不是仅仅停留在文字表面。我的一个体会就是,书这个东西真的是因人而异,合适的人,在合适的时间,读到合适的书是最好的。经典的大部分不适合初学者,一些看似平淡的语句或者是生活经历描述,也只有过来人才能体会,否则就会显得矫揉造作,自然是没有好的评价。
清楚自己干的事情,对于这件事情背景了解约清楚,那么就会干的越好。
The difference between a bad programmer and a good programmer is understanding.
That is, bad programmers don’t understand what they are doing, and good program- mers do. Believe it or not, it really is that simple. The more you understand what you are doing, the better you can do it. It applies to programming just the same as every other field in the world, except that it’s more important in programming because writ- ing software is almost purely a mental activity where understanding is everything.
简单代码意味着对于其他程序员来说(包括未来的自己)更容易阅读和维护,而不只是说代码量少,或者是依赖少,或者是没有使用新技术,又或者是快速完成任务。
A good programmer should do everything in his power to make what he writes simple for other programmers to use and comprehend.
Misconceptions About Simplicity
Sometimes this idea of simplicity is misunderstood to mean that programs should not have a lot of code, or shouldn’t use advanced technologies. But that’s not true. Some- times a lot of code actually leads to simplicity; it just means more writing and more reading, which is fine. And usually, advanced technologies lead to more simplicity, even though learning them takes time.
Some people believe that writing simple code takes more time than quickly writing something that “does the job.” There is no data of which I am aware that validates this idea. Serious software development nearly always has long timelines—weeks or months at the shortest. When you add complexity into your program, you’re slowing yourself down tomorrow. Every study that I have read (and all of my personal experience) con- cludes that writing simple code ultimately gets the job done faster, even when you think that complexity is a shortcut.
设计上不要求民主或者是妥协,而是需要有少数的设计者来决定,以此来体现设计上的一致性。但是设计者需要倾听其他人的意见,有时候这些设计者可能还需要说服其他人来相信这个设计,这样才能让别人来一起写代码,这也要求设计者不能独断专行。
However, this does not mean that design is a democracy. You must not design by com- mittee. The result will be an actively bad design—one which makes things more com- plex instead of simpler. Instead, all developers should have the authority to make good design decisions in their own areas. If they make poor or mediocre decisions, these should be overridden by a senior developer or the lead programmer, who should have veto power over the designers below them. But otherwise, responsibility for the design of code should rest with the people who are actually working on it.
A designer should always be willing to listen to suggestions and feedback, because programmers are usually smart people who have good ideas. But after considering all the data, any given decision must be made by an individual, not by a group of people.
软件的目的是为了帮助人,如果这个没有这个思维,那么做不出好的软件。我在想,这是否意味着软件设计上是否也需要体现出某些共情:App开发者需要共情终端用户(App好用),Lib开发者需要共情于普通开发者(Lib好用/效率高)。当然每个软件侧重点不同,共情并不意味着一味地简化接口把用户当白痴。
Is it possible to derive a single statement of purpose that would fit all software? Well, I believe that I have.
The purpose of software is to help people.
We can break this down to a more specific purpose for individual pieces of software. For example, a word processor exists to help people write things, and a web browser exists to help people browse the Web.
Some pieces of software exist only to help specific groups of people. For example, there are many pieces of accounting software that exist to help accountants; these target only that specific group of people.
What about software that helps animals or plants? Well, its purpose is really to help people help animals or plants.
The important thing here is that software is never there to help inanimate objects. Software does not exist to help the computer, it always exists to help people. Even when you’re writing libraries, you’re writing to help programmers, who are people. You are never writing to help the computer.
作者这里说了为什么需要将feature request理由写下来,我觉得这些依据可能是比较通用的:
There are also several other useful reasons to ask this question:
- It helps resolve uncertainties about the feature’s description or how it should be implemented. For example, the answer above about keyboard shortcuts tells us that the implementation must be fast, because that’s the value users get out of it.
- It helps the team come to an agreement about the value of a feature.Somepeople may not like the idea of keyboard shortcuts, but everybody should be able to agree that the answer above explains why they are valuable. In fact, some developers may even have a better idea of how to fulfill that user’s need (interacting with the text editor more quickly) without keyboard shortcuts. That’s fine! If the answer leads us to a better feature idea, we should implement that instead. The answer tells us what’s really needed, not just what the user thought he wanted.
- Answering the question will make it obvious that some features are more important than others. This helps the project leaders prioritize work.
- At the worst,if our text editor has be come bloated with too many features over time, the answer can help us decide which features should be removed.
软件价值包括两个部分:价值概率以及价值大小。价值概率是:有多少用户可能使用到这个软件web browser),而价值大小是:用户多大程度上可以受益于这个软件(EDA)。无论如何,价值都和用户(人)相关,如果某个feature没有人使用的话,或者它的开发周期太长用户无法忍受的话,那么它就没有任何价值。
Value is actually composed of two factors: the probability of value (how likely it is that this change will help a user), and the potential value (how much this change will help a user during those times when it does help that person).
Features that have no users have no immediate value. These could include features that users can’t find, features that are too difficult to use, or features that simply don’t help anybody. They may have value in the future, but they have no value now.
This also means that in most cases, you must actually release your software in order for it to be valuable. A change that takes too long to make can actually end up having zero value, because it doesn’t get released in time to help people effectively. It can be important to take release schedules into account when determining the desirability of changes.
估算软件开发开销的时候,不能仅仅考虑自己部分的cost, 还需要考虑其他组件;不仅要考虑当前的cost, 还需要考虑应对将来变化的cost. 如果将这个cost全部囊括进来的话,那么设计/实现上就会尽可能地避免complex: 因为我们很难估计complex的东西在将来的cost.
When considering the effort involved in a change, it’s important to take into account all the effort that might be involved, not just the time you’re going to spend program- ming. How much research will it take? How much communication will all of the de- velopers have to do with each other? How much time will you spend thinking about the change?
In short, every single piece of time connected with a change is part of the effort cost.
作者总结来一个公式,来计算desirability of change(D): 我们多大程度上需要修改软件: D = (Vn+Vf) / (Ei + Em), 其中
- Vn = Value of now
- Vf = Value of future
- Ei = Effort of Implementation.
- Em = Effort of maintenance.
简单来说就需要考虑Cost(现在/将来)和Value(现在和将来). 不过我们将时间拉得足够长的话,那么Vf和Em比重会逐渐增大,就是说基本上等于 D ~= Vf/Em. 如果Vf我们没有办法控制的话,那么降低Em就应该减少Complex而增加Simplicity,当前simple code可以更好地应对将来的变化。
The desirability of a change is directly proportional to the value now plus the future value, and inversely proportional to the effort of implementation plus the effort of maintenance.
“Future value” and “effort of maintenance” both depend on time, which causes inter- esting things to happen with the equation when we apply it to a real-world situation. To demonstrate these, let’s pretend we can use money to solve the equation for both value and effort. “Value” will be measured by how much money the change will make us. “Effort” will be measured in terms of how much money it will cost us to implement the change. You should not use the equation this way in the real world, but for the sake of our example, it’s going to simplify things.
Guiding Principles是这么一类帕累托规则:只要遵循它,不管未来如何,情况不会变得更差。我们没有办法预测未来,但是我们只要遵循guiding principles的话,我们就可以做出更好的软件。
This is why, in any type of engineering—including the field of software development —we have “guiding principles.” These are certain rules that, when we follow them, keep things working well no matter what happens in the future. That is what the laws and rules of software design are—our “guiding principles” as designers.
So yes, it’s important to remember that there will be a future. But that doesn’t mean you have to predict that future. Instead, it explains why you should be making decisions according to the laws and rules in this book—because they lead to good future software, no matter what that future brings.
It is not even possible to predict all the ways that a particular law or rule may help you in the future—but it will help, and you’ll be glad you applied it in your work.
三种错误应对Law of Change的做法:写不需要的代码,代码写得太死,代码写得太灵活。1好理解就是不要随便增加feature, 因为more code, more tests, more bugs. 而2/3之间则有点矛盾,需要根据情况做好平衡。正确应对方法就是,迭代式的设计和开发,不至于出现明显的flaws.
The Three Flaws
There are three broad mistakes that software designers make when attempting to cope with the Law of Change, listed here in order of how common they are:
- Writing code that isn’t needed
- Not making the code easy to change
- Being too generic
Incremental Development and Design
There is a method of software development that avoids the three flaws by its very nature, called “incremental development and design.” It involves designing and building a sys- tem piece by piece, in order.
premature optimization的一个现实比喻。和所有其他类似行为一样,premature optimization都是在没有足够证据的情况下进行的改进,这些改进可能会造成某些功能broken, 可能甚至会造成性能退化。软件改进中,evidence是非常重要的东西。
The most famous error in this area is what we call “premature optimization.” That is, some developers seem to like to make things go fast, but they spend time optimizing their code before they know that it’s slow! This is like a charity sending food to rich people and saying, “We just wanted to help people!” Illogical, isn’t it? They’re solving a problem that doesn’t exist.
其他程序员并不是缺乏智力来理解你的代码,缺乏的是知识:他对代码里面的pattern, 以及细节通理解通常不会有太大问题, 相反最大的理解问题是“你这个是在解决什么问题,为什么需要这么设计,这么设计有什么好处”。
Many programmers are particularly bad about this with their code. They assume that other programmers will be willing spend a lot of time learning all about their code, because after all, it took a lot of time to write it! The code is important to them, so won’t it be important to everybody?
Now, programmers are generally an intelligent bunch. But it’s still a mistake to think, “Oh, other programmers will understand everything I’ve done here without any sim- plification or explanation of my code.” It’s not a matter of intelligence—it’s a matter of knowledge. Programmers who are new to your code don’t know anything about it; they have to learn. The easier you make it for them to learn, the faster they are going to figure it out, and the easier it will be for them to use it.
There are lots of ways to make your code easy to learn: simple documentation, simple design, step-by-step tutorials, etc.
另外编写复杂代码,或多或少是对阅读者的一种鄙视:我可以写出这么复杂精巧的代码,所以我比你更high-level. 这种传达含义是非常implicit的但是对于人际关系和团队关系影响却是非常toxic的。我觉得可以认为,代码也是某种介质,通过这种介质可以对他人施加某种影响: 如果你的代码是complex的,那么某种bully的成分在其中;而如果你的代码是simple的,那么他们或许能感受到其中的真诚和友好。
None of us like being talked down to or treated like we’re idiots. And sometimes that leads us to create things that are a little complicated, so that we feel like we aren’t talking down to the user or to other programmers. We throw in some big words, make it a little less than simple, and people respect our intelligence but feel kind of stupid because they don’t get it. They might think we’re way smarter than they could ever be, and that is kind of flattering. But really, is that helping them?
On the other hand, when you make your product or code stupidly simple, you’re al- lowing people to understand it. That makes them feel smart, lets them do what they’re trying to do, and doesn’t reflect badly on you at all. In fact, people will probably admire you more if you make things simple than if you make them complex.
我觉得下面这段话很好地解释了为什么历经考验的软件很难被替换掉。从这个软件本身来说,它积累了足够的场景,解决了足够多的问题,并且还有市场先发优势。对于后起之秀如果想与之竞争,那么它也不可避免地需要解决complexity的问题:我可以很容易地在1-2个场景下面比竞品表现非常好,但是随着场景越来越多,我需要更多更多的功能/代码才能表现同样良好。而更多的功能/代码就会带来complexity, 这种complexity不仅仅是这个功能本身,还牵扯到其他组件以及未来的change,所以完成它需要的时间不是线性的,并且codebase本身需要非常careful design才能适应之后的变化。
When you work as a professional programmer, chances are you’ll know somebody (or you are somebody!) who’s going through this common development horror story: “We started working on this project five years ago, and the technology we were using/making was modern then, but it’s obsolete now. Things keep getting more and more complex with this obsolete technology, so it keeps getting less and less likely that we’ll ever finish the project. But if we rewrite, we could be here for another five years!”
Another popular one is: “We can’t develop fast enough to keep up with modern user needs.” Or, “While we were developing, Company X wrote a product better than ours much more quickly than we did.”
We know now that the source of these problems is complexity. You start out with a simple project that can be completed in one month. Then you add complexity, and the task will take three months. Then you take each piece of that and make it more complex, and the task will take nine months.
Complexity builds on complexity—it’s not just a linear thing. That is, you can’t make assumptions like: “We have 10 features, so adding 1 more will only add 10 percent more time.” In fact, that one new feature will have to be coordinated with all 10 of your existing features. So, if it takes 10 hours of coding time to implement the feature itself, it may well take another 10 hours of coding time to make the 10 existing features all interact properly with the new feature. The more features there are, the higher the cost of adding a feature gets. You can minimize this problem by having an excellent software design, but there will still always be some slight extra cost for every new feature.
出去增加features之外,作者还列举了下面这些行为会增加complexity. 其中Misunderstanding这点我觉得值得注意:我遇到不少程序员对某个功能/系统认识不足,导致非常乐观地估计开发时间和难度,而当开发接近尾声才发现许多点和之前设想不同的时候,那么就会许多patch代码出来,而这些patch代码其实是unexpected的。
There are other ways to add complexity than just adding features, too. The most com- mon other ways are:
Expanding the purpose of the software
Generally, just don’t ever do this. Your marketing department might be drooling over the idea of making a single piece of software that does your taxes and cooks dinner, but you should be screaming as loud as you can whenever any suggestion like that comes near your desk. Stick to the existing purpose of your software—it just has to do what it does well, and you will succeed (as long as your software helps people with something they actually need and want help with).
Adding programmers
Yes, that’s right—adding more people to the team does not make things simpler; instead, it adds complexity. There’s a famous book called The Mythical Man Month by Fred Brooks, that points this out. If you have 10 programmers, adding an eleventh means spending time to groove in that one programmer, plus time to groove in the existing 10 programmers to the new person, plus the time spent by the new person interacting with the existing 10 programmers, and so on and so on. You are more likely to be successful with a small group of expert programmers than a large group of inexpert programmers.
Changing things that don’t need to be changed
Any time you change something, you’re adding complexity. Whether it’s a re- quirement, a design, or just a piece of code, you’re introducing the possibility of bugs, as well as the time required to decide upon the change, the time required to implement the change, the time required to validate that the new change works with all the other pieces of the software, the time required to track the change, and the time required to test the change. Each change builds on the last in terms of all this complexity, so the more you change, the more time each new change is going to take. It’s still important to make certain changes, but you should be making informed decisions about them, not just making changes on a whim.
Being locked into bad technologies
Basically, this is where you decide to use some technology, and then are stuck with it for a long time because you’re so dependent on it. A technology in this sense is “bad” if it locks you in (doesn’t allow you to switch easily to some other technology in the future), isn’t going to be flexible enough for your future needs, or just doesn’t have the level of quality you need in order to design simple software with it.
Misunderstanding
Programmers who don’t fully understand their work tend to develop complex sys- tems. It can become a vicious cycle: misunderstanding leads to complexity, which leads to further misunderstanding, and so on. One of the best ways to improve your design skills is to be sure that you fully understand the systems and tools you are working with. The better you understand these, and the more you know about software in general, the simpler your designs can be.
Poor design or no design
Basically, this just means “a failure to plan for change.” Things are going to change, and design work is required to maintain simplicity while the project grows. You have to design well at the start and keep on designing well as the system expands —otherwise, you can introduce massive complexity very fast, because with a poor design, each new feature multiplies the complexity of the code instead of just adding a little bit to it.
Reinventing the wheel
If, for example, you invent your own protocol when a perfectly good one exists, you’re going to be spending a lot of time working on the protocol, when you could just be working on your software. You should almost never have any huge invented- in-house dependency, like a web server, a protocol, or a major library, unless that is your product. The only times it’s okay to reinvent the wheel are when any of the following are true:
- You need something that doesn’t exist yet.
- All of the existing “wheels” are bad technologies that will lock you in.
- The existing “wheels” are fundamentally incapable of handling your needs.
- The existing “wheels” aren’t being properly maintained and you can’t take over maintenance of them (because, for example, you don’t have the source code).
判断是否值得使用某个技术:社区本身是否活跃?是否由某个vendor在push这个技术?(是否会产生vendor lock-in问题)
A technology’s survival potential is the likelihood that it will continue to be maintained. If you get stuck with a library or some dependency that becomes obsolete and un- maintained, you’re really in for some trouble.
You can get some idea of the survival potential of a piece of software by looking at its recent release history. Have the developers been frequently coming out with new ver- sions that solve real user problems? Also, how responsive are the developers to bug reports? Do they have a mailing list or a support team that’s very active? Are there lots of people online talking about this technology? If a technology has a lot of momentum now, you can be fairly sure that it’s not going to die any time soon.
Also look at whether just one vendor is pushing the technology, or if it’s broadly ac- cepted and used across many areas of software by many different developers. If there is only one vendor who pushes and forwards the system, there’s a risk that that vendor will either go out of business or just decide to stop maintaining the system.
如果要重写某个软件,必须满足下面所有条件,要求是非常苛刻的。所以也可以看到,其实大多数情况,重写某个软件并不是明智的:
- 有明确的证据证明重写是值得的。
- 有足够的时间来编写新系统。
- 新系统本身的design必须好过之前的。
- 新系统同时也必须不断地得到反馈(灰度上线)
- 足够的资源(人力时间)来维护两套系统。
Now, with all that said, there are situations in which rewriting is acceptable. However, they are very rare. You should only rewrite if all of the following are true:
- You have developed an accurate estimate that shows that rewriting the system will be a more efficient use of time than redesigning the existing system. Don’t just guess—do actual experiments with redesigning the existing system to see how it goes. It can be very hard to confront the existing complexity and resolve some piece of it, but you must actually attempt this a few times before you can know how much effort fixing all of it will require.
- You have a tremendous amount of time to spend on creating a new system.
- You are somehow a better designer than the original designer of the system or, if you are the original designer, your design skills have improved drastically since you designed the original system.
- You fully intend to design this new system in a series of simple steps and have users who can give you feedback for each step along the way.
- You have the resources available to both maintain the existing system and design a new system at the same time. Never stop maintaining a system that is currently in use so that the programmers can rewrite it. Systems must always be maintained if they are in use. And remember that your personal attention is also a resource”that must be taken into account here—do you have enough time available in each day to be a designer on both the new system and the old system simultaneously, if you are going to work on both?