We propose that “software engineering” encompasses not just the act of writing code, but all of the tools and processes an organization uses to build and maintain that code over time.(我们建议,"软件工程 "不仅包括编写代码的行为,还包括一个组织用来长期构建和维护代码的所有工具和流程。)
The book emphasizes three fundamental principles that we feel software organizations should keep in mind when designing, architecting, and writing their code:
Time and Change How code will need to adapt over the length of its life Scale and Growth How an organization will need to adapt as it evolves Trade-offs and Costs How an organization makes decisions, based on the lessons of Time and Change and Scale and Growth(本书强调了三个基本原则,我们认为软件组织在设计、架构和编写代码时应该牢记这些原则: 时间和变化代码如何展期生命周期内进行适配。 规模和增长一个组织如何适应它的发展过程。 权衡和成本一个组织如何根据时间和变化以及规模和增长的经验教训做出决策。)
We see three critical differences between programming and software engineering: time, scale, and the trade-offs at play. On a software engineering project, engineers need to be more concerned with the passage of time and the eventual need for change. In a software engineering organization, we need to be more concerned about scale and efficiency, both for the software we produce as well as for the organization that is producing it. Finally, as software engineers, we are asked to make more complex decisions with higher-stakes outcomes, often based on imprecise estimates of time and growth.(我们看到,编程和软件工程之间有三个关键的区别:时间、规模和权衡取舍。在一个软件工程项目中,工程师需要更多关注时间成本和需求变更。在软件工程中,我们需要更加关注规模和效率,无论是对我们生产的软件,还是对生产软件的组织。最后,作为软件工程师,我们被要求做出更复杂的决策,其结果风险更大,而且往往是基于对时间和规模增长的不确定性的预估。)
We can also say that software engineering is different from programming in terms of the complexity of decisions that need to be made and their stakes. In software engineering, we are regularly forced to evaluate the trade-offs between several paths forward, sometimes with high stakes and often with imperfect value metrics. The job of a software engineer, or a software engineering leader, is to aim for sustainability and management of the scaling costs for the organization, the product, and the development workflow . With those inputs in mind, evaluate your trade-offs and make rational decisions. We might sometimes defer maintenance changes, or even embrace policies that don’t scale well, with the knowledge that we’ll need to revisit those decisions. Those choices should be explicit and clear about the deferred costs.(我们还可以说,软件工程与编程的不同之处在于需要做出的决策的复杂性及其风险。在软件工程中,我们经常被迫在几个路径之间做评估和权衡,有时风险很高,而且价值指标不完善。软件工程师或软件工程负责人的工作目标是实现组织、产品和开发工作流程的可持续性和管理扩展成本为目标。考虑到这些投入,评估你的权衡并做出理性的决定。有时,我们可能会推迟维护更改,甚至接受扩展性不好的策略,因为我们知道需要重新审视这些决策。这些决策应该是明确的和清晰的递延成本。)
So, concretely, how does short-term programming differ from producing code with a much longer expected life span? Over time, we need to be much more aware of the difference between “happens to work” and “is maintainable.” There is no perfect solution for identifying these issues. That is unfortunate, because keeping software maintainable for the long-term is a constant battle.(那么,具体来说,短期编程与生成预期生命周期更长的代码有何不同?随着时间的推移,我们需要更多地意识到“正常工作”和“可维护”之间的区别。识别这些问题没有完美的解决方案。这是不幸的,因为保持软件的长期可维护性是一场持久战。)
If you are maintaining a project that is used by other engineers, the most important lesson about “it works” versus “it is maintainable” is what we’ve come to call Hyrum’s Law: With a sufficient number of users of an API, it does not matter what you promise in the contract: all observable behaviors of your system will be depended on by somebody.(如果你正在维护一个由其他工程师使用的项目,那么关于“有效”与“可维护”最重要的一课就是我们所说的海勒姆定律:
当一个 API 有足够多的用户的时候,在约定中你承诺的什么都无所谓,所有在你系统里面被观察到的行为都会被一些用户直接依赖。)
Everything your organization relies upon to produce and maintain code should be scalable in terms of overall cost and resource consumption. In particular, everything your organization must do repeatedly should be scalable in terms of human effort. Many common policies don’t seem to be scalable in this sense.(你的组织生产和维护代码所依赖的一切都应该在总体成本和资源消耗方面具有可扩展性。特别是,你的组织必须重复做的每件事都应该在人力方面具有可扩展性。从这个意义上讲,许多通用策略似乎不具有可扩展性。)
This type of approach might work in a small software setting but quickly fails as both the depth and breadth of the dependency graph increases. Teams depend on an ever- increasing number of Widgets, and a single build break can affect a growing percentage of the company. Solving these problems in a scalable way means changing the way we do deprecation: instead of pushing migration work to customers, teams can internalize it themselves, with all the economies of scale that provides.(这种方法可能适用于小型软件项目,但随着依赖关系图的深度和广度的增加,很快就会失败。团队依赖越来越多的小部件,单个构建中断可能会影响公司不断增长的百分比。以一种可扩展的方式解决这些问题,意味着需要改变我们废弃的方式: 不是将迁移工作推给客户,团队可以将其内部消化,并提供所需资源投入。)
One of the broad truths we’ve seen to be true is the idea that finding problems earlier in the developer workflow usually reduces costs. Consider a timeline of the developer workflow for a feature that progresses from left to right, starting from conception and design, progressing through implementation, review, testing, commit, canary, and eventual production deployment. Shifting problem detection to the “left” earlier on this timeline makes it cheaper to fix than waiting longer, as shown in Figure 1-2.(我们看到的一个普遍真理是,在开发人员的工作流程中发现的问题,通常可以降低成本。考虑开发人员工作流程的时间表,从左到右,从概念和设计开始,通过实施、评审、测试、提交、金丝雀和最终的生产部署来进行。在此时间线之前,将问题发现转移到“左侧”会使修问题解决成本更低,如图1-2所示。)
What do we mean by cost? We are not only talking about dollars here. “Cost” roughly translates to effort and can involve any or all of these factors:
- Financial costs (e.g., money)
- Resource costs (e.g., CPU time)
- Personnel costs (e.g., engineering effort)
- Transaction costs (e.g., what does it cost to take action?)
- Opportunity costs (e.g., what does it cost to not take action?)
- Societal costs (e.g., what impact will this choice have on society at large?)
我们所说的成本是什么呢?我们这里不仅仅是指金钱。“成本”大致可以转化为努力的方向,可以包括以下任何或所有因素:
- 财务成本(如金钱)
- 资源成本(如CPU时间)
- 人员成本(例如,工作量)
- 交易成本(例如,采取行动的成本是多少?)
- 机会成本(例如,不采取行动的成本是多少?)
- 社会成本(例如,这个选择将对整个社会产生什么影响?)