1
0
wiki/Personal/Book/计算机/程序员的README.html

661 lines
501 KiB
HTML
Raw Normal View History

2024-09-03 21:23:28 +08:00
<!DOCTYPE html>
<html><head><title>程序员的README</title><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><meta property="og:title" content="程序员的README"/><meta property="og:description" content="对于刚刚成为软件工程师的新手来说知道如何编写代码只是成功了一半。你可能很快就会发现学校并没有教授在现实世界中至关重要的技能和工作中必要的流程。本书恰恰填补了这一环节它是作者十多年来在大型公司指导初级工程师工作的教程涵盖软件工程的基础知识和best实践。 本书第12 章讲解当你在公司开启你的职业生涯时会发生什么第311 章会扩展你的工作技能教你如何使用现有代码库、解决和防止技术债、编写生产级软件、管理依赖关系、有效地测试、评审代码、交付软件、处理On-Call 时的事故和构建可演进的架构等;剩余章节涵盖管理能力和职业阶梯的提升等相关内容,例如敏捷计划、与管理者合作以及成长为资深工程师的必经之路。本书中非常重要的一部分内容是教你如何应对糟糕的管理,以及如何调整自己的节奏。 本书内容不仅浅显易懂,还覆盖整个软件开发周期,是一本技术主管希望每名新入行的工程师在开始工作之前都能阅读的书。."/><meta property="og:image" content="https://wiki.7wate.com/static/og-image.png"/><meta property="og:width" content="1200"/><meta property="og:height" content="675"/><link rel="icon" href="../../../static/icon.png"/><meta name="description" content="对于刚刚成为软件工程师的新手来说知道如何编写代码只是成功了一半。你可能很快就会发现学校并没有教授在现实世界中至关重要的技能和工作中必要的流程。本书恰恰填补了这一环节它是作者十多年来在大型公司指导初级工程师工作的教程涵盖软件工程的基础知识和best实践。 本书第12 章讲解当你在公司开启你的职业生涯时会发生什么第311 章会扩展你的工作技能教你如何使用现有代码库、解决和防止技术债、编写生产级软件、管理依赖关系、有效地测试、评审代码、交付软件、处理On-Call 时的事故和构建可演进的架构等;剩余章节涵盖管理能力和职业阶梯的提升等相关内容,例如敏捷计划、与管理者合作以及成长为资深工程师的必经之路。本书中非常重要的一部分内容是教你如何应对糟糕的管理,以及如何调整自己的节奏。 本书内容不仅浅显易懂,还覆盖整个软件开发周期,是一本技术主管希望每名新入行的工程师在开始工作之前都能阅读的书。."/><meta name="generator" content="Quartz"/><link rel="preconnect" href="https://fonts.googleapis.com"/><link rel="preconnect" href="https://fonts.gstatic.com"/><script async src="https://umami.7wate.com/script.js" data-website-id="c061efdc-95dd-4d21-9d04-a1ffda0a85b9"></script><script>
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?94d8ccb156eb7c65abf317e6e01cdba9";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script><script async src="https://www.googletagmanager.com/gtag/js?id=G-MHMEL0F832"></script><script>
(function() {
window.dataLayer = window.dataLayer || [];
function gtag() {
window.dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-MHMEL0F832');
})();
</script><link href="../../../index.css" rel="stylesheet" type="text/css" spa-preserve/><link href="https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css" rel="stylesheet" type="text/css" spa-preserve/><link href="https://fonts.googleapis.com/css2?family=IBM Plex Mono&amp;family=Schibsted Grotesk:wght@400;700&amp;family=Source Sans Pro:ital,wght@0,400;0,600;1,400;1,600&amp;display=swap" rel="stylesheet" type="text/css" spa-preserve/><script src="../../../prescript.js" type="application/javascript" spa-preserve></script><script type="application/javascript" spa-preserve>const fetchData = fetch(`../../../static/contentIndex.json`).then(data => data.json())</script></head><body data-slug="Personal/Book/计算机/程序员的README"><div id="quartz-root" class="page"><div id="quartz-body"><div class="left sidebar"><h1 class="page-title "><a href="../../..">📚 X·Eden</a></h1><div class="spacer mobile-only"></div><div class="search "><div id="search-icon"><p>Search</p><div></div><svg tabIndex="0" aria-labelledby="title desc" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7"><title id="title">Search</title><desc id="desc">Search</desc><g class="search-path" fill="none"><path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4"></path><circle cx="8" cy="8" r="7"></circle></g></svg></div><div id="search-container"><div id="search-space"><input autocomplete="off" id="search-bar" name="search" type="text" aria-label="Search for something" placeholder="Search for something"/><div id="results-container"></div></div></div></div><div class="darkmode "><input class="toggle" id="darkmode-toggle" type="checkbox" tabIndex="-1"/><label id="toggle-label-light" for="darkmode-toggle" tabIndex="-1"><svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" version="1.1" id="dayIcon" x="0px" y="0px" viewBox="0 0 35 35" style="enable-background:new 0 0 35 35;" xmlSpace="preserve"><title>Light mode</title><path d="M6,17.5C6,16.672,5.328,16,4.5,16h-3C0.672,16,0,16.672,0,17.5 S0.672,19,1.5,19h3C5.328,19,6,18.328,6,17.5z M7.5,26c-0.414,0-0.789,0.168-1.061,0.439l-2,2C4.168,28.711,4,29.086,4,29.5 C4,30.328,4.671,31,5.5,31c0.414,0,0.789-0.168,1.06-0.44l2-2C8.832,28.289,9,27.914,9,27.5C9,26.672,8.329,26,7.5,26z M17.5,6 C18.329,6,19,5.328,19,4.5v-3C19,0.672,18.329,0,17.5,0S16,0.672,16,1.5v3C16,5.328,16.671,6,17.5,6z M27.5,9 c0.414,0,0.789-0.168,1.06-0.439l2-2C30.832,6.289,31,5.914,31,5.5C31,4.672,30.329,4,29.5,4c-0.414,0-0.789,0.168-1.061,0.44 l-2,2C26.168,6.711,26,7.086,26,7.5C26,8.328,26.671,9,27.5,9z M6.439,8.561C6.711,8.832,7.086,9,7.5,9C8.328,9,9,8.328,9,7.5 c0-0.414-0.168-0.789-0.439-1.061l-2-2C6.289,4.168,5.914,4,5.5,4C4.672,4,4,4.672,4,5.5c0,0.414,0.168,0.789,0.439,1.06 L6.439,8.561z M33.5,16h-3c-0.828,0-1.5,0.672-1.5,1.5s0.672,1.5,1.5,1.5h3c0.828,0,1.5-0.672,1.5-1.5S34.328,16,33.5,16z M28.561,26.439C28.289,26.168,27.914,26,27.5,26c-0.828,0-1.5,0.672-1.5,1.5c0,0.414,0.168,0.789,0.439,1.06l2,2 C28.711,30.832,29.086,31,29.5,31c0.828,0,1.5-0.672,1.5-1.5c0-0.414-0.168-0.789-0.439-1.061L28.561,26.439z M17.5,29 c-0.829,0-1.5,0.672-1.5,1.5v3c0,0.828,0.671,1.5,1.5,1.5s1.5-0.672,1.5-1.5v-3C19,29.672,18.329,29,17.5,29z M17.5,7 C11.71,7,7,11.71,7,17.5S11.71,28,17.5,28S28,23.29,28,17.5S23.29,7,17.5,7z M17.5,25c-4.136,0-7.5-3.364-7.5-7.5 c0-4.136,3.364-7.5,7.5-7.5c4.136,0,7.5,3.364,7.5,7.5C25,21.636,21.636,25,17.5,25z"></path></svg></label><label id="toggle-label-dark" for="darkmode-toggle" tabIndex="-1"><svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" version="1.1" id="nightIcon" x="0px" y="0px" viewBox="0 0 100 100" style="enable-background='new 0 0 100 100'" xmlSpace="preserve"><title>Dark mode</title><path d="M96.76,66.458c-0.853-0.852-2.15-1.064-3.23-0.534c-6.063,2.991-12.858,4.571-19.655,4.571 C62.022,70.495,50.88,65.88,42.5,57.5C29.043,44.043,25.658,23.536,34.076,6.47c0.532-1.08,0.318-2.379-0.534-3.23 c-0.851-0.852-2.15-1.064-3.23-0.534c-4.918,2.427-9.375,5.619-13.246,9.491c-9.447,9.447-14.65,22.008-14.65,35.369 c0,13.36,5.20
<ul>
<li><strong>书名</strong>《程序员的README》</li>
<li><strong>作者</strong> 克里斯·里科米尼 德米特里·里亚博伊</li>
<li><strong>分类</strong> 计算机-计算机综合</li>
<li><strong>ISBN</strong>9787115599438</li>
<li><strong>出版社</strong>:人民邮电出版社有限公司</li>
</ul>
<h2 id="概述">概述<a aria-hidden="true" tabindex="-1" href="#概述" class="internal"> §</a></h2>
<p>对于刚刚成为软件工程师的新手来说知道如何编写代码只是成功了一半。你可能很快就会发现学校并没有教授在现实世界中至关重要的技能和工作中必要的流程。本书恰恰填补了这一环节它是作者十多年来在大型公司指导初级工程师工作的教程涵盖软件工程的基础知识和best实践。 本书第12 章讲解当你在公司开启你的职业生涯时会发生什么第311 章会扩展你的工作技能教你如何使用现有代码库、解决和防止技术债、编写生产级软件、管理依赖关系、有效地测试、评审代码、交付软件、处理On-Call 时的事故和构建可演进的架构等;剩余章节涵盖管理能力和职业阶梯的提升等相关内容,例如敏捷计划、与管理者合作以及成长为资深工程师的必经之路。本书中非常重要的一部分内容是教你如何应对糟糕的管理,以及如何调整自己的节奏。 本书内容不仅浅显易懂,还覆盖整个软件开发周期,是一本技术主管希望每名新入行的工程师在开始工作之前都能阅读的书。</p>
<h2 id="划线">划线<a aria-hidden="true" tabindex="-1" href="#划线" class="internal"> §</a></h2>
<blockquote>
<p>本书中非常重要的一部分内容是教你如何应对糟糕的管理,以及如何调整自己的节奏。</p>
</blockquote>
<blockquote>
<p>还覆盖整个软件开发周期,是一本技术主管希望每名新入行的工程师在开始工作之前都能阅读的书。</p>
</blockquote>
<blockquote>
<p>一名成熟的软件开发者的标志是打破固有的习惯,批判性地回顾旧代码,发现瑕疵,做到自省,且为没多做些什么而感到羞愧。</p>
</blockquote>
<blockquote>
<p>在本书的前几章中,“提问”就被高度重视,这让我感到非常高兴。向同事提问和学习是快速成长和习得新技能的有效方式。为你的工作成果感到自豪通常是件好事,但当自己在持续改进和交付中比以前做得更好时,你应该优先感到自豪。</p>
</blockquote>
<blockquote>
<p>软件开发本身带来的欣喜是一种“尤里卡时刻”的幸福体验尤其是在无数个面对调试器“狂按”F10键的夜晚自己提交的特性成功上线时的喜悦抑或是眼看着自己设计的技术架构方案从纸面上的框图到代码实现再到最终交付的满足感。</p>
</blockquote>
<blockquote>
<p>在实际的工作中,成功地交付软件并不是只有唯一的路径可走。如何衡量与之匹配的需求、成本和风险才是考验团队的地方。</p>
</blockquote>
<blockquote>
<p>翻译完本书的最后一章时正好是我踏入软件行业的12年整此时的我却觉得自己的职业生涯好像才刚刚开始。</p>
</blockquote>
<blockquote>
<p>知道如何编写代码——也就是如何使用计算机去解决问题,仅仅是“战斗的一半”。</p>
</blockquote>
<blockquote>
<p>然而要成为一名高效的软件工程师,你还需要那些学校里没有教授过的技能。而本书将会教你这些技能。</p>
</blockquote>
<blockquote>
<p>撰写设计文档、如何维护旧代码、如何On-Call待命、如何规划你的工作以及如何与你的管理者和团队互动</p>
</blockquote>
<blockquote>
<p>当你能够安全地交付代码并与你的团队无缝协作时,你就会到达这个里程碑。</p>
</blockquote>
<blockquote>
<p>你知道如何使用集成开发环境(IDE)、构建系统、调试代码和测试框架。你熟悉持续集成、系统指标和监控、配置和打包系统。你积极主动地创建和改进测试代码。在做架构决策时,你会考虑到长期运维。</p>
</blockquote>
<blockquote>
<p>在模棱两可的情况下,你会主动寻求帮助并得到明确的结果。你能以建设性的方式提出问题和定义课题。</p>
</blockquote>
<blockquote>
<p>你会文档化你的工作。</p>
</blockquote>
<blockquote>
<p>重点并不是要写一份完美的文档,而是要写得足够多,以引发讨论,充实细节。这是坎宁安定律的一个应用,该定律认为:“在互联网上获得正确答案的最好方法并不是提出问题,而是发布错误的答案。”</p>
</blockquote>
<blockquote>
<p>了解如何编译、测试和部署代码。阅读那些提交代码的请求和代码评审意见</p>
</blockquote>
<blockquote>
<p>不要满足于你的第一版设计。反复斟酌,要随时做好准备,因为你的系统会随着时间的推移而不断变化。</p>
</blockquote>
<blockquote>
<p>你要开始学习在必要的维护和重构中间寻找平衡。不要试图重构一切。</p>
</blockquote>
<blockquote>
<p>能力的4个阶段“无意识的无能力”(unconscious incompetence)、“有意识的无能力”(conscious incompetence),“有意识的有能力”(conscious competence)和“无意识的有能力”(unconscious competence)。</p>
</blockquote>
<blockquote>
<p>我们还将准备一些提示,方便你在“万事都求人”和“独行侠”之间取得平衡</p>
</blockquote>
<blockquote>
<p>冒充者综合征和邓宁-克鲁格效应,这可能会导致新工程师感到自信不足或自信爆棚,</p>
</blockquote>
<blockquote>
<p>无论你是一名刚毕业的学生还是一名经验丰富的老手,如果你不学习,你就会落后。</p>
</blockquote>
<blockquote>
<p>因为你总会希望尽快将软件上线,而花时间阅读文档和摆弄工具却会让你慢下来。别担心,大家都预料到你会需要些时间来成长。前置学习是一项有价值的投资,许多公司会专门为新员工设计学习课程</p>
</blockquote>
<blockquote>
<p>错误难免会发生。每名工程师都有类似的故事。尽你所能,努力理解你在做什么,但要知道这种事情总会发生</p>
</blockquote>
<blockquote>
<p>错误是不可避免的。成为一名软件工程师的路途艰辛,我们有时会失败。这几乎是所有人都知道的事情。降低系统风险并使这些错误不那么致命是你的管理者和团队的工作。如果你失败了,也不要被击垮:写下经验教训,然后继续前行。</p>
</blockquote>
<blockquote>
<p>文档可能会过期,同事们也会忘记某些事情,但是实例代码是安全的,</p>
</blockquote>
<blockquote>
<p>请每周都花一部分时间去阅读。可供阅读的内容有很多:团队文档、设计文档、代码、积压的任务票、书籍、论文和技术网站。不要试图一下子把所有东西都读完。请从团队文档和设计文档入手。这些文档会就事情是如何组合在一起的给你一个整体的概念。要特别注意那些关于如何权衡取舍和背景的讨论。接下来你就可以深入研究那几个与你最初任务相关的子系统了</p>
</blockquote>
<blockquote>
<p>仔细研究代码的数据结构和算法</p>
</blockquote>
<blockquote>
<p>留意那些惯用写法和风格,也就是去学习“本地方言”(local dialect)</p>
</blockquote>
<blockquote>
<p>尝试自己寻找答案。即使你的同事知道答案,你也要付出努力,这样你会学到更多。如果你没有找到答案,当你寻求帮助时,你的调查仍然会成为你的起点</p>
</blockquote>
<blockquote>
<p>限制你研究一个问题时预期花费的时间。在你开始研究之前就应该设定好时间限制,这样可以鼓励你遵守这个限制,防止收益递减(研究最终会拖累生产性)。</p>
</blockquote>
<blockquote>
<p>如果你在第二个时限之后仍然找不到确定的答案,就应该及时止损并寻求帮助。及时止损需要自律和练习,因为你要对自己负责。</p>
</blockquote>
<blockquote>
<p>在提出问题时描述你已经知道的情况。不要只是分享你的原始笔记。简要地描述你所做的尝试和发现,这表明你已经花了很多时间去试图自己解决这个问题。这样做也会给别人一个回答你的起点。</p>
</blockquote>
<blockquote>
<p>在你设置的会议议程中应包括这个清单,不要只靠脑袋来记问题,也不要事前不做功课就来参加。</p>
</blockquote>
<blockquote>
<p>一般有两个常见的障碍会影响许多工程师,即“冒充者综合征”和邓宁-克鲁格效应。如果你了解这些现象是什么,以及如何克服它们,你会成长得更快。</p>
</blockquote>
<blockquote>
<p>他们总是到处批判公司的技术栈,抱怨代码的质量,贬低设计。他们确信自己的想法是正确的。他们的默认模式是直接回绝或无视反馈</p>
</blockquote>
<blockquote>
<p>当你浏览代码时,你就会注意到它的缺点。混乱的代码是变化的自然副作用,不要把代码的不整洁归咎于开发者。这种走向无序的趋势被称为软件的熵(software entropy)。</p>
</blockquote>
<blockquote>
<p>幸运的是,软件的熵可以被管理</p>
</blockquote>
<blockquote>
<p>本金是那些需要修复的原始不足。利息是随着代码的发展没有解决的潜在不足因为实施了越来越复杂的变通方法。随着变通办法的复制和巩固利息就会增加。复杂性蔓延开来就会造成bug。未支付的技术债很常见遗留代码里有很多这样的债务。</p>
</blockquote>
<blockquote>
<p>这种类型的债务更像是在出问题的领域反思学习或作为软件架构师成长的必经之路,而不是未做功课这么简单。健康的团队使用诸如项目回顾等做法来发现无心之债,并讨论何时以及是否偿还。</p>
</blockquote>
<blockquote>
<p>要边做边解决,着手去做小幅的重构。在小幅的、独立的提交(commit)和拉动请求(pull request)中推动问题的修改。</p>
</blockquote>
<blockquote>
<p>下面是讨论技术债的一个优秀的模板1.按事实陈述情况2.描述技术债的风险和成本3.提出解决方案4.讨论备选方案不采取行动也是备选方案5.权衡利弊。</p>
</blockquote>
<blockquote>
<p>3.3 变更代码</p>
</blockquote>
<p>变更代码和在新代码库中写代码完全不一样,你必须在不破坏现有行为的情况下进行这些修改。你必须理解其他开发者的想法,坚持原有的代码风格和设计模式。而且,你必须在工作中温和地改进代码库。</p>
<p>无论是添加新特性、重构、删除代码还是修复bug变更代码的技术大体上一致。事实上不同类型的变更经常被结合起来使用。所谓重构是指在不改变软件行为的情况下改进内部代码结构。它经常发生在添加新特性的时候因为它使新特性可以更容易地被添加。而在修复bug的过程中则经常删除代码。</p>
<p>改变现有的大型代码库是一项需要经过多年甚至几十年锤炼的专业技能。下面的小技巧在你开始的时候就会帮助到你。</p>
<p>3.3.1 善于利用现有代码</p>
<blockquote>
<p>改变现有的大型代码库是一项需要经过多年甚至几十年锤炼的专业技能</p>
</blockquote>
<blockquote>
<p>互联网上的编程传说经常引用童子军的原则:“住过的营地要比住之前更干净”。</p>
</blockquote>
<blockquote>
<p>当你修复错误或增加新的特性时,只清理有关联性的代码。不要不顾一切地去找“脏”代码,要“随缘”一些。</p>
</blockquote>
<blockquote>
<p>。在你的工作中,要随时定位有异味的代码。代码异味(code smell)是一个术语专指那些不一定是bug但采用了已知会导致问题的代码模式通常“闻起来很怪”</p>
</blockquote>
<blockquote>
<p>。利用一切你所拥有的工具。如果你的编程语言有一个优秀的IDE就去使用它。</p>
</blockquote>
<blockquote>
<p>重置你的分支,压缩你的提交,并在提交代码修改供评审之前写一份清晰的提交信息。</p>
</blockquote>
<blockquote>
<p>品在做某件事情时至少要比目前流行的方式好十倍。两倍或三倍的改进不足以让人们快速或大量地转向新事物。</p>
</blockquote>
<blockquote>
<p>所有的技术都会发生故障但旧的东西以可预测的方式发生故障新东西往往会以令人惊讶的方式发生故障。缺乏成熟度意味着更小的社区、更低的稳定性、更少的文档以及更差的兼容性。新技术甚至在Stack Overflow[插图]上有更少的答案。</p>
</blockquote>
<blockquote>
<p>有时新技术会解决你公司的问题,有时则不会。要辨别何时使用新技术,需要明确的规则和经验。新技术的收益必须超过其成本。</p>
</blockquote>
<blockquote>
<p>不要以为重构工作会很轻松,这将是一个艰难的过程</p>
</blockquote>
<blockquote>
<p>编写拥有良好防御性的代码是一种对那些运行你的代码的人(包括你自己!)富有同情心的表现</p>
</blockquote>
<blockquote>
<p>现在都对类型提示和静态类型检查器有越来越强大的支持</p>
</blockquote>
<blockquote>
<p>代码清单4-1所示的Python 3.5的方法就使用了类型提示来接收和返回一个字符串。</p>
</blockquote>
<blockquote>
<p>如果一个参数应该大于0那就要确保它大于0如果一个参数是IP地址那就要检查它是否是一个有效的IP地址。</p>
</blockquote>
<blockquote>
<p>计算机硬件并不总值得信赖,网络和磁盘可能会损坏数据。如果你需要强大的耐久性保证,使用校验和的方式来检查数据没有意外的变化。</p>
</blockquote>
<blockquote>
<p>也不要忽视安全问题外部输入是危险的。恶意用户可能试图在输入中注入代码或SQL或撑爆缓冲区以获得对你的应用程序的控制权限。使用成熟的类库和框架来防止跨站脚本攻击总是强制转义输入的字符来防止SQL注入攻击。</p>
</blockquote>
<blockquote>
<p>如果一个内置的异常可以描述问题,就不要创建自定义的异常。开发人员有经验去处理现有的异常类型,他们会知道这些异常具体是什么意思。</p>
</blockquote>
<blockquote>
<p>当你创建自己的异常时,不要把它们弄得太通用。通用的异常很难处理,因为开发人员并不知道他们正面临什么样的具体问题。如果开发人员没有得到已发生错误的精确信息,他们就会迫使整个应用程序以失败结束,这将是一个重大的动作。对于你引发的异常类型的描述要尽可能具体,这样开发人员就能对程序失败做出适当的反应</p>
</blockquote>
<blockquote>
<p>跟踪这类错误是令人“抓狂”的——你修复了一个bug却发现真正的问题出在上游。</p>
</blockquote>
<blockquote>
<p>谨慎的做法是使用一种叫作“退避”(backoff)的策略。退避会非线性地增加休眠时间(通常使用指数退避,如(retry number)^2</p>
</blockquote>
<blockquote>
<p>如果一个网络服务器发生了一起突发事件,而且所有的客户端也都同时经历了这起突发事件,那么就用同样的方法进行退避。</p>
</blockquote>
<blockquote>
<p>处理重试的最好方法是构建幂等系统。一个幂等的操作是可以被进行多次并且仍然产生相同结果的操作。</p>
</blockquote>
<blockquote>
<p>通过允许客户端单独为每个请求提供一个唯一ID的方式远程API就可以变为幂等API。当客户端重试时它提供的唯一ID与失败时的相同。如果该请求已经被处理过了服务器可以移除重复的请求。让你的所有操作都成为幂等操作这可大大简化系统的交互同时也可消除一大类潜在的错误。</p>
</blockquote>
<blockquote>
<p>当故障发生后,要确保清理所有的资源,释放你不再需要的内存、数据结构、网络套接字和文件句柄。</p>
</blockquote>
<blockquote>
<p>一个资源已经接近其容量上限就应该是一个WARN。每当你记录一个WARN时应该对应一个你希望看到这个日志的人去采取的具体行动。</p>
</blockquote>
<blockquote>
<p>日志信息不应该包括任何私人数据,如密码、安全令牌、信用卡号码或电子邮件地址。</p>
</blockquote>
<blockquote>
<p>应用程序的系统指标可以被汇总到一个集中式可视化系统中如Datadog、LogicMonitor或Prometheus。</p>
</blockquote>
<blockquote>
<p>分布式调用跟踪。对上游API的一次调用可能会导致对下游的数百次不同服务的RPC调用</p>
</blockquote>
<blockquote>
<p>对人友好的配置文件、环境变量和命令行参数是最常见的方法</p>
</blockquote>
<blockquote>
<p>通常动态配置带来的收益往往比不上它引入的复杂性,你需要仔细考虑运行过程中因为各种配置变化而产生的所有影响。它还会使你更难跟踪哪项配置被改变了,谁改变了它,以及它的值是什么,这些信息在调试运维问题时可能是至关重要的。它还会增加对其他分布式系统的外部依赖性。这听起来很简单,但重新启动一个进程来获取新的配置通常在操作上和架构上都更好一些。</p>
</blockquote>
<blockquote>
<p>不要盲目地从其他文件中复制配置(一个被称为船货崇拜的例子:在没有真正理解它们的作用或原理的情况下就复制东西)。</p>
</blockquote>
<blockquote>
<p>脚本化的工具很容易实现自动化。如果你打算构建一个基于用户界面的工具那就把逻辑抽象成一个共享库或服务这样基于CLI的工具也可以使用。</p>
</blockquote>
<blockquote>
<p>left-pad的软件包消失后成千上万的JavaScript项目开始无法编译。</p>
</blockquote>
<blockquote>
<p>● 唯一性(unique):版本不应该被重复使用。构件会被分发、缓存,并被自动化工作流拉取。永远不要在现有版本下重新发布更改的代码。● 可比性(comparable):版本应该帮助人们和工具对版本的优先顺序进行推断。当一个构建依赖于同一构件的多个版本时,可以使用优先顺序来解决冲突。● 信息性(informative):版本信息区分了预先发布的代码和已发布的代码,将构建流水号与构件相关联,并设置了稳定性和兼容性的合理预期。</p>
</blockquote>
<blockquote>
<p>这是版本管理中最常用的方案之一。官方的SemVer规范可在其网站中找到。该规范定义了3个数字主版本号、次版本号和补丁版本号有时也称作微版本号。这3个数字被合并为“主版本号.次版本号.补丁版本号”的版本号格式。httpclient版本4.3.6意味着主版本号、次版本号和补丁版本</p>
</blockquote>
<blockquote>
<p>比较常见的相依性地狱的罪魁祸首是循环依赖、钻石依赖和版本冲突。</p>
</blockquote>
<blockquote>
<p>在现实中,兼容性是一个“美丽的愿望”。项目经常在没有检查兼容性的情况下就发放版本,即使是自动化也不能完全保证其兼容性。</p>
</blockquote>
<blockquote>
<p>大多数团队会在合并代码的修改之前进行代码评审。高质量的代码评审文化有助于所有具有不同经验水平的工程师的成长,并促进他们对代码库的共同理解。糟糕的代码评审文化会抑制创新,减慢开发速度,并且导致滋生怨恨情绪。</p>
</blockquote>
<blockquote>
<p>评审整个代码库的变更可以确保不止一个人熟悉生产环境中代码的每一行,对代码库的共同理解有助于团队更有凝聚力地扩展代码。</p>
</blockquote>
<blockquote>
<p>不要执着于那些你提交评审的代码,要期待它在评审过程中发生变化,有时甚至是重大的变化</p>
</blockquote>
<blockquote>
<p>一些开发者通过提交代码评审的方式来触发持续集成(continuous integrationCI)系统来绕过这个问题,这是一种糟糕的做法。</p>
</blockquote>
<blockquote>
<p>不要试图让你的队友在预排会议中实际地进行代码评审,参加者应该把他们的评论留到未来真正的代码评审环节。预排会议的目的是帮助你的团队理解为什么要提出修改,并给他们一个良好的心理模型,以便他们可以自行去进行详细的代码评审。</p>
</blockquote>
<blockquote>
<p>从你的代码上得到的那些批评性的评论可能让你很难接受。切记应该保持一些情感上的距离——这些评审意见是针对代码</p>
</blockquote>
<blockquote>
<p>首先审视你自己的反应,你本能地保护你的代码只是因为你编写了它们,还是因为你的方式事实上更好?清楚地解释你的观点,如果你们还是不能达成一致,咨询一下你的管理者下一步该怎么做。团队处理代码评审冲突的方式各不相同,有的服从提交者,有的服从技术负责人,还有的服从小组的法定人数。应该遵循团队惯例。</p>
</blockquote>
<blockquote>
<p>在你的日历上划出代码评审时间。预定的评审时间会使你很容易继续你的其他任务,因为你知道你以后会有集中的时间段进行代码评审。这也会使你的评审保持高质量——当你有专门的时间时,你就不会对需要切换回其他任务而感到有那么大的压力。</p>
</blockquote>
<blockquote>
<p>你需要对代码修改的正确性、可实施性、可维护性、可读性和安全性提供反馈</p>
</blockquote>
<blockquote>
<p>如果你从阅读代码中学到了一些新的东西,请明确地传达给作者。</p>
</blockquote>
<blockquote>
<p>即使是一项令你讨厌的修改,你也可以对它说些好话——如果没有别的原因,就承认它的意图和努力。</p>
</blockquote>
<blockquote>
<p>代码评审通常在一个专门的UI中处理比如GitHub中的拉取请求界面。不要忘记代码评审本身也只是代码而已。你仍然可以迁出或下载那些拟议的修改并在本地处理它们。</p>
</blockquote>
<blockquote>
<p>尊重正在进行的修改的范围。在你阅读的过程中,你会发现改进相邻代码的方法,并产生一些关于新特性的想法,不要坚持将这些修改作为现有评审的一部分来进行。另开一张任务票来改进代码,把工作留到以后。确定严格的范围将提高速度并保持增量更改。</p>
</blockquote>
<blockquote>
<p>你的团队可能把整个流程——从打包到展开,统称为发布(release)。他们可能把打包一个构件称为发布,而把构件交付下载的过程称为发行(publishing)。直到一个特性在生产环境中被打开时才能称其为被“发布”了,而在这之前的一切行动都是部署(deploy)。在本章中我们将提到软件交付的4</p>
</blockquote>
<blockquote>
<p>构建(build)、发布、部署和展开(rollout)</p>
</blockquote>
<blockquote>
<p>Gitflow使用开发分支、热修复分支和发布分支。</p>
</blockquote>
<blockquote>
<p>你应该尽可能频繁地发布。较长的发布周期给人一种错误的安全感两次发布之间的漫长周期感觉像是有充足的时间来测试变化。在实践中快速的发布会产生更稳定的软件当发现bug时更容易修复。</p>
</blockquote>
<blockquote>
<p>每个周期的变化较少所以每个版本的风险都较小。当一个bug出现在生产环境中时在调试时要回顾的代码变化就会更少。代码在开发人员的头脑中是新鲜的这将使bug更容易和更迅速地被修复。</p>
</blockquote>
<blockquote>
<p>发布经理使用加密密钥对构件进行签名这样用户就可以验证下载的软件包是否来自Apache。</p>
</blockquote>
<blockquote>
<p>Puppet、Salt、Ansible和Terraform等现成的解决方案可以与现有的工具集成并且它们是专门为了自动化部署而设计的。</p>
</blockquote>
<blockquote>
<p>为了避免失败的那部分部署操作,应使部署要么全部完成要么什么都没做(即原子性)。</p>
</blockquote>
<blockquote>
<p>特性开关、熔断器、“摸黑启动”、“金丝雀部署”和“蓝绿部署”。</p>
</blockquote>
<blockquote>
<p>特性开关有时被用于A/B测试这是一种测试用户对新特性反应的技术。如果以具有统计意义的方式对用户进行分组用特性开关进行A/B测试是可行的。</p>
</blockquote>
<blockquote>
<p>金丝雀部署不同的是,流量的切换是原子化的,蓝色和绿色环境尽可能地保持一致</p>
</blockquote>
<blockquote>
<p>亚马逊的构建者库也是一个伟大的免费资源,可用于交付最佳实践。该库位于亚马逊构建者的主页,那上面有关于持续交付、自动部署和回滚的帖子。</p>
</blockquote>
<blockquote>
<p>然而大概每名On-Call人员最终都会遇到一起运维事故生产软件的关键问题。事故是由自动监控系统发出的警报或由支持工程师观察到问题并报告给值班人员的。On-Call的开发人员必须对事故分流、缓解症状和最终解决。</p>
</blockquote>
<p>当关键警报发生时On-Call的开发人员会被呼叫。“呼叫”是手机出现之前的一种过时的称呼。现在警报是通过聊天软件、电子邮件、电话或文本信息等渠道发出的。如果你像我们一样不接听任何来自未知号码的电话请确保将警报服务的电话号码添加到你的联系人白名单。</p>
<p>所有的On-Call轮换的工作都应该以交接开始和结束。上一名On-Call的开发人员总结当前的所有运维事故并为下一名On-Call的开发人员提供任何未解决任务的背景。如果你已经很好地跟踪了你的工作交接工作就不是什么大事了。</p>
<blockquote>
<p>创建一个你在紧急情况下可以依赖的资源清单可以直接链接到你的服务的关键仪表盘和运行手册、访问日志的说明、重要的聊天室以及故障排除指南。创建一个单独的“On-Call”书签文件夹并保持更新这会很方便。与团队分享你的清单以便其他人可以使用和改进它。</p>
</blockquote>
<blockquote>
<p>,开发人员会感到压力和变得暴躁——这是人的本性</p>
</blockquote>
<blockquote>
<p>简洁确保你的沟通容易被阅读和理解。如果你并不知道答案,就说出来;如果你知道答案,就大声说出来。回应请求要迅速。回应不一定代表解决方案。告诉请求者你已经看到了他们的请求,并确保你理解问题所在。</p>
</blockquote>
<blockquote>
<p>后续行动(follow-up):对事故的根本原因——为什么会发生,进行调查。如果事故很严重,就会进行正式的事后调查,或进行回顾性调查。建立后续任务,以防止那个(或那些)根本原因的再次出现。团队要寻找流程、工具或文档中的任何漏洞。在所有的后续任务完成之前,相应事故的处理不应该被认为已经结束了</p>
</blockquote>
<blockquote>
<p>设计工作被分成两种活动:单独的深入思考和协作的小组讨论。研究、头脑风暴和写作构成了深度工作。设计讨论和对设计文件的评论构成了合作的部分。这个过程的有形产出是一份设计文档。</p>
</blockquote>
<blockquote>
<p>当你在撰写文档的过程中,你发现了更多的未知因素。你创建了一些小的原型来验证你的设计、回答问题,并帮助你在可行的替代方案中做出选择。你进行更多的研究,并请专家提供意见。你充实了设计文档的草稿</p>
</blockquote>
<blockquote>
<p>你现在处于圆锥体的顶部。你在你的设计中已经投入了大量的工作,而且对你的方案充满信心。你将设计方案在整个组织内传阅。安全、运维、相关的团队和架构师都需要了解你所承诺的变更,这不仅仅是为了提供反馈,也是为了更新他们对整个系统工作方式的心理模型。</p>
</blockquote>
<blockquote>
<p>询问利益相关者他们认为问题究竟是什么。这些利益相关者可能是你的管理者、队友、产品经理或技术负责人</p>
</blockquote>
<blockquote>
<p>不会在你锁定的整段时间内进行“设计”。你的大脑需要时间来放松:休息一下,给自己一个呼吸的空间;允许你的思想放松和游荡;去散步、泡茶、阅读、写作、画画。</p>
</blockquote>
<blockquote>
<p>并非每一项变更都需要设计文档更不用说正式的设计评审过程了。你的组织可能有自己的指导方针。如果没有指导方针就用这3个标准来决定是否需要设计文档。● 该项目将需要至少一个月的工程时间。● 这一变更将对软件的扩展和维护产生长期的影响。● 该变更将显著影响其他团队。</p>
</blockquote>
<blockquote>
<p>写作作为一项技能,就像其他技能一样,是通过实践来进步的。充分利用写作的机会——设计文档、电子邮件、代码评审意见——努力写得清晰。</p>
</blockquote>
<blockquote>
<p>不要让人惊讶你需要有礼貌地并且渐进地让人们了解你的设计方案。如果正式的设计文档是其他团队和技术负责人第一次了解你的工作,你就是在为自己的失败埋下伏笔。每一方都有不同的视角和不同的利益诉求,他们可能会对一份突然出现的、他们没有发言权的设计文档做出强烈的反应。</p>
</blockquote>
<blockquote>
<p>当一个问题特别多元或有争议时,要选择更大、更有包容性的头脑风暴会议。对于更直接的讨论,应保持较少的被邀请者人数,使谈话更容易进行。</p>
</blockquote>
<blockquote>
<p>会议结束后,以白板上的图片为指导,根据你的回忆写一份总结。把这些笔记发给与会者和其他相关的队友。</p>
</blockquote>
<blockquote>
<p>[插图]</p>
</blockquote>
<blockquote>
<p>《软件设计的哲学》(A Philosophy of Software Design)中写到:“复杂性是与系统结构有关的东西,复杂性使人难以理解和修改系统。”按照奥斯特霍特的说法,复杂系统有两个特点:高依赖性和高隐蔽性</p>
</blockquote>
<blockquote>
<p>面对未来未知的需求,工程师们通常会选择下面两种策略中的一种:试图预判未来的需求,或者建立抽象模型作为逃生舱门,使后续的代码修改更容易。</p>
</blockquote>
<blockquote>
<p>避免过早优化,避免不必要的灵活抽象模型,以及避免最小可行产品(minimum viable productMVP)所不需要的产品特性——你需要那些可以获得用户反馈的最低限度的功能集。</p>
</blockquote>
<blockquote>
<p>按技术领域进行分组的代码在单一业务领域内效果很好,但随着业务的发展,就会变得很混乱</p>
</blockquote>
<blockquote>
<p>个人和互动高于流程和工具工作的软件高于详尽的文档客户合作高于合同谈判响应变化高于遵循计划</p>
</blockquote>
<blockquote>
<p>一旦敏捷开发模型流行开来,“黑带忍者”、权威认证和流程顾问就会笼罩在一些组织之上。人们迷恋于“做敏捷”的“正确”方法,而往往损害了第一个原则:“个人和互动高于流程和工具”。</p>
</blockquote>
<blockquote>
<p>会让每个人都了解你的进展,让你负起责任和保持专</p>
</blockquote>
<blockquote>
<p>队友们围成一圈,介绍自上一次站会以来他们所做的工作,他们计划在未来做什么</p>
</blockquote>
<blockquote>
<p>你对公司的方向有什么疑问?你对组织变革有什么疑问?</p>
</blockquote>
<blockquote>
<p>反馈:我们可以在哪些方面做得更好?你对团队的计划流程有什么看法?你最大的技术难题是什么?你希望你能做什么而你却做不到?你最大的问题是什么?公司的最大问题是什么?你或团队中的其他人遇到了什么阻碍?职业生涯:你的管理者对你都有哪些职业建议?你有哪些可以改进的地方?你希望自己有哪些技能?你的长期目标是什么,你觉得你在这些目标上的进展如何?个人事务:你的生活中有什么新鲜事?你的管理者应该注意你的哪些个人问题?</p>
</blockquote>
<blockquote>
<p>PPP中的每个P进展、计划与问题都有自己的小节。每个小节应该有3到5个要点每个要点应该很简短只有1到3个句子。</p>
</blockquote>
<blockquote>
<p>定义了目标(目的),并为每个目标附上衡量标准(关键结果)</p>
</blockquote>
<blockquote>
<p>在写自我评价时不要凭记忆行事。记忆是不稳定的你可能只关注某些难忘的项目。务必保有一份最新的在整个一年中完成的工作的清单——一份已完成的待办事项清单、一套PPP或一份“子弹日记”以唤起你的记忆。看看你在公司的问题跟踪系统中已完成的任务。你完成了哪些里程碑、史诗和用户故事已合并的代码拉动请求和代码评审也显示了你所做的工作。还有不要忘记你的非工程类的项目辅导实习生、代码评审、参与面试、博客文章、演讲、文档——所有的这些都应该被认可使用你所拥有的一切来写一份诚实的自我评价。</p>
</blockquote>
<blockquote>
<p>可以使用一对一面谈的方式来获得反馈。</p>
</blockquote>
<blockquote>
<p>不要听信表面上的反馈。你的管理者仅仅是视角之一(尽管是一个重要的视角),试着把管理者的反馈纳入你的观点,而不是直接采用管理者的反馈</p>
</blockquote>
<blockquote>
<p>我们今天能谈谈职业生涯的路径吗?老实说,我不确定我能看到自己五年后在哪里,甚至我的选择是什么。你看到的一些常见的职业路径是什么,它们之间有什么区别吗?我很喜欢我目前的项目,但我也对安全领域感到好奇。是否有机会可以让我做一些与安全有关的工作呢?</p>
</blockquote>
<blockquote>
<p>培养T型技能参加工程师训练营主导晋升过程不要过于频繁地更换工作并多自我调节。</p>
</blockquote>
<blockquote>
<p>软件工程有许多专业领域前端、后端、运维、数据仓库和机器学习等。“T型”工程师在大多数领域内都能有效地工作并且至少是某一个领域的专家。</p>
</blockquote>
<blockquote>
<p>你的职业生涯是一场马拉松,而不是短跑冲刺——你有几十年的时间。给自己定下节奏,享受这段旅程吧!</p>
</blockquote>
<blockquote>
<p>与你喜欢的人一起工作并解决你所热衷的问题,你就可以完成伟大的事情</p>
</blockquote>
<h2 id="笔记">笔记<a aria-hidden="true" tabindex="-1" href="#笔记" class="internal"> §</a></h2>
<blockquote>
<p>技术债</p>
</blockquote>
<p>💭 技术债务大多是时空局限性带来的产物,整体来是一个熵增的过程。别妄想永远消除技术债务,只需要平时定期审计,重构,从而维持一个相对平衡的状态即可。</p>
<blockquote>
<p>On-Call</p>
</blockquote>
<p>💭 处理故障首先确定范围,然后定义性质。</p>
<blockquote>
<p>一名成功的软件工程师究竟是什么样子的呢?以及如何成为一名成功的软件工程师呢?</p>
</blockquote>
<p>💭 活在当下,着眼未来</p>
<blockquote>
<p>如果你的公司没有安排新人入职培训,那需要你自己去向你的管理者要一份公司的“组织架构图”,了解清楚谁负责什么,谁向谁汇报,都有哪些不同的部门,以及它们之间的关系。记得做好笔记。</p>
</blockquote>
<p>💭 公司的战略方向,核心业务,市场地位,组织架构都是入职之后需要迅速了解,代入的重要因素!</p>
<blockquote>
<p>这样做的目的是让你去了解那些步骤,而不是去打动谁。</p>
</blockquote>
<p>💭 充分把握生产流程中的每一个环节</p>
<blockquote>
<p>本节将列举各种各样的学习方法。切勿试图同时去做本章中列出的所有事情!因为那样会让你感到倦怠。切记善用个人的时间——虽然说持续进步非常重要,但是把所有清醒的时间都花在工作上是不健康的。</p>
</blockquote>
<p>💭 一定要学会摸鱼学习,这才是自己的!😎</p>
<blockquote>
<p>错误是不可避免的。成为一名软件工程师的路途艰辛,我们有时会失败。这几乎是所有人都知道的事情。降低系统风险并使这些错误不那么致命是你的管理者和团队的工作。如果你失败了,也不要被击垮:写下经验教训,然后继续前行。</p>
</blockquote>
<p>💭 兼容性,容错性,而不是矫枉过正,一棒子打死🔨😥</p>
<blockquote>
<p>,潘卡给出了一些背景,描述了问题,告诉艾丽斯他已经尝试了什么,然后才请求帮助。</p>
</blockquote>
<p>💭 场景,因素,问题,目的,结果</p>
<blockquote>
<p>谨慎的、有意的技术债(右上)是技术债的典型形式:在代码的已知不足和交付速度之间进行务实的取舍。只要团队有规划地解决这个问题,这就是好的债务。</p>
</blockquote>
<p>💭 平衡点</p>
<blockquote>
<p>不要把你的呼吁建立在价值判断上(“这代码又老又难看”),将重点放在技术债的成本和修复它带来的好处上。</p>
</blockquote>
<p>💭 不能光说,也得去做</p>
<blockquote>
<p>在不影响整个项目持续运转的情况下[插图]要持续地重构工程,这样重构的成本就会平摊在多次的版本更迭中。</p>
</blockquote>
<p>💭 隐形成本</p>
<blockquote>
<p>不要因为你不喜欢你公司或行业的标准就忽视它们编写非标准的代码意味着它将无法适应公司的环境。因为持续集成检查、IDE插件、单元测试、代码校验、日志聚合工具、指标仪表盘和数据管道都已经集成在一起了你自定义的方案肯定会代价高昂。</p>
</blockquote>
<p>💭 系统化,标准化的技术设施建设才可以让万丈高楼平地起</p>
<blockquote>
<p>分叉(fork)是对一个代码库进行完整的、独立的复制分叉之后的代码库有自己的主干、分支和标签。在GitHub这样的代码共享平台上在向上游代码库提交拉动请求之前就可以分叉上游代码库。分叉操作可以让那些对主代码库没有写入权限的人仍然可以对项目做出贡献这是一种正常而健康的做法。</p>
</blockquote>
<p>💭 目前分叉可以划分为硬分叉和软分叉,硬分叉则是完全独立出来,上下游无关。软分叉则是为了满足某些非社区主流特性,上下游关联。</p>
<blockquote>
<p>[插图]</p>
</blockquote>
<p>💭 生活不是一天天变好的,日子也不是一天过完的。病来如山倒,病去如抽丝,技术债务根本无法消除,只能渐进式改进,尽可能地维持平衡。</p>
<blockquote>
<p>配置即代码(configuration as codeCAC)的哲学认为,配置应该受到与代码同样严格的要求。配置错误可能是灾难性的,一个错误的整数或缺失的参数就可以毁掉一个应用程序。</p>
</blockquote>
<p>💭 配置同样需要接受版本控制</p>
<blockquote>
<p>优秀的代码评审可以作为一个教学工具,传播认识,记录实现的决策,并提供代码的更改记录以确保安全性与合规性。</p>
</blockquote>
<p>💭 信息对称,深度交流</p>
<blockquote>
<p>代码修改由准备、提交、评审、最后批准和合并这几个环节组成。</p>
</blockquote>
<p>💭 流程化标准化管理建设</p>
<blockquote>
<p>每个人的记忆都会“消失”,你回应得越快,你得到他人回应的速度就越快。</p>
</blockquote>
<p>💭 不要考虑,直接去干</p>
<blockquote>
<p>一个快捷方式或软链接就可以被原子化地翻转。</p>
</blockquote>
<p>💭 Linux 软连接</p>
<blockquote>
<p>摸黑启动的软件其实仍然启用了,代码也被调用了,只是结果被丢掉了。摸黑启动可帮助开发者和运维人员在生产环境中了解他们的软件,对用户的影响最小。</p>
</blockquote>
<p>💭 镜像流量</p>
<blockquote>
<p>随时响应”并不意味着立即放下你正在做的事情来解决最新的问题。对于许多请求完全可以先承认你已经收到了询问并回答你应该在什么时候能看一下这个问题“我现在正在协助其他人我可以在15分钟内给您答复吗”一般来说人们希望On-Call工程师能做出快速反应但不一定需要快速解决问题。</p>
</blockquote>
<p>💭 先承接情绪价值</p>
<blockquote>
<p>有些支持请求异常紧急,而有些请求则在一周内完成就可以。如果你无法判断一个请求的紧急程度,请询问该请求的影响是什么。影响范围将决定优先级。如果你不认可请求者对于某个问题的优先级次序的看法,请与你的管理者讨论一下。</p>
</blockquote>
<p>💭 划分范围,确定性质。</p>
<blockquote>
<p>它影响了多少人有多大的危害性在SLI和触发警报的指标如果适用的话的帮助下使用你公司的优先级分类和SLO/SLA定义来确定问题的优先级。</p>
</blockquote>
<p>💭 划范围,定性质。</p>
<blockquote>
<p>你在应急方案的阶段目标是降低问题的影响。应急方案并不是要彻底地解决这个问题,而是要降低其严重性。修复一个问题可能需要很多时间,而应急方案通常可以很快完成。</p>
</blockquote>
<p>💭 尽可能的减少损失,影响</p>
<blockquote>
<p>工程师们从原来的连接器中移除所有健康的数据流,试图重现这个问题。</p>
</blockquote>
<p>💭 复现错误</p>
<blockquote>
<p>负责连接器的工程经理安排了后续工作On-Call工程师撰写了一份“尸检”文件的草稿。安排一次“尸检”会议。最终通过“尸检”这一过程新开了3张任务票分别调查为什么APM要使用消息头、为什么连接器的消费者节点不能反序列化、为什么手动的消费者节点不能输出空字符串的头。
任何事故都是一件大事,所以需要后续行动来继续跟进。目的是从事故中学习,防止它再次发生。要写一份事后总结的文档,并进行评审,同时开启新任务以防止其再次发生。</p>
</blockquote>
<p>💭 复盘总结</p>
<blockquote>
<p>你的首要任务是定义和理解你要解决的那个(或那些)问题。你需要了解问题的边界,以便知道如何解决它,并避免构建错误的东西。</p>
</blockquote>
<p>💭 理解问题,定义性质,确定边界🤔</p>
<blockquote>
<p>当你写代码的时候,要使用最小惊讶原则和封装原则。这些设计原则将使你的代码易于演进。</p>
</blockquote>
<p>💭 封装,继承,多态三板斧</p>
<blockquote>
<p>保持代码灵活性的最佳方法之一是减少代码的总量。对于你所构建的一切,问问自己哪些是绝对必要的,其余的就舍弃掉。</p>
</blockquote>
<p>💭 断舍离</p>
<blockquote>
<p>解决大局观上的偏差</p>
</blockquote>
<p>💭 不能掉队</p>
<blockquote>
<p>如果你已经给出了反馈意见,并保持了耐心,但事情仍然没有进展,那就起身离开。</p>
</blockquote>
<p>💭 无法形成正反馈,就需要接受无尽的消极。</p>
<blockquote>
<p>一名软件工程师的职业曲线是漫长的,本书将带你走完旅程的开端。接在后面的是终身学习、技术领导力,甚至可能是走上管理岗位或创业。无论你选择何种职业路径,你都必须继续成长。</p>
</blockquote>
<p>💭 持续学习,技术领导(•̀ᴗ•́)و̑̑</p>
<blockquote>
<p>了解晋升的流程,确保你的工作是有价值的和可见的,当你认为自己接近下一个级别时,要大声说出来。</p>
</blockquote>
<p>💭 主动出击</p>
<blockquote>
<p>反过来说,不要待得太久。工作僵化、停滞不前是改变现状的正当理由。在一家公司工作时间长的工程师自然会成为“历史学家”,他们教导工程师事情是如何运转的、谁知道什么,以及为什么事情是按照他们的方式完成的。这样的知识是有价值的,甚至是一名主任工程师职责的一部分,但如果你的价值更多来自过去的工作而不是现在的贡献,那么它就会阻碍你的成长。更换公司并在一个新的环境中找到自己,可以重启你的成长。</p>
</blockquote>
<p>💭 谨防温水煮青蛙</p>
<h2 id="书评">书评<a aria-hidden="true" tabindex="-1" href="#书评" class="internal"> §</a></h2>
<blockquote>
<p>✨其实应该更早一些读到这本书。 实际工作中最好是在职场的第二年开始的时候,首先经过一年的成长已经度过了新手期,同时经过这本书的指导,可以让我们更容易地走好下一阶段的路,从而最低成本试错,减少走弯路,争取到达新高峰!</p>
</blockquote>
<h2 id="点评">点评<a aria-hidden="true" tabindex="-1" href="#点评" class="internal"> §</a></h2></article></div><div class="right sidebar"><div class="graph "><h3>Graph View</h3><div class="graph-outer"><div id="graph-container" data-cfg="{&quot;drag&quot;:true,&quot;zoom&quot;:true,&quot;depth&quot;:1,&quot;scale&quot;:1.1,&quot;repelForce&quot;:0.5,&quot;centerForce&quot;:0.3,&quot;linkDistance&quot;:30,&quot;fontSize&quot;:0.6,&quot;opacityScale&quot;:1,&quot;showTags&quot;:true,&quot;removeTags&quot;:[]}"></div><svg version="1.1" id="global-graph-icon" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 55 55" fill="currentColor" xmlSpace="preserve"><path d="M49,0c-3.309,0-6,2.691-6,6c0,1.035,0.263,2.009,0.726,2.86l-9.829,9.829C32.542,17.634,30.846,17,29,17
s-3.542,0.634-4.898,1.688l-7.669-7.669C16.785,10.424,17,9.74,17,9c0-2.206-1.794-4-4-4S9,6.794,9,9s1.794,4,4,4
c0.74,0,1.424-0.215,2.019-0.567l7.669,7.669C21.634,21.458,21,23.154,21,25s0.634,3.542,1.688,4.897L10.024,42.562
C8.958,41.595,7.549,41,6,41c-3.309,0-6,2.691-6,6s2.691,6,6,6s6-2.691,6-6c0-1.035-0.263-2.009-0.726-2.86l12.829-12.829
c1.106,0.86,2.44,1.436,3.898,1.619v10.16c-2.833,0.478-5,2.942-5,5.91c0,3.309,2.691,6,6,6s6-2.691,6-6c0-2.967-2.167-5.431-5-5.91
v-10.16c1.458-0.183,2.792-0.759,3.898-1.619l7.669,7.669C41.215,39.576,41,40.26,41,41c0,2.206,1.794,4,4,4s4-1.794,4-4
s-1.794-4-4-4c-0.74,0-1.424,0.215-2.019,0.567l-7.669-7.669C36.366,28.542,37,26.846,37,25s-0.634-3.542-1.688-4.897l9.665-9.665
C46.042,11.405,47.451,12,49,12c3.309,0,6-2.691,6-6S52.309,0,49,0z M11,9c0-1.103,0.897-2,2-2s2,0.897,2,2s-0.897,2-2,2
S11,10.103,11,9z M6,51c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S8.206,51,6,51z M33,49c0,2.206-1.794,4-4,4s-4-1.794-4-4
s1.794-4,4-4S33,46.794,33,49z M29,31c-3.309,0-6-2.691-6-6s2.691-6,6-6s6,2.691,6,6S32.309,31,29,31z M47,41c0,1.103-0.897,2-2,2
s-2-0.897-2-2s0.897-2,2-2S47,39.897,47,41z M49,10c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S51.206,10,49,10z"></path></svg></div><div id="global-graph-outer"><div id="global-graph-container" data-cfg="{&quot;drag&quot;:true,&quot;zoom&quot;:true,&quot;depth&quot;:-1,&quot;scale&quot;:0.9,&quot;repelForce&quot;:0.5,&quot;centerForce&quot;:0.3,&quot;linkDistance&quot;:30,&quot;fontSize&quot;:0.6,&quot;opacityScale&quot;:1,&quot;showTags&quot;:true,&quot;removeTags&quot;:[]}"></div></div></div><div class="toc desktop-only"><button type="button" id="toc" class><h3>Table of Contents</h3><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="fold"><polyline points="6 9 12 15 18 9"></polyline></svg></button><div id="toc-content"><ul class="overflow"><li class="depth-0"><a href="#简介" data-for="简介">简介</a></li><li class="depth-0"><a href="#概述" data-for="概述">概述</a></li><li class="depth-0"><a href="#划线" data-for="划线">划线</a></li><li class="depth-0"><a href="#笔记" data-for="笔记">笔记</a></li><li class="depth-0"><a href="#书评" data-for="书评">书评</a></li><li class="depth-0"><a href="#点评" data-for="点评">点评</a></li></ul></div></div><div class="backlinks "><h3>Backlinks</h3><ul class="overflow"><li>No backlinks found</li></ul></div><div class="explorer mobile-only"><button type="button" id="explorer" data-behavior="collapse" data-collapsed="collapsed" data-savestate="true" data-tree="[{&quot;path&quot;:&quot;Journal&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Journal/2024&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Journal/2024/W33&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Obsidian&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Obsidian/Templates&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Blog&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Blog/2018&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Blog/2020&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Blog/2021&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Blog/2022&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Blog/2023&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Blog/2024&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Book&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Book/个人成长&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Book/医学健康&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Book/历史&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Book/哲学宗教&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Book/心理&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Book/政治军事&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Book/教育学习&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Book/文学&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Book/生活百科&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Book/社会文化&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Book/科学技术&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Book/经济理财&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Book/艺术&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Book/计算机&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Journal&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Journal/2022&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Journal/2022/W34&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;Personal/Journal/2022/W35&quot;,&quot;collapsed&qu
function toggleCallout() {
const outerBlock = this.parentElement;
outerBlock.classList.toggle(`is-collapsed`);
const collapsed = outerBlock.classList.contains(`is-collapsed`);
const height = collapsed ? this.scrollHeight : outerBlock.scrollHeight;
outerBlock.style.maxHeight = height + `px`;
let current = outerBlock;
let parent = outerBlock.parentElement;
while (parent) {
if (!parent.classList.contains(`callout`)) {
return;
}
const collapsed2 = parent.classList.contains(`is-collapsed`);
const height2 = collapsed2 ? parent.scrollHeight : parent.scrollHeight + current.scrollHeight;
parent.style.maxHeight = height2 + `px`;
current = parent;
parent = parent.parentElement;
}
}
function setupCallout() {
const collapsible = document.getElementsByClassName(
`callout is-collapsible`
);
for (const div of collapsible) {
const title = div.firstElementChild;
if (title) {
title.removeEventListener(`click`, toggleCallout);
title.addEventListener(`click`, toggleCallout);
const collapsed = div.classList.contains(`is-collapsed`);
const height = collapsed ? title.scrollHeight : div.scrollHeight;
div.style.maxHeight = height + `px`;
}
}
}
document.addEventListener(`nav`, setupCallout);
window.addEventListener(`resize`, setupCallout);
</script><script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs';
const darkMode = document.documentElement.getAttribute('saved-theme') === 'dark'
mermaid.initialize({
startOnLoad: false,
securityLevel: 'loose',
theme: darkMode ? 'dark' : 'default'
});
document.addEventListener('nav', async () => {
await mermaid.run({
querySelector: '.mermaid'
})
});
</script><script src="https://cdn.jsdelivr.net/npm/katex@0.16.7/dist/contrib/copy-tex.min.js" type="application/javascript"></script><script src="../../../postscript.js" type="module"></script></html>