enti下载器

我们的代码已被破坏了几个星期。 编译器错误,测试失败,行为错误困扰着我们的团队。 为什么? 因为我们被盲目蛙跳打了。 通过对关键组件进行多次并发更改以希望对其进行改进,我们已经从其丑陋但稳定且可以工作的状态飞跃到了破碎的沼泽。 我们最好的意图给我们带来了破坏,预计将需要几个工时才能完成的工作使我们失去了一个多月的时间,直到改变最终得以恢复(暂时)。

经验教训:避免蛙跳。 相反,请遵循肯特·贝克(Kent Beck)的“ 冲刺enti”的策略- 循序渐进 ,安全,可靠地进行操作,不会破坏代码。 经常(最好是每天)将其部署到生产中,以迫使自己进行非常小的且非常安全的更改。 请勿同时更改多个不相关的内容。 不要以为您知道代码是如何工作的。 不要以为您要进行的更改是简单的更改。 进行全面测试(不要过于信任您的测试套件)。 通过运行测试,执行代码,运行生产中的代码,让计算机为您提供有关更改的反馈和确凿的事实。

发生了什么? 我们有批处理作业,其配置属性可以通过(1)命令行参数或(2)特定于作业或(3)文件中的共享条目来设置。 通过静态调用Configuration.get('my.property')访问它的作业。 由于全局的,自动加载的配置使得无法对具有不同配置的作业进行单元测试,因此我们想用传递过来的配置实例代替单例。 我将简要描述我们失败的重构,提出一种更好的方法,并讨论如何在没有此类失败的情况下开发和重构软件。

沼泽之路

我们试图产生这种情况:

大型重构失败

我们首先用三个可实例化的类替换了静态Configuration ,每个可实例化的类仅具有一个职责( SRP )。

然后,我们修改了每个作业以接受/创建所需的作业。

我们还将一些命令行参数和属性重命名为更易理解的名称,并进行了各种小的改进。

最后,我们用更好的东西代替了配置系统的(错误)用法,该配置系统用于存储有关作业上次完成工作的位置(“书签”)的信息。

副作用是,还进行了其他一些更改,例如,不再从类路径上的某个位置加载默认配置,而是从相对文件系统路径中加载了默认配置,并且命令行参数的处理方式几乎没有什么不同。

除一项测试外,其他所有测试均已通过,并且看起来都不错。

它变得更加复杂(更改需要其他更改,原始设计过于简单等),因此花费的时间比预期的要长,但是我们终于进行了处理。

但是,当我们尝试运行该应用程序时,它不起作用。 它找不到其配置文件(因为不再在类路径中查找它),一些用于尊重作业特定值的属性也不再这样做,并且我们在不知不觉中引入了一些不一致和缺陷。 事实证明,要找出各个配置属性是如何交互的,并确保在必要的所有位置以正确的顺序尊重所有配置源(命令行参数,共享属性和特定于作业的属性),这太困难了。 我们修复它的尝试花了很长时间而且徒劳。 (修复您一开始不了解的旧代码库绝非易事。)

即使我们设法解决了所有问题,仍然会存在另一个问题。 更改不向后兼容。 为了能够将它们部署到生产中,我们需要停止所有操作,(正确地)更新所有配置和我们的cron作业。 潜在的许多错误。

有更好的方法吗?

这是否意味着改进软件的风险太大而无法获得回报? 否,如果我们更加谨慎,请以较小,安全,经过验证的步骤进行,并尽量减少或避免破坏性变化。 让我们看看如果遵循这些原则怎么办。

介绍配置实例

作为第一步(见图2),我们可以离开几乎一切,因为它是,只介绍一个临时ConfigurationInstance 1类相同的API(为了使下拉更换更容易)作为Configuration ,但是非静态和修改Configuration将所有呼叫转发到其自己的(单实例) ConfigurationInstance实例2

一个小,简单,安全的更改。

然后,我们可以修改我们的工作,一个接一个,使用一个ConfigurationInstance通过获得Configuration.getInstance()默认情况下并在创建时也可以选择接受它的一个实例。

(请注意,在此过程中的任何时候,我们都可以并且应该将其部署到生产中。)

代码如下所示:

class Configuration {private static ConfigurationInstance instance = new ConfigurationInstance();public static ConfigurationInstance getInstance() { return instance; }public static void setTestInstance(ConfigurationInstance config) { instance = config; } // for tests onlypublic static String get(String property) { instance.get(property); }
}
class ConfigurationInstance {...public String get(String property) { /** some code ... */ }
}
class MyUpdatedJob {private ConfigurationInstance config;/** The new constructor */MyUpdatedJob(ConfigurationInstance config) { this.config = config; }/** @deprecated The old constructor kept for now for backwards-compatibility */MyUpdatedJob() { this.config = Configuration.getInstance(); }doTheJob() { ... config.get('my.property.xy') ... }
}
class OldNotUpdatedJob {doTheJob() { ... Configuration.get('my.property.xy') /* not updated yet, not testable */ ... }
}

一旦完成所有作业,我们就可以更改作业的实例化以传递到ConfigurationInstance 。 接下来,我们可以删除所有其余的对Configuration引用,将其删除,然后将ConfigurationInstance重命名为Configuration 。 我们可以定期将其部署到我们的测试/过渡环境,最后部署到生产中,以确保一切仍然正常(对于更改而言,这应该是最小的)。

接下来,作为单独的独立更改,我们可以考虑并更改“书签”的存储方式。 其他改进(例如重命名配置属性)也应在以后独立完成。 (毫不奇怪,您所做的更改越多,出现问题的风险就越大。)

我们也可以/应该引入代码,以自动从旧配置迁移到新配置,例如,如果不存在新格式的书签,则以旧格式读取书签并将其存储在新配置中。 对于属性,我们可以添加代码以同时检查旧名称和新名称(并在仍然使用旧名称时发出警告)。 因此,部署更改将不需要我们与配置和执行更改同步。

-

1 )请注意,Java不允许我们使用相同名称的静态和非静态方法,因此我们需要在Configuration中使用不同名称创建实例方法,或者像我们一样创建另一个类。 我们希望保持相同的名称,以使从静态配置迁移到实例配置成为一个简单而安全的搜索和替换的问题(将字段configuration添加到目标类之后,将“ Configuration. ”替换为“ configuration. ”。) “ ConfigurationInstance”是一个丑陋的名称,但以后可以轻松安全地进行更改。

2 )迈克尔·费瑟斯(Michael Feathers)的开创性著作《有效地使用旧版代码 》中介绍的引入实例委托人重构

安全软件演进的原理

更改遗留的代码-结构不良,测试欠佳的代码是有风险的,但是对于防止其进一步威慑,使其更好,从而降低其维护成本而言是必需的。 如果我们谨慎行事,并采取小而安全的步骤进行操作,同时定期(通过测试和部署到阶段/生产中)对变更进行定期验证,则很有可能将风险降到最低。

什么是安全的零钱?

这取决于。 但是一个很好的经验法则可能是这样的变化:(1)其他人每天都可以合并,然后(2)可以部署到阶段(例如,一天之后,进入生产)。 如果每天都有可能合并,则它必须相对较小且无损。 如果应每天进行部署,则它必须是安全的并且向后兼容(或自动迁移旧数据)。 如果您的更改大于或大于此更改,则说明更改过大/过高。

安全成本与收益

以安全的方式更改软件既不容易也不便宜。 这要求我们认真思考,偶尔使它变得难看,花费资源来维护(临时)向后兼容性。 在不顾及这种安全性的情况下更改代码似乎更加有效-但只有在您遇到意料之外的问题并花几天时间尝试修复问题,陷入困境时(即,队友不断更改的代码库),这才更有效率。 它与TDD类似-速度较慢,但​​由于您无需浪费时间调试和排除生产问题,因此可以得到回报。 (不幸的是,显示您*不要*放松团队或管理层的时间)。

冲刺C –系列微小变化

为了以小而安全的步骤进行更改,我们经常需要对其进行分解,并通过一系列小更改不断地朝着目标设计发展代码,每个小更改都在代码库的有限区域内并沿单个轴进行,仅一件事。 更改越小越安全,我们执行和验证它们的速度就越快,因此随着时间的推移会发生很大变化–这就是Kent Beck所说的Sprinting Centipede策略。

并行设计 :有时无法真正分解更改,例如用另一个替换数据存储。 在这种情况下,我们可以应用例如并行设计技术,即在保持旧代码不变的情况下发展新代码。 例如,我们可以首先只编写代码以将数据存储在新数据库中; 然后开始从中读取,同时从旧数据库中读取,验证结果是否相同并返回旧数据库; 然后我们可以开始返回新结果(仍然保留旧代码以便能够切换回去); 最后,我们可以淘汰旧代码和存储。

先决条件

当然,只有在您可以构建,测试和部署并Swift收到有关可能出现的问题的警告时,才可以遵循Sprinting Centipede策略。 测试和部署(以及反馈)的周期越长,您必须执行的步骤就越长,否则您将花费​​大部分时间等待。

常问问题

我如何将不确定的变更部署到生产中?

代码,尤其是遗留代码,很少能进行真正的彻底测试,因此我们永远无法完全确定其正确性。 但是我们不能放弃改进代码,否则只会变得更糟。 恐惧和停滞不是解决方案,而是对代码库和流程的改进。 我们必须谨慎考虑风险并进行相应的测试。 更改越小,监视和回滚或快速修复的能力越好,则可能造成的缺陷影响就越小。 (如果有出现缺陷的机会,那么将会更早或更晚出现缺陷。)

结论

遵循Sprinting Cantipede软件演化策略似乎很慢,即以小规模,安全,很大程度上无中断的增量步骤进行,同时与主要开发部门定期合并,并经常通过运行测试和部署到生产环境来验证更改, 。 但是,我们的经验表明,由于意外而导致的盲目青蛙飞跃(希望驱动的大型更改或批量更改)实际上可能要慢得多,并且有时是不可行的,但总是会出现复杂性和缺陷以及随之而来的延迟,分支分散等。而且我相信这种情况经常发生。 因此,一次只更改一件事情,最好是在不进行其他(配置等)更改的情况下部署更改,收集反馈,确保您可以随时停止重构,同时从中获得尽可能多的价值。它(而不是将大量精力投入到大型更改中,并冒着将其完全放弃的风险)。 你有什么经验?

值得注意的是,类似的问题经常在项目级别上发生。 人们尝试一次生产太多产品,而不是根据反馈而不是信念来做最少的事情,部署和继续开发。

资源资源

  • 有限红色协会 ( 此处是一个很好的总结 )– Joshua Kerievsky讨论了在开发软件时减少“红色”时间段的必要性(还介绍了并行更改,我在上面将其称为并行设计)
  • 肯特·贝克(Kent Beck)关于低功能延迟和高吞吐量的软件设计最佳实践的演讲摘要
  • Mikado方法一书 (在线)在“ mikado方法–失败的产物”部分(当前版本为1.2)中很好地描述了典型的失败重构。

致谢

我要感谢我的同事Anders,Morten和Jeanine的帮助和反馈。 对不起,安德斯,我不能再说短了。 你知道,每个字都是我的孩子。

参考: The Sprinting Centpede Strategy:如何在我们的JCG合作伙伴 Jakub Holy( The Holy Java博客) 上改进软件而又不破坏其功能 。

翻译自: https://www.javacodegeeks.com/2013/01/the-sprinting-centipede-strategy-how-to-improve-software-without-breaking-it.html

enti下载器

enti下载器_短跑enti策略:如何在不破坏软件的情况下改进软件相关推荐

  1. 短跑enti策略:如何在不破坏软件的情况下改进软件

    我们的代码已被破坏了几个星期. 编译器错误,测试失败,错误行为困扰着我们的团队. 为什么? 因为我们被盲目蛙跳打了. 通过对关键组件进行多次并发更改以希望对其进行改进,我们已经从其丑陋但稳定且可工作的 ...

  2. python tkinter下载器_下载小说还要去找网站?Python使用tkinter打造一个小说下载器...

    前言 今天教大家用户Python GUI编程--tkinter 打造一个小说下载器,想看什么小说,就下载什么小说 先看下效果图 Tkinter 是使用 python 进行窗口视窗设计的模块.Tkint ...

  3. python小说下载器_【Python】DouBiNovel小说下载器V0.1.1(源码+成品)【失效待修复】...

    简单说明 之前在论坛发现一个很好的小说阅读站,可惜只能在线阅读,无法下载到本地,导出到电子书设备上.恰好最近在学python,于是就有了写一个下载器的想法. 但,对于初学菜鸟.又是初三学子的我,这又谈 ...

  4. KT6368A蓝牙芯片用户PC升级_搭配下载器_使用说明

    目录 一.下载原理简介 KT6368A双模蓝牙芯片是flash版本,支持重复烧录程序,但是烧录程序必须使用专用的下载工具 这个工具需要由我们来提供. 下载的总体思路是,把芯片和PC电脑相连接,通过US ...

  5. python3中的int类型占64位_在windows 10 64位计算机中,默认情况下,numpy数组数据类型将以int32形式出现...

    最初的海报Prana问了一个非常好的问题."为什么在64位计算机上,整数默认设置为32位?"在 据我所知,简短的回答是:"因为它的设计是错误的". 显然,64位 ...

  6. python实现bt下载器_使用Python编写基于DHT协议的BT资源爬虫

    关于DHT协议 DHT协议作为BT协议的一个辅助,是非常好玩的.它主要是为了在BT正式下载时得到种子或者BT资源.传统的网络,需要一台中央服务器存放种子或者BT资源,不仅浪费服务器资源,还容易出现单点 ...

  7. python多线程下载器_用 python 实现一个多线程网页下载器

    学习之 #!/usr/bin/env python # -*- coding:utf-8 -*- import urllib, httplib import thread import time fr ...

  8. 双屏不同缩放比例_[WIN10]如何解决鼠标在双屏分辨率不同的情况下移动的问题 顺便说下 U2718Q 的体验...

    问题描述 买的 27 寸的 4K 屏幕 U2718Q 到了.我之前用的是一个 LG 的 1980X1080 的 24 寸屏幕. 我先把两个屏幕左右摆放使用. 然后问题就来了. 两个屏幕尺寸差不多,但是 ...

  9. itunes 区域上架情况_在没有额外的膨胀软件的情况下安装iTunes的分步指南

    itunes 区域上架情况 Last week our friend Ed Bott wrote up an excellent article on how to install iTunes 10 ...

最新文章

  1. 【c语言】蓝桥杯算法训练 乘法表
  2. VTK:Filtering之TriangulateTerrainMap
  3. Android--intent详解
  4. RabbitMQ学习之集群消息可靠性测试
  5. 吴恩达《机器学习》第一章:监督学习和无监督学习
  6. 拓扑排序----Kahn算法和字典序最小的拓扑排序
  7. C++程序的编写和实现
  8. React+Webpack+Antd+Babel 兼容低版本浏览器(下)
  9. “野火FreeRTOS教程”第9章知识点总结-空闲任务与阻塞延时
  10. robotframework-selenium2library-导入可选参数
  11. 四大金融资产管理公司的起起伏伏
  12. 计算机复制功能快捷键,电脑复制快捷键是什么(全部复制粘贴的快捷键是什么)...
  13. Email,电子邮箱免费注册流程
  14. 【Qt开发】编译时报“undefined reference to“问题的解决方案
  15. 【数据结构】顺序表实现超详解(保姆级教程)
  16. “麦田音乐节·超时空歌会”即将破空 探索元宇宙虚拟演唱会新形式
  17. 苏宁搭台品牌唱戏,净水市场将变天
  18. 一个流和百亿级的表的join
  19. 解决VMware中centos 7虚拟机,主ip地址:网络信息不可用。
  20. 支持OneNote for Window10代码高亮工具

热门文章

  1. 今天一个大龄同事被辞退了,顿时让我思绪万千。程序员32岁是一个坎,大龄程序员的出路到底在哪?
  2. 聊下Android的专利许可和商标
  3. RTU、FTU、DTU、TTU都是什么鬼?
  4. 台式计算机硬盘能扩大吗,电脑怎么增加磁盘内存
  5. 微信小程序 slot插槽基本使用
  6. 桥接模式ping不通主机和外网
  7. python音频处理库librosa基本操作
  8. 28岁想入行软件测试,可行吗?
  9. C语言编周期100ms的方波信号,单片机系统设计 - 2020学年春(邸志刚)-中国大学mooc-题库零氪...
  10. 用音响里的零件做迷你小机器人_用这些磁性“积木”,搭个迷你机器人|这个设计了不起...