本文概述
大规模重构:为什么你会那样做?
如果没有损坏, 请不要修复。
这是一个众所周知的词组, 但据我们所知, 大多数人类技术进步都是由那些决定修复未损坏问题的人取得的。特别是在软件行业, 人们可能会争辩说, 我们所做的大部分工作都是修复未损坏的问题。
修复功能, 改善UI, 提高速度和内存效率, 添加功能:所有这些活动都很容易看出它们是否值得做, 然后作为经验丰富的Rails开发人员, 我们赞成或反对在这些上花费时间。但是, 有一项活动, 大部分都属于灰色区域-标准重构, 尤其是大规模代码重构。
术语大规模重构值得解释。确切地说, “大规模”的含义会因情况的含糊而有所不同, 但我认为, 除了几个类或一个子系统之外, 影响最大的是任何事物, 并且其接口”很大”。 。”另一方面, 任何隐藏在单个类的界面后面的Rails重构肯定会”很小”。当然, 两者之间有很多灰色区域。最后, 相信你的直觉, 如果你害怕这样做, 那么它可能很大。
根据定义, 重构不会产生任何可见的功能, 无法向客户端显示任何内容, 也不会交付任何内容。充其量, 它们可能会提高速度和内存使用率, 但这不是主要目标。可能有人会说, 主要目标是使你满意的代码。但是, 由于你以某种方式重新布置代码, 从而在整个代码库中产生深远的影响, 因此有可能使所有事情陷入混乱, 并且会出现问题。当然, 这就是我们提到的恐惧之源。你是否曾经向代码库介绍过新人, 而在他们询问了一段特殊的组织代码之后, 你做出了以下回应:
Yeeeaahh, 这是当时很有意义的旧代码, 但规范已更改, 现在修复起来太昂贵了吗?
甚至你甚至都给了他们一个严肃的表情, 并告诉他们不要去碰它。
问题”我们为什么还要这么做?”是自然的, 可能和做一样重要…
问题”我们为什么还要这么做?”这是很自然的事情, 并且可能与重构过程一样重要, 因为你经常不得不说服其他人, 让你花费大量时间进行重构。因此, 让我们考虑一下你想这样做的情况以及获得的好处:
性能提升
从可维护性的角度来看, 你对代码的当前组织感到满意, 但仍然会导致性能问题。优化当前设置的方式太困难了, 更改将非常脆弱。
这里只有一件事要做, 那就是对其进行广泛地描述。运行基准测试并估算你将获得多少, 然后尝试估算它将如何转化为具体收益。有时你甚至可能意识到建议的代码重构不值得。有时, 你将获得大量冷硬数据来支持你的案件。
架构上的改进
架构可能还不错, 但有些过时了, 或者每次接触代码库的那部分内容时都会感到畏缩。它运行良好且快速, 但是添加新功能很麻烦。痛苦在于重构的商业价值。 “痛苦”还意味着重构过程将需要更长的时间才能添加新功能, 甚至可能更长。
这是有好处的。在有或没有建议的大型重构的情况下, 估算一些示例功能的成本/收益。解释类似的差异将应用于大多数即将到来的功能, 这些功能现在和将来在开发系统时永远涉及系统的该部分。你的估计可能是错误的, 因为它们经常在软件开发中使用, 但是它们的比率可能会达到标准。
使其更新
有时, 代码最初编写得很好。你对此感到非常满意。它速度快, 存储效率高, 可维护且与规格保持一致。原来。但是随后规格发生变化, 业务目标发生变化, 或者你了解到有关最终用户的新知识, 从而使你的最初假设无效。该代码仍然可以很好地运行, 并且你仍然对此感到满意, 但是当你在最终产品的上下文中查看它时, 会有些尴尬。事物放置在稍微错误的子系统上, 或者属性放在错误的类中, 或者某些名称不再有意义。现在, 他们正在履行完全不同的业务术语。但是, 要证明任何种类的大规模Rails重构仍然是非常困难的, 因为所涉及的工作将与其他任何示例一起进行, 但是收益远没有那么明显。当你考虑它时, 维护它并不难。你只需要记住, 有些事情实际上是另外一些事情。你只需要记住, A实际上意味着B, 而A上的属性Y实际上与C有关。
这才是真正的好处。在神经心理学领域, 有许多实验表明我们的短期记忆或工作记忆只能容纳7 +/- 2个元素, 其中之一就是Sternberg实验。当我们学习一门学科时, 我们从基本要素开始, 最初, 当我们考虑更高层次的概念时, 我们必须考虑它们的定义。例如, 考虑一个简单的术语”盐腌的SHA256密码”。最初, 我们必须在工作内存中保留” saltted”和” SHA256″的定义, 甚至可能是”哈希函数”的定义。但是, 一旦我们完全理解了该术语, 它就只占用了一个内存插槽, 因为我们直观地理解了它。这就是为什么我们需要完全理解较低级别的概念以便能够推理出较高级别的概念的原因之一。对于我们项目的特定术语和定义也是如此。但是, 如果每次我们讨论代码时都必须记住翻译的真实含义, 那么该翻译将占据这些宝贵的工作内存插槽中的另一个。它会产生认知负担, 并使我们的代码逻辑难以推理。反过来, 如果更难推理, 则意味着我们更有可能忽略重点并引入错误。
并且不要忘记更明显的副作用。与我们的客户或只熟悉正确业务术语的任何人讨论变更时, 很可能会感到困惑。加入团队的新人们必须熟悉业务术语以及代码中的对应术语。
我认为这些原因非常引人注目, 并且在许多情况下证明了重构成本的合理性。仍然要小心, 在很多情况下, 你必须使用最佳判断来确定何时以及如何进行重构。
最终, 由于我们许多人喜欢开始一个新项目的相同原因, 大规模重构是好的。你查看该空白源文件, 一个崭新的世界开始在你的脑海中旋转。这次你会做对, 代码将优雅, 布局精美, 快速, 健壮且易于扩展, 最重要的是每天都可以工作。无论大小, 重构都可以使你重新获得那种感觉, 将新的生命注入旧的代码库中并偿还技术债务。
最后, 最好是由计划驱动重构, 以使其更易于实现某些新功能。在这种情况下, 重构将更加集中, 并且通过更快地实现功能本身, 还可以立即获得很多重构所花费的时间。
准备
确保你可能接触的代码库的所有区域的测试覆盖面都很好。如果你发现某些部分的覆盖范围不够, 请先花一些时间来提高测试范围。如果你根本没有测试, 则应首先花时间创建这些测试。如果你无法创建适当的测试套件, 请专注于验收测试并编写尽可能多的内容, 并确保在重构时编写单元测试。从理论上讲, 你可以在没有良好的测试覆盖率的情况下进行代码重构, 但是这将需要你进行大量的手动测试, 并且需要经常进行。这将花费更长的时间, 并且更容易出错。最终, 如果你的测试覆盖范围不够好, 那么执行大规模Rails重构的成本可能会很高, 令人遗憾的是, 你应该考虑完全不这样做。在我看来, 这是自动化测试的一个好处, 而人们对其强调不够。自动化测试使你可以经常且更重要地进行重构, 以大胆地进行重构。
确定你的测试覆盖面良好后, 就该开始进行更改映射了。首先, 你不应该进行任何编码。你需要大致规划出所有涉及的变化, 并通过代码库跟踪所有后果, 并将有关所有这些的知识载入你的脑海。你的目标是确切了解为什么要更改某些内容及其在代码库中的作用。如果你偶然发现这些变化仅仅是因为它们看起来像需要更改, 或者是因为某些东西坏了并且似乎可以解决问题, 那么你可能会陷入一条死胡同。新代码似乎可以正常运行, 但是不正确, 现在你甚至都记不起所做的所有更改。在这一点上, 你可能需要放弃在大规模代码重构上所做的工作, 并且实际上浪费了你的时间。因此, 请花点时间浏览代码, 以了解将要进行的每个更改的后果。最后它将付出可观的回报。
在重构过程中需要帮助。你可能会喜欢别的东西, 但我喜欢简单的白纸和一支笔。我首先在纸张的左上角写要进行的初始更改。然后, 我开始寻找受变更影响的所有地点, 并在初始变更下将它们写下来。在这里使用你的判断很重要。最终, 纸上的笔记和图表都在那里, 请自己选择一种最适合你的记忆的风格。我写了简短的代码片段, 在其下方带有项目符号点, 并带有许多箭头, 这些箭头指向其他此类注释, 指示直接或间接依赖于它的内容。我还用速记符号注释了箭头, 以提醒我在代码库中注意到的某些特定内容。请记住, 在接下来的几天中, 当你执行在其中计划的更改时, 你只会回到那些笔记上, 完全可以使用非常简短的密码提示, 这样它们会占用较少的空间, 并且更易于在纸张上进行布局。在Rails重构几个月后, 我几次打扫办公桌, 结果发现其中一张。那简直是胡言乱语, 我完全不知道那张纸上的意思是什么, 除了它可能是由疯了的人写出来的。但是我知道在我解决这个问题时纸是必不可少的。另外, 不要认为你需要写出每一个更改。你可以将它们分组并以其他方式跟踪详细信息。例如, 在你的主要论文中, 你可以注意到你需要”将所有出现的A.b重命名为C.d”, 然后可以通过几种不同的方式跟踪具体情况。你可以将它们全部写在一张单独的纸上, 可以计划再次对它的所有出现进行全局搜索, 或者可以简单地将所有需要更改的源文件保留在所选编辑器中并完成所有更改的映射后, 请记住一下。
当你确定初始更改的后果时, 由于其性质是大规模的, 因此你很可能会确定其他更改, 这些更改本身会带来进一步的后果。还要对它们重复分析, 并注意所有相关的更改。根据更改的大小, 你可以将它们写在同一张纸上, 也可以选择一张新的空白纸。映射更改时要尝试做的一个非常重要的事情是尝试确定可以实际停止分支更改的边界。你希望将重构限制为最小的, 合理的, 四舍五入的更改集。如果你看到一个可以停下来停下来的地方, 那么即使你认为应该对其进行重构, 也可以这样做, 即使它在概念上与你的其他更改有关。完成这一轮代码重构, 彻底测试, 部署并返回更多内容。你应该积极寻找这些要点, 以使更改的大小可管理。当然, 一如既往地做出判断。通常, 我可以通过添加一些代理类来做一些接口转换来缩短重构过程。我什至在意识到要完成大量工作的同时, 甚至开始实施它们, 只要将重构推到了”自然停止”(即几乎不需要代理代码)的地步即可。然后, 我回溯, 还原了上一次更改并进行了重构。如果这听起来有点像绘制未知领域, 那是因为我感觉是这样, 除了区域地图只是二维的。
执行
完成重构准备后, 就可以执行该计划了。确保专心致志, 并确保无干扰的环境。在这一点上, 我有时甚至会完全转向互联网连接。问题是, 如果你准备得很好, 旁边的纸上就会有一系列好的笔记, 你的注意力会增强!你通常可以通过这种方式非常快地进行更改。从理论上讲, 大部分工作是在准备过程中事先完成的。
实际重构代码后, 请注意一些奇怪的代码, 这些代码会执行非常具体的操作, 可能看起来像是不好的代码。也许它们是错误的代码, 但实际上, 它们实际上是在处理调查生产中的错误时发现的一个奇怪的极端情况。随着时间的流逝, 大多数Rails代码都在处理奇怪的案例错误, 例如”头发”或”疣”, 例如, IE6可能需要一个奇怪的响应代码, 或者其中的条件会处理一个奇怪的计时错误。它们对于全局并不重要, 但仍然是重要的细节。理想情况下, 如果不首先尝试覆盖它们, 则将它们明确地包含在单元测试中。我曾经负责将一个中型应用程序从Rails 2移植到Rails3。我对代码非常熟悉, 但是有点混乱, 并且要考虑很多更改, 所以我选择了重新实现。实际上, 这并不是真正的重新实现, 因为这几乎从来都不是明智之举, 但是我从一个空白的Rails 3应用开始, 然后将旧应用的垂直部分重构为新应用, 大致使用了所描述的过程。每次完成垂直切片时, 我都会浏览旧的Rails代码, 查看每一行并仔细检查新代码中是否包含它。从本质上讲, 我是在选择所有旧代码”头发”并将它们复制到新代码库中。最后, 新的代码库解决了所有极端情况。
确保经常进行手动测试。这将迫使你在重构过程中寻找自然的”中断”, 这将使你能够测试系统的一部分, 并使你确信自己没有破坏过程中未曾预期破坏的任何内容。
本文总结
重构Rails代码后, 请确保最后一次检查所有更改。查看整个差异并进行遍历。通常, 你会在重构开始时注意到一些你错过的细微事情, 因为你现在不了解。大规模重构有一个很好的好处:你可以清楚地看到代码组织的形象, 尤其是当你最初没有编写代码时。
如果可能的话, 也请一位同事对其进行审查。他甚至不必特别熟悉代码库的确切部分, 但他应该对项目及其代码有大致的了解。对更改有全新的了解会很有帮助。如果你绝对不能邀请其他开发人员来研究它们, 那么你就必须假装自己一个人。睡个好觉, 并焕然一新。
如果你缺少质量检查人员, 则也必须戴上那顶帽子。同样, 稍事休息一下, 与代码保持距离, 然后返回执行手动测试。你刚刚经历了进入杂乱无章的电气配线柜, 并用一堆工具整理一下, 可能会切割和重新接线的过程, 因此比平时需要多加注意。
最后, 考虑所有计划中的变更, 享受劳动成果, 这些变更现在将变得更加干净, 易于实施。
什么时候不做?
尽管定期执行大规模重构有很多好处, 以保持项目代码的新鲜和高质量, 但它仍然是一项非常昂贵的操作。在某些情况下, 不建议这样做:
你的考试覆盖率很差
如前所述:非常差的测试覆盖范围可能是个大问题。使用你自己的判断, 但是在短期内将重点放在提高覆盖率上, 同时开发新功能并执行尽可能多的本地化小规模重构可能会更好。一旦你决定尝试并对代码库的较大部分进行排序, 这将对你有很大帮助。
重构不是由新功能驱动的, 并且代码库已经很长时间没有更改
我使用过去时, 而不是故意说”代码库不会改变”。从经验来看(根据经验, 我的意思是多次犯错), 你几乎永远无法依赖于何时需要更改代码库某些部分的预测。因此, 要做第二件事:看过去, 并假设过去会重演。如果很长一段时间没有更改任何内容, 那么你现在可能不需要更改它。等待这种变化出现, 然后再进行其他工作。
你时间紧迫
维护是项目生命周期中最昂贵的部分, 而重构使其成本更低。任何企业绝对必须使用重构来减少技术债务, 以使将来的维护成本降低。否则, 就有进入恶性循环的危险, 在这种恶性循环中添加新功能变得越来越昂贵。我希望这很糟糕, 这是不言而喻的。
也就是说, 大规模重构在需要多长时间的情况下是非常不可预测的, 因此你不应该半途而废。如果出于种种内部或外部原因, 你因时间紧迫而不确定自己是否能够在该时间范围内完成工作, 那么你可能需要放弃重构。压力和压力, 尤其是时间引起的压力和压力会导致浓度降低, 这对于大规模重构是绝对必要的。努力从你的团队那里获得更多的”支持”, 以预留时间, 并在有时间的情况下查看日历。不必是连续的时间段。当然, 你还需要解决其他问题, 但是这些休息时间不应超过一两天。如果是这样, 你将不得不提醒自己自己的计划, 因为你将开始忘记对代码库的了解以及停止的确切位置。
总结
我希望我给了你一些有用的指导, 并说服了你在某些情况下执行大规模重构的好处, 并且我敢说。主题很模糊, 当然这里没有说什么是绝对的事实, 具体情况因项目而异。我尝试提供建议, 我认为这通常是适用的, 但与往常一样, 请考虑你的特定情况并利用你自己的经验来适应其特定挑战。祝你好运!
评论前必须登录!
注册