近期变更

以下是网站近期更新列表。您也可以通过 RSS 源 获取此信息,我会在 推特Mastodon 上发布新文章公告。

我会在此页面列出新文章和现有文章的补充内容。由于我经常分期发布文章,因此此页面上的许多条目将是近期发布文章的新增内容,此类公告会缩进显示,并且不会显示在我的主页的近期更改部分中。


照片流 130

2024 年 4 月 21 日星期日 美国东部时间下午 6:21

加州大苏尔


在遗留系统替换中使用数据复制

2024 年 4 月 10 日星期三 美国东部时间上午 11:00

Alessio FerriTom Coggrave 通过介绍如何使用数据复制,完成了关于将接缝引入大型机系统的文章。如果操作得当,它可以为替换工作提供快速启动,但我们必须警惕它将新系统耦合到遗留模式

更多…


在大型机的批处理管道中创建接缝

2024 年 4 月 4 日星期四 美国东部时间上午 9:39

大型机处理通常组织成批处理流程的管道,这些流程消耗和创建数据文件。Alessio FerriTom Coggrave 展示了他们发现的几种将接缝引入这些管道的方法,以便可以通过替换平台中的步骤替换处理。

更多…


揭示大型机外部接口中的接缝

2024 年 4 月 2 日星期二 美国东部时间上午 9:46

Alessio FerriTom Coggrave 通过将数据库视为粗粒度接缝,继续分析应用程序的内部接缝。他们描述了如何将接缝引入数据库读取器和写入器。

更多…


加入领英

2024 年 3 月 28 日星期四 美国东部时间下午 12:26

随着推特被马斯克接管,我越来越多地听到更多人使用领英来了解新的专业资料。因此,几周前,我开设了我的领英帐户,以便人们可以在该平台上关注我。

我一直避免使用领英——我发现人脉关系的整体氛围令人反感。我已经收到太多想要建立联系的人的垃圾邮件。但领英添加了“创作者模式”,鼓励人们关注某人的帖子,而不是双向联系。到目前为止,它似乎运行良好,所以我决定我也将在该帐户上发布所有更新。但是我仍然避免建立联系,所以除非我们已经进行了一些实质性的合作,否则请不要向我发送联系请求。

我仍然在 X (推特) 上发布帖子,但随着它稳步恶化,它越来越少地引起我的注意。如果您在那里关注我,我建议您尽可能切换到我的其他订阅源

我并不特别喜欢领英的订阅源,因为我几乎无法控制它。我从不喜欢网站推荐,更喜欢简单的个人订阅源。遗憾的是,领英没有列表,并将某人喜欢的所有内容都推送到单个连接订阅源中。这导致帖子过多,这意味着我不会关注太多。


告别,John Kordyback

2024 年 3 月 27 日星期三 美国东部时间下午 12:48

我们珍爱的同事和朋友 John Kordyback 于上周去世,享年 64 岁。

更多…


揭示大型机外部接口中的接缝

2024 年 3 月 27 日星期三 美国东部时间上午 10:36

Alessio FerriTom Coggrave 开始详细介绍他们使用两个外部接口领域探索的接缝。文件的批处理输入被复制到新的实现中,同时比较处理管道的输出。可以使用代理覆盖 API 接入点,并将流量逐渐定向到新的实现。

更多…


揭示大型机中的接缝以进行增量现代化

2024 年 3 月 26 日星期二 美国东部时间上午 9:32

大型机系统继续运行着世界上大部分的计算工作负载,但通常很难添加新功能来支持不断增长的业务需求。此外,使它们难以增强的架构挑战也使它们难以替换。为了降低风险,我们使用增量方法进行遗留系统替换,逐步用现代技术的实现替换遗留功能。此策略要求我们将接缝引入大型机系统:我们可以将逻辑流转移到更新服务的点。在最近的一个项目中,Alessio FerriTom Coggrave 研究了几种将这些接缝引入长期运行的大型机系统的方法。

更多…


如何捕获定性指标

2024 年 3 月 19 日星期二 美国东部时间上午 9:33

Abi NodaTim Cochran 完成了他们关于定性指标的文章,概述了如何有效地捕获它们。他们讨论了人们在回应调查时经历的心理步骤,并提供了一个模板,以便在评估开发人员体验时开始使用。最后一部分着眼于定性和定量如何协同工作:通常首先使用定性指标来建立基线并确定重点关注的领域,然后使用定量指标深入研究特定领域。

更多…


《重构》开篇章节的代码示例

2024 年 3 月 15 日星期五 美国东部时间上午 11:04

有时人们会向我要一份我在《重构》开篇章节中使用的代码,以便他们自己可以跟着做。我有理由不提供这段代码,特别是懒惰。幸运的是,Emily Bache 更加敬业,她建立了一个 GitHub 存储库——Theatrical Players Refactoring Kata——其中包含代码和足够的测试,可以合理地进行重构。

然而,该存储库的内容远不止于此,因为它包含了十几种语言的类似示例代码,包括 C、Java、Rust 和 Python。

她最近在她的YouTube 频道上发布了一个视频,概述了她为什么鼓励人们在阅读该章节时使用这段代码。她的频道包含许多关于良好代码技术的视频,并且她有一个Patreon 供读者支持她的工作。


定性指标的好处

2024 年 3 月 13 日星期三 美国东部时间上午 10:36

Abi NodaTim Cochran 继续讨论使用定性指标来评估开发团队的生产力。在本期中,他们将定性指标分为态度指标和行为指标。我们还看到,定性指标使您能够衡量原本无法衡量的事物,提供缺失的可见性,并为定量数据提供必要的背景。

更多…


通过人来衡量开发人员的生产力

2024 年 3 月 12 日星期二 美国东部时间上午 9:36

衡量开发人员的生产力是一项艰巨的挑战。传统的指标侧重于开发周期时间和吞吐量,这是有限的,而且对于还有什么其他选择,也没有明显的答案。定性指标提供了一种强大的方法,可以使用来自开发人员自身的数据来衡量和理解开发人员的生产力。Abi NodaTim Cochran 通过解释什么是定性指标以及为什么我们不应该因为它们是主观的或不可靠的而拒绝它们,开始了他们的讨论。

更多…


如果我们每天轮换配对会怎样?

2024 年 3 月 6 日星期三 美国东部时间上午 10:33

在结对编程时,经常轮换配对非常重要,但许多进行结对编程的组织都不愿意这样做。Gabriel RobainaKieran Murphy 提出了一个问题:“如果我们每天轮换配对会怎样?”,并与三个团队一起进行了一项每日轮换配对的练习。他们开发了一种轻量级方法来帮助团队反思配对的好处和挑战以及如何解决这些挑战。最初的恐惧被克服了,团队发现了频繁轮换配对的好处。他们了解到,频繁交换配对可以大大增强配对的好处。他们的文章分享了他们开发的方法、他们的观察结果,以及参与团队成员分享的一些常见恐惧和见解。

更多…


遗留系统替换模式:事件拦截

2024 年 3 月 5 日星期二 美国东部时间上午 10:03

当我们逐步替换遗留系统时,我们有很多情况下遗留系统及其替换需要交互。由于这些遗留系统通常难以更改且成本高昂,因此我们需要一种机制,该机制可以集成替换元素,同时最大限度地减少对遗留系统的影响。Ian Cartwright、Rob Horn 和 James Lewis 解释了我们如何对状态更改事件使用事件拦截,从而允许我们将它们转发到替换系统。

更多…


博客:定期面对面

2024 年 2 月 27 日星期二 美国东部时间上午 9:17

通信技术的进步导致越来越多的团队以远程优先的风格工作,这一趋势因 Covid-19 大流行的强制隔离而得到加强。但是,远程工作的团队仍然可以从面对面聚会中受益,并且应该每隔几个月进行一次。

远程优先的团队中的每个人都在不同的地点,完全通过电子邮件、聊天、视频和其他通信工具进行交流。它有明显的好处:可以从世界各地招募人员加入团队,我们可以让有照顾责任的人参与进来。浪费在令人沮丧的通勤上的时间可以变成富有成效的或恢复的时间。

但是,无论人们在远程工作中多么有能力,无论现代协作工具变得多么漂亮,都比不上与团队中的其他成员在同一个地方。当人们面对面时,人际交往总是更加丰富。视频通话太容易变得事务性,几乎没有时间进行闲聊来建立良好的人际关系。如果没有这些更深层次的联系,误解就会演变成严重的关系困难,团队可能会陷入如果每个人都能够面对面交谈就能有效解决的境地。

我从那些在远程优先工作中有效的人那里看到的一个常见模式是,他们确保定期举行面对面会议。在这些会议期间,他们安排了那些一起完成效果更好的工作内容。对于需要单独集中精力的任务,远程工作更有效,现代工具可以使远程配对变得可行。但是,当每个人都在同一个房间里时,需要许多人快速反馈的任务更容易完成。没有任何视频会议系统可以创造出如此深度的互动,盯着电脑屏幕看别人在做什么很累,而且没有机会一起出去喝杯咖啡来休息一下。

为了让人们有效地一起工作,他们需要相互信任,了解他们可以在多大程度上相互依赖。信任在网上很难建立,因为在网上没有我们在同一个房间里时可能出现的社交线索。因此,面对面聚会中最有价值的部分不是安排好的工作,而是在喝咖啡时的闲聊和午餐时的欢乐。非正式的谈话,大部分与工作无关,建立了人际交往,使工作互动更加有效。

这些准则建议了面对面会议的内容。协同工作本身就很有价值,也是团队建设的重要组成部分。因此,我们应该安排一整天的工作时间,专注于那些受益于面对面低延迟沟通的任务。然后,我们应该留出足够多的时间用于休息、非正式聊天和走出办公室的机会。我会避免任何刻意安排的“团队建设”活动,仅仅是因为我讨厌它们。那些组织过这类聚会的人强调,每个人在聚会后都会充满活力,因此在接下来的几周内工作效率会更高。

远程团队的成员可能分布在很远的地方,通常情况下,成员之间相隔数小时的行程。对于这样的团队,我的经验法则是每两到三个月聚在一起一周。在团队成熟之后,他们可能会决定减少聚会的频率,但我担心如果一个团队一年中至少没有两次面对面的会议会怎么样。如果一个团队的所有成员都在同一个城市,但为了减少通勤时间而采用远程优先的工作方式,那么他们可以组织时间更短、频率更高的聚会。

这种聚会可能会让人们重新思考如何配置办公空间。自疫情以来,人们对办公室使用率大幅下降的讨论不绝于耳。办公室很可能不再是日常工作场所,而更多地成为这类不定期团队聚会的场所。这就需要灵活舒适的团队聚会空间。

一些组织可能会对团队聚会的差旅和住宿费用感到不满,但他们应该将其视为对团队效率的投资。忽视这些面对面的交流会导致团队陷入困境,朝着错误的方向前进,充满冲突,以及员工失去动力。与之相比,节省机票和酒店费用是得不偿失的。

延伸阅读

远程优先是远程工作的一种形式,我在远程工作与集中办公一文中探讨了不同类型的远程工作方式及其优缺点。

在Thoughtworks,我们在大约20年前刚开始建立离岸开发中心时,就意识到了定期面对面聚会对远程团队的重要性。这些聚会催生了我在使用敏捷软件流程进行离岸开发一文中描述的实践。

远程工作,尤其是在跨时区的情况下,更加需要异步协作模式。我的同事、产品经理Sumeet Moghe在他的著作异步优先行动指南中深入探讨了如何做到这一点。

软件产品公司Atlassian最近完全转向了远程工作模式,并发布了一份经验报告。他们已经了解到,团队最好每年大约进行三次面对面聚会。Claire Lew在2018年对远程优先团队进行了一项调查,指出四分之一的受访者“每年都会进行多次”务虚会。37Signals作为一家远程优先的公司已经运营了近20年,并每年安排两次聚会

致谢

Alejandro Batanero、Andrew Thal、Chris Ford、Heiko Gerin、Kief Morris、Kuldeep Singh、Matt Newman、Michael Chaffee、Naval Prabhakar、Rafael Detoni和Ramki Sitaraman在我们的内部邮件列表中讨论了这篇文章的草稿。


LLM应用开发的工程实践

2024年2月13日,星期二,美国东部时间12:22

LLM工程不仅仅是提示设计或提示工程。在这里,David TanJessie Wang反思了测试和重构等常规工程实践如何帮助他们快速可靠地交付LLM应用原型。

更多…


入职瓶颈:最后一部分

2024年1月31日,星期三,美国东部时间11:57

Tim和Prem完成了他们关于有效入职的文章。 他们讨论了结对编程、建立个人环境以及消除流程中摩擦的价值。

更多…


入职瓶颈:良好入职的更多步骤

2024年1月30日,星期二,美国东部时间09:33

Tim和Prem继续概述有效入职流程的步骤。 他们谈到了让新员工融入公司文化、做好录用通知后的体验和第一天入职体验,以及投资自助式知识管理。

更多…


通过补全功能改进我的Emacs体验

2024年1月25日,星期四,美国东部时间13:20

我使用Emacs已经很多年了,用它来编写我的网站、书籍以及大部分程序。(例外情况是使用IntelliJ IDEA编写Java代码和使用RStudio编写R代码。)因此,我很高兴看到过去几年中Emacs的功能有了很大的改进,这让我觉得它不再是一个进化死胡同。对我来说,Emacs体验的最大改进之一是使用正则表达式进行补全列表。

许多Emacs命令会生成可供选择的列表。如果我想访问(打开)一个文件,我输入查找文件的组合键,Emacs就会在minibuffer(一个用于与命令交互的特殊区域)中弹出一个候选文件列表。这些文件列表可能非常长,尤其是我要请求列出当前项目中的所有文件时。

为了指定我想要的文件,我可以输入一些文本来过滤列表,所以如果我想打开文件articles/simple/2024-emacs-completion.md,我可能会输入emacs。我不必只获取那一个文件,只需过滤到足够小的列表通常就足够了。

有一种特定类型的正则表达式构建器我觉得最有用,它用空格分隔正则表达式。这将允许我输入articles emacs来获取文件路径中包含“articles”和“emacs”的所有文件的列表。它本质上是将字符串“articles emacs”转换为正则表达式\\(articles\\).*\\(emacs\\)。更好的是,这样的匹配器允许我以任何顺序输入正则表达式,因此“emacs articles”也能匹配。这样,一旦第一个正则表达式弹出一个过滤后的列表,我就可以使用第二个正则表达式来选择我想要的那个文件,即使区分的正则表达式在我最初搜索的前面。

安装这样一个补全匹配器对我的Emacs使用方式产生了显著的影响,因为它使我在与命令交互时可以轻松过滤大型列表。其中最显著的一点是它改变了我对M-x的使用方式,M-x是调出所有交互式Emacs函数列表的组合键。使用正则表达式匹配器过滤列表后,我只需敲击几下键盘就可以使用其名称调用Emacs命令。这样我就不必记住键盘快捷键了。有了这个功能,我可以通过M-x调用我不太常用的命令。我不经常列出所有打开的缓冲区,所以与其试图记住它的组合键,我只需输入M-x ibibuffer就会很快弹出。我使用的M-x命令(counsel-M-x)在正则表达式的第一个字符处插入了一个“^”,这将第一个正则表达式锚定到行的开头,这对我有很大帮助。因为我把我所有自己写的函数都加上了mf-前缀,所以即使函数名很长,我也能很容易地找到它们。我写了一个命令来删除URL中的域名,我把它叫做mf-url-remove-domain,可以用M-x mf url来调用它。

Emacs中有很多软件包可以进行这种匹配,多到令人困惑。我现在使用的是Ivy。默认情况下,它使用空格分隔的正则表达式匹配器,但不支持任何顺序。为了按照我喜欢的方式配置它,我使用

(setq ivy-re-builders-alist '((t . ivy--regex-ignore-order)))

Ivy是一个名为counsel的软件包的一部分,该软件包包含各种增强此类选择的命令。

Ivy并不是唯一能做到这一点的工具。事实上,Emacs中补全工具的世界让我非常困惑:很多工具之间存在重叠和交互,我并不真正理解。这个领域的工具包括HelmcompanyVerticoConsult。《精通Emacs》有一篇关于理解Minibuffer补全的文章,但它没有解释它所讲的机制是如何与Ivy的功能相适应的,我也没有花时间去弄清楚这一切。

总的来说,我强烈推荐《精通Emacs》这本书来学习如何使用这个不可思议的工具。Emacs的功能非常多,即使像我这样使用了几十年的用户,在阅读这本书时也会发现“我不知道它还能做到这一点”。

对于那些好奇的人,这里是我的Emacs配置的相关部分

(use-package ivy
  :demand t
  :diminish ivy-mode
  :config
  (ivy-mode 1)
  (counsel-mode 1)
  (setq ivy-use-virtual-buffers t)
  (setq ivy-use-selectable-prompt t)
  (setq ivy-ignore-buffers '(\\` " "\\`\\*magit"))
  (setq ivy-re-builders-alist '(
                                (t . ivy--regex-ignore-order)
                                ))
  (setq ivy-height 10)
  (setq counsel-find-file-at-point t)
  (setq ivy-count-format "(%d/%d) "))

(use-package counsel
  :bind (
         ("C-x C-b" . ivy-switch-buffer)
         ("C-x b" . ivy-switch-buffer)
         ("M-r" . counsel-ag)
         ("C-x C-d" . counsel-dired)
         ("C-x d" . counsel-dired)
         )
  :diminish
  :config
  (global-set-key [remap org-set-tags-command] #'counsel-org-tag))

(use-package swiper
  :bind(("M-C-s" . swiper)))

(use-package ivy-hydra)

入职瓶颈:创造通往高效的道路

2024年1月24日,星期三,美国东部时间10:45

Tim和Prem开始讨论如何摆脱入职的困境,他们解释了如何为新员工创造一条通往高效的道路。这条道路概述了员工的需求以及入职流程应该如何满足这些需求。

更多…


快速成长企业的瓶颈#06:入职

2024年1月23日,星期二,美国东部时间09:13

过去一年对科技行业来说是艰难的一年,科技行业遭遇了自本世纪初互联网泡沫破灭以来最大规模的裁员和裁员潮。随着2024年的到来,我们看到了行业复苏的早期迹象,这 hopefully 意味着科技公司将很快再次考虑招聘。如果这样的好日子真的回来了,企业将再次面临一个常见的问题,那就是新员工需要很长时间才能变得高效。 Tim Cochran和Premanand Chandrasekaran在我们关于快速成长企业瓶颈系列文章的第六部分中解决了这个问题。在第一部分中,Tim和Prem着眼于快速发展的组织正在遭遇这一瓶颈的迹象。

更多…


持续集成的一次重大修订

2024年1月18日,星期四,美国东部时间10:01

在本世纪之交,我有幸参与了几个开发持续集成实践的项目。我把我从这项工作中吸取的教训写成了一篇文章,发表在我的网站上,这篇文章至今仍是这一重要实践的常用参考资料。去年年底,一位同事联系我说,这篇文章现在已经快20年了,仍然很有用,但已经过时了。他给我发了一些修改建议,我以此为契机,对这篇文章进行了彻底的修改,考虑了原文中的每一部分,并添加了新的部分来处理过去20年中出现的问题。

在这几十年里,特性分支已经在业界得到了广泛的采用。我的许多同事认为,持续集成更适合许多团队,我希望这篇文章能够帮助读者评估这是否是事实,如果是,如何有效地实施持续集成。

更多…


Bliki:遗留接缝

2024年1月4日,星期四,美国东部时间09:13

在处理遗留系统时,识别和创建接缝非常有价值:我们可以不修改源代码就改变系统行为的地方。一旦我们找到了一个接缝,我们就可以利用它来打破依赖关系,简化测试,插入探针以获得可观察性,并在遗留系统替换过程中将程序流重定向到新的模块。

Michael Feathers在他的著作修改代码的艺术中,在遗留系统的上下文中创造了“接缝”一词。他的定义是:“接缝是指在不修改代码的情况下改变程序行为的地方”

下面是一个接缝很方便的例子。想象一下,有一些代码可以计算订单的价格。

// TypeScript
export async function calculatePrice(order:Order) {
  const itemPrices = order.items.map(i => calculateItemPrice(i))
  const basePrice = itemPrices.reduce((acc, i) => acc + i.price, 0)
  const discount = calculateDiscount(order)
  const shipping = await calculateShipping(order)
  const adjustedShipping = applyShippingDiscounts(order, shipping)
  return basePrice + discount + adjustedShipping
}

函数 calculateShipping 调用了一个外部服务,该服务速度慢(且成本高),因此我们不想在测试时调用它。相反,我们想引入一个 存根,以便我们可以为每个测试场景提供预设的确定性响应。不同的测试可能需要函数的不同响应,但我们不能在测试中编辑 calculatePrice 的代码。因此,我们需要在调用 calculateShipping 的周围引入一个接缝,以便我们的测试可以将调用重定向到存根。

一种方法是将 calculateShipping 的函数作为参数传递

export async function calculatePrice(order:Order, shippingFn: (o:Order) => Promise<number>) {
  const itemPrices = order.items.map(i => calculateItemPrice(i))
  const basePrice = itemPrices.reduce((acc, i) => acc + i.price, 0)
  const discount = calculateDiscount(order)
  const shipping = await shippingFn(order)
  const adjustedShipping = applyShippingDiscounts(order, shipping)
  return basePrice + discount + adjustedShipping
}

然后,此函数的单元测试可以替换一个简单的存根。

const shippingFn = async (o:Order) => 113
expect(await calculatePrice(sampleOrder, shippingFn)).toStrictEqual(153)

每个接缝都有一个启用点:“一个你可以决定使用一种行为或另一种行为的地方” [WELC]。将函数作为参数传递会在 calculateShipping 的调用者中打开一个启用点。

这使得测试变得容易多了,我们可以输入不同的运费值,并检查 applyShippingDiscounts 是否响应正确。虽然我们必须更改原始源代码才能引入接缝,但对该函数的任何进一步更改都不需要我们更改该代码,所有更改都发生在启用点,该点位于测试代码中。

将函数作为参数传递并不是引入接缝的唯一方法。毕竟,更改 calculateShipping 的签名可能会很麻烦,而且我们可能不希望在生产代码中通过旧式调用堆栈传递运费函数参数。在这种情况下,查找可能是更好的方法,例如使用服务定位器。

export async function calculatePrice(order:Order) {
  const itemPrices = order.items.map(i => calculateItemPrice(i))
  const basePrice = itemPrices.reduce((acc, i) => acc + i.price, 0)
  const discount = calculateDiscount(order)
  const shipping = await ShippingServices.calculateShipping(order)
  const adjustedShipping = applyShippingDiscounts(order, shipping)
  return basePrice + discount + adjustedShipping
}
class ShippingServices {
  static #soleInstance: ShippingServices
  static init(arg?:ShippingServices) {
    this.#soleInstance = arg || new ShippingServices()
  }
  static async calculateShipping(o:Order) {return this.#soleInstance.calculateShipping(o)}
  async calculateShipping(o:Order)  {return legacy_calcuateShipping(o)}
  // ... more services

定位器允许我们通过定义子类来覆盖行为。

class ShippingServicesStub extends ShippingServices {
  calculateShippingFn: typeof ShippingServices.calculateShipping =
     (o) => {throw new Error("no stub provided")}
  async calculateShipping(o:Order) {return this.calculateShippingFn(o)}
  // more services

然后我们可以在测试中使用一个启用点

const stub = new ShippingServicesStub()
stub.calculateShippingFn = async (o:Order) => 113
ShippingServices.init(stub)
expect(await calculatePrice(sampleOrder)).toStrictEqual(153)

这种服务定位器是通过函数查找设置接缝的经典面向对象方法,我在这里展示它是为了说明我可能在其他语言中使用的方法,但我不会在 TypeScript 或 JavaScript 中使用这种方法。相反,我会将类似的内容放入模块中。

export let calculateShipping = legacy_calculateShipping

export function reset_calculateShipping(fn?: typeof legacy_calculateShipping) {
  calculateShipping = fn || legacy_calculateShipping
}

然后我们可以在这样的测试中使用代码

const shippingFn = async (o:Order) => 113
reset_calculateShipping(shippingFn)
expect(await calculatePrice(sampleOrder)).toStrictEqual(153)

正如最后一个示例所暗示的那样,用于接缝的最佳机制很大程度上取决于语言、可用的框架以及旧系统的风格。控制旧系统意味着学习如何将各种接缝引入代码中,以提供正确的启用点,同时最大限度地减少对旧软件的干扰。虽然函数调用是引入此类接缝的简单示例,但在实践中它们可能要复杂得多。一个团队可能需要花费几个月的时间来确定如何将接缝引入到一个陈旧的旧系统中。向旧系统添加接缝的最佳机制可能与我们在全新领域中为获得类似灵活性所做的不同。

Feathers 的书主要侧重于对旧系统进行测试,因为这通常是以合理的方式使用它的关键。但接缝的用途远不止于此。一旦我们有了一个接缝,我们就可以将探针放置到旧系统中,从而提高系统的可观察性。我们可能希望监控对 calculateShipping 的调用,确定我们使用它的频率,并捕获其结果以进行单独分析。

但接缝最有价值的用途可能是它们允许我们将行为从旧系统中迁移出来。接缝可能会将高价值客户重定向到不同的运费计算器。有效的旧系统替换建立在将接缝引入旧系统的基础上,并使用它们逐渐将行为迁移到更现代的环境中。

接缝也是我们在编写新软件时需要考虑的事情,毕竟每个新系统迟早都会变成旧系统。我的许多设计建议都是关于构建具有适当放置的接缝的软件,以便我们可以轻松地测试、观察和增强它。如果我们在编写软件时考虑到测试,我们往往会获得一组良好的接缝,这就是为什么 测试驱动开发 是一种如此有用的技术的原因。


我在 2023 年最喜欢的音乐发现

2024 年 1 月 2 日,星期二,美国东部时间下午 6:42

又一年,又到了挑选 六个最喜欢的音乐发现 的时候了。2023 年包括氛围蓝草音乐、非洲-安第斯放克音乐、诺森伯兰小风笛、科拉舞曲和乌克兰民间爵士乐。

更多…


博客:软件与工程

2023 年 12 月 13 日,星期三,美国东部时间上午 12:00

在我的整个职业生涯中,人们一直将软件开发与“传统”工程进行比较,通常是以责备软件开发人员没有做好工作的方式。作为一个获得电子工程学位的人来说,这在我职业生涯的早期引起了我的共鸣。但这种思维方式是有缺陷的,因为大多数人对工程在实践中的运作方式有错误的印象。

Glenn Vanderburg 花了很多时间来研究这些误解,我强烈建议任何想将软件开发与工程进行比较的人都去看看他的演讲 真正的软件工程。也很值得一听 他在 Oddly Influenced 播客上的采访。遗憾的是,我一直无法说服他把这些材料写下来——这将是一篇很棒的文章。

Hillel Wayne 是另一位对这种关系有很好的思考的人。他采访了一群“跨界者”——那些在传统工程和软件领域都工作过的人。他将自己学到的东西写成了一系列文章,从 我们真的是工程师吗? 开始。


博客:测试驱动开发

2023 年 12 月 11 日,星期一,美国东部时间下午 2:40

测试驱动开发 (TDD) 是一种构建软件的技术,它通过编写测试来指导软件开发。它是由 Kent Beck 在 20 世纪 90 年代后期作为极限编程的一部分开发的。本质上,我们反复遵循三个简单的步骤

虽然这三个步骤(通常概括为*红-绿-重构*)是该过程的核心,但还有一个至关重要的初始步骤,即我们首先写出一个测试用例列表。然后我们选择其中一个测试,对其应用红-绿-重构,完成后再选择下一个。正确地对测试进行排序是一项技能,我们希望选择那些能快速将我们带到设计重点的测试。在这个过程中,我们应该在想到更多测试时将它们添加到列表中。

首先编写测试,即 XPE2 所说的测试优先编程,提供了两个主要好处。最明显的是,它是一种获得 自测试代码 的方法,因为我们只能编写一些功能代码来响应测试通过。第二个好处是,首先考虑测试会迫使我们首先考虑代码的接口。这种对接口以及如何使用类的关注有助于我们将接口与实现分离,这是许多程序员难以掌握的良好设计的关键要素。

我听到的最常见的搞砸 TDD 的方法是忽略了第三步。重构代码以保持其整洁是该过程的关键部分,否则我们最终只会得到一堆混乱的代码片段。(至少这些代码片段会有测试,所以这比大多数设计失败的结果要好一些。)

延伸阅读

Kent 对 规范的 TDD 方法 的总结是关键的在线总结。

如果想了解更多内容,请参阅 Kent Beck 的著作 测试驱动开发

James Shore 的著作 敏捷开发的艺术 中的相关章节是对 TDD 的另一个合理的描述,它还将其与有效的敏捷开发的其他部分联系起来。James 还写了一系列名为 让我们玩转 TDD 的截屏视频。

修订

我最初发布此页面的时间是 2005 年 3 月 5 日。受 Kent 的规范文章的启发,我在 2023 年 12 月 11 日对其进行了更新。


博客:差异调试

2023 年 12 月 4 日,星期一,美国东部时间上午 12:00

回归错误是软件中已经存在一段时间的特性中新出现的错误。在查找这些错误时,找出软件中的哪些更改导致了这些错误的出现通常很有价值。查看这些更改可以为错误的位置以及如何消除错误提供宝贵的线索。这种调查形式没有一个众所周知的术语,但我称之为差异调试。

差异调试只有在我们对代码进行版本控制的情况下才有效,但幸运的是,现在这是常态。但也有一些事情需要做,才能使其有效地工作。我们需要 可重现的构建,以便我们可以轻松地运行旧版本的软件。由于 高频集成,拥有小的提交非常有帮助。这样,当我们找到有问题的提交时,就可以更容易地缩小范围,找出发生了什么。

为了找到产生错误的提交,我们首先要找到任何一个没有错误的过去版本。将此标记为*最后一个好的*版本,并将当前版本标记为*最早的坏的*版本。然后找到两者之间的中间提交,看看错误是否在那里。如果是,则此提交成为最早的坏的版本,否则它成为最后一个好的版本。重复此过程(即“半区间”或“二分”搜索),直到找到有问题的提交。

如果我们使用 git,那么 git bisect 命令将为我们自动完成大部分工作。如果我们可以编写一个测试来显示错误的存在,那么 git bisect 也可以使用它,从而自动完成查找有问题的提交的整个过程。

我经常发现差异调试在编程会话中很有用。如果我有一些需要几分钟才能运行的慢测试,我可能会编程半小时,只运行最相关测试的一个子集。只要我在每次绿色测试运行后提交,如果其中一个较慢的测试失败,我就可以使用差异调试。这就是极其频繁地提交的价值所在,即使它们很小,以至于我觉得最好将它们压缩到长期历史中。一些 IDE 通过自动保存比版本控制提交更细粒度的本地历史记录,使这变得更容易。

修订

我最初是在 2004 年 6 月 1 日发布此页面的。在其最初的形式中,它更像是一个随意的经验报告。我在 2023 年 12 月 4 日对其进行了重写,使其更像是对该术语的定义。差异调试并不是一个在业界流行起来的术语,但我还没有看到其他术语被普遍用来描述它。


如何解决编码助手的不可靠性

2023 年 11 月 28 日,星期二,美国东部时间上午 10:21

在过去的一年里,许多开发人员将 LLM 编码助手纳入了他们的工作中,发现它们是一个有用的工具。但这些工具的问题之一是它们不可靠,经常会提出糟糕的或完全错误的建议。Birgitta Böckeler 继续探索面向开发人员的 GenAI,她分享了她学到的关于 如何看待这种不可靠性 以及为什么将 LLM 工具称为“Dusty”可能更好的知识。

更多…


《分布式系统模式》由 Pearson 出版

2023 年 11 月 24 日,星期五,美国东部时间下午 2:11

在过去的四年里,我的同事 Unmesh Joshi 一直在开发一系列模式,以帮助我们所有人更好地理解现代分布式系统是如何工作的。我们一直在本网站上发布这些模式的草稿。现在,这些草稿已经变成了一本 由 Addison-Wesley 出版 的书,属于我的签名系列。因此,我们现在已经从本网站上删除了正在进行中的草稿,并用 模式摘要目录 替换了它们。对于订阅了 oreilly.com 的用户,我们提供了从摘要到在线书籍相关章节的深层链接。

更多…


文科学位帮助我在科技行业取得成功的三个原因

美国东部时间 2023 年 11 月 9 日,星期四 10:12

我的同事李珊妮遇到过很多想进入科技行业的学生,他们都选择了专业性很强的专业。然而,珊妮发现传统的文科教育赋予了她与产品经理工作高度相关的技能。

更多…