我们如何阻止临时解决方案永远持续下去?
说一个问题有两种可能的解决方案:第一种是快速但笨拙的。第二种是可取的,但实施起来将需要更长的时间。我们需要快速解决问题,因此我们决定尽快将hack部署到位,然后计划着手开发更好的解决方案。问题是,一旦问题得到缓解,它就会急剧下降到待办事项清单中。我们仍然计划在某个时候提供更好的解决方案,但是很难证明现在就实施它是合理的。突然之间,我们发现使用了效果不理想的解决方案已经花费了五年时间,并且诅咒了一段时间。
这听起来很熟悉吗?我知道在我工作的地方不止一次。一位同事描述了故意制作不良的GUI,以防止长期使用它的偶然性。我们有更好的策略吗?
解决方案
在进行截止日期驱动的工作时,这是一个主要问题。我发现添加了有关为什么选择这种方式的非常详细的注释,以及有关如何进行编码的一些提示。这样,人们在查看代码时就可以看到并保持最新状态。
另一个可行的选择是在跟踪框架中添加一个bug.feature(我们确实有一个吧,对不对?),详细说明返工。这样一来,它就可见了,并可能在某个时候迫使该问题出现。
这是一个很难的电话。我个人做过一些骇客活动,有时我们不得不将产品推销到客户手中。但是,我照顾它的方法就是做到这一点。
告诉项目负责人,老板或者客户:有一些地方需要清理,并进行更好的编码。我需要一周的时间来做,现在要花更少的钱,然后从现在起六个月后,当我们需要在子系统上实现扩展时。
- 我确保在短期解决方案进入之后,我特别对长期解决方案的优先级发表意见。
- 我详细说明了为什么它是hack而不是长期解决方案的原因,并利用这些原因来使利益相关者(经理,客户等)了解为什么需要对其进行修复
- 根据情况,我什至可能会在其中注入一些最坏情况的恐惧。 "如果这条线安全地折断,整个桥可能会倒塌!"
- 我负责提出长期解决方案,并确保已部署该解决方案
策略1(几乎从未选择过):不要实施合并。甚至不要让人们知道这是可能的。第一次就用正确的方法做。就像我说的那样,由于时间限制,几乎从未选择过此选项。
策略2(不诚实):说谎和欺骗。告诉管理人员黑客中存在错误,以后可能会引起重大问题。不幸的是,在大多数情况下,管理人员只是说要等到漏洞成为问题之后,再修复漏洞。
策略2a:与策略2相同,但确实存在错误。但是,同样的问题。
策略3(以及我个人最喜欢的策略):尽可能设计解决方案,并做得足够好,以至于实习生或者代码猴子都可以做到。证明花少量的代码猴子钱比证明自己的薪水要容易,因此它可能就完成了。
策略4:等待重写。继续等待。迟早(可能稍后),某人将不得不重写该东西。那时也可以这样做。
通常,此类问题是由于与管理层或者客户的沟通不畅而引起的。如果该解决方案适用于客户,则他们认为没有理由要求对其进行更改。因此,需要事先告知他们要进行的权衡,以便他们在实施快速解决方案后可以计划额外的时间来解决问题。
如何解决它取决于它为什么是一个不好的解决方案。如果解决方案不好,因为很难更改或者维护,那么第一次我们必须进行维护并且有更多时间,那么这是升级到更好解决方案的正确时机。在这种情况下,如果我们告诉客户或者老板我们首先选择了一条捷径,将很有帮助。这样,他们知道他们不能期望下一次快速解决方案。弄乱用户界面可能是确保客户回来修复问题的好方法。
如果由于风险或者不稳定而导致解决方案不好,那么我们确实需要与进行规划的人员交谈,并计划一些时间来尽快解决问题。
好问题。这也使我很烦恼,而且在大多数情况下,我是唯一负责在自己的项目(是的,小型企业)中确定问题优先级的人。
我发现需要解决的问题通常只是问题的一部分。 IOW,需要紧急修复的客户并不需要解决整个问题,只是部分问题变得更大或者更小。有时,这使我能够创建一种解决方案,该解决方案不是完整问题的解决方案,而仅仅是客户的子集,并且可以让我在问题跟踪器中保留更大的问题。
当然,这可能根本不适用于工作环境:(
我们不做临时解决方案。
有时我认为程序员只需要被告知这一点。
对此感到抱歉,但很严重-hacky解决方案毫无价值,即使在第一次迭代时,也可能比正确完成一部分解决方案花费的时间更长。
请停止给我废话代码进行维护。只要总是正确就可以了。无论花多长时间,谁会对你大吼大叫。
当我们坐在那里,在其他人都在调试他们愚蠢的骇客的早期交付之后,不停地抽签,我们将感谢我。
即使我们不认为自己是一个优秀的程序员,也要始终努力做到最好,绝不要走捷径-这样做不会花费我们任何时间。如果我们不相信我,我可以证明这一说法是正确的。
Suddenly you find you've spent five years using the less-than-perfect solution, cursing it the while.
如果我们要诅咒它,为什么它在TODO列表的底部?
- 如果它不影响我们,我们为什么要诅咒它?
- 如果它影响到我们,那么这是一个问题,需要立即解决。
我尝试构建该hacky解决方案,以便可以尽可能轻松地将其长期迁移。假设我们有一个正在SQL Server cuz中建立数据库的家伙,这是他最强大的数据库,但是公司标准是Oracle。使用尽可能少的不可转让功能(例如Bit数据类型)来构建数据库。在此示例中,不难避免使用位类型,但是这使以后的转换更加容易。
唯一可以证明修复这些问题的理由(因为它们并没有真正损坏,只是丑陋)是在我们拥有涉及相同代码部分的另一功能或者错误修复时,我们也可以重新编写它。
我们必须对开发人员的时间成本进行数学计算。如果满足软件需求,并且唯一的错误是代码在幕后令人尴尬,那么就不值得修复。
整个公司都可能倒闭,因为过分热心的工程师每年都会在遇到麻烦时坚持重新架构。
如果它没有错误,并且满足要求,那么就完成了。装运它。继续。
[编辑]
当然,我并不是要提倡所有时间都被黑客入侵。我们必须在开发过程的正常过程中仔细设计和编写代码。但是,当我们遇到需要快速完成的黑客攻击时,我们必须进行成本效益分析,以清理代码是否值得。如果在应用程序的整个生命周期中,我们花费在纠缠的hack上的编码时间要多于修复该代码的时间,那么当然要修复它。但是,如果不这样做,那么仅仅因为查看源代码会使我们生病,那么重新编码一个可以正常运行的,没有错误的应用程序将非常昂贵且冒险。
这是一篇有关技术债务的相关文章。
基本上,这类似于我们做出的所有技术决策的债务。有好债,也有坏债……我们必须选择可以以最少的长期成本实现我们想要的目标的债务。
最糟糕的债务是小而少的积累快捷方式,类似于信用卡债务……每个人都没有受到伤害,但是很快我们就进入了贫困之家。
祝你好运。以我的经验,这几乎是不可能实现的。
一旦由于压力而走上了实施骇客的湿滑之路,那么我们很可能会习惯于一直使用它。无论内部实施多么糟糕,几乎都没有足够的时间来对已经可以正常工作的东西进行重新设计。是什么让我们认为我们会"在以后的某个时间"神奇地有更多的时间来修复该hack?
我能想到的唯一例外是该黑客行为是否完全阻止我们实现客户所需的另一项功能。然后,我们别无选择,只能进行返工。
一旦制作了一个我们不希望保留的短期演示版,我们就必须这样做。客户希望在winTel盒子上安装它,因此我们在SGI / XWindows中开发了原型。 (我们都精通两种语言,所以这不是问题)。
编写一个测试案例,说明黑客失败。
如果我们无法编写一个测试失败的测试,则说明该黑客毕竟没有问题,或者测试框架不够完善。如果是前者,请先逃之before,然后再浪费不必要的优化工作。如果是后者,则寻求另一种方法(标记黑客或者测试...)
告白:
为了从其他代码层读取数据,我在C ++中使用了" #define private public"。它以hack的形式出现,但是效果很好,而且修复它从未成为优先事项。现在是3年后...
无法删除黑客的主要原因之一是在修复黑客时引入新错误的风险。 (特别是在处理TDD之前的代码库时。)
这使我想起了" CTool"的故事。最初,我们的一位开发人员提出了CTool,我称他为Don,这是解决我们遇到的问题的一种可能方法。作为认真工作的类型,Don随身携带了一个工作原型。你知道我要去哪里。一夜之间,CTool成为了整个公司部门工作的一部分。在第二天或者第三天,关于CTool缺点的抱怨开始泛滥。用户质疑唐的能力,承诺和智商。 Don的抗议从未被认为是生产应用程序,对此却置若de闻。这种情况持续了好几年。最终,在Don离开后很久就有人来重写应用程序。到了这个时候,名称CTool变得非常讨厌,以至于无法将CTool版本2命名为CTool。甚至为CTool举行了一场正式的葬礼,有点让人联想到Office Space中的复印机(或者它是打印机?)执行现场。
有人可能会说Don应该配上吊带和箭头,因为它不能正确地修复CTool。我唯一要说的是,在现实世界中说不应该破解解决方案可能是不合理的。但是,如果我们是这样做的人,请谨慎行事。
我的答案与其他答案略有不同。我的经验是,以下做法可保持敏捷,并从hackey的第一个迭代/ alpha解决方案过渡到Beta / production就绪状态:
- 测试驱动开发
- 小单元重构
- 持续整合
- 良好的配置管理
- 敏捷数据库技术/数据库重构
而且,不用说我们必须获得利益相关者的支持才能正确地完成这些任务。但是,有了这些产品,我们便拥有正确的工具和流程,可以自信地以主要方式快速更改产品。有时,变更能力是我们管理变更风险的能力,从开发的角度来看,这些工具/技术为我们提供了更坚实的基础。
- 以书面形式获得(电子邮件)。因此,当出现问题时,以后的管理人员就不会"忘记"它应该是暂时的。
- 使它对用户可见。危机越发明显,人们越不会忘记回去并以正确的方式去做。
- 在临时解决方案针对项目,资源和时间线到位之前就进行谈判,以获取真正的解决方案。真正的解决方案的工作可能应该在临时解决方案完成后立即开始。
教育负责最终决策的人,从长远来看,为什么这种行事方式不好是坏事。
- 用它们可以涉及的术语来描述问题。
- 包括成本,生产率和收入曲线图。
- 教他们关于技术债务的知识。
- 定期重构,如果我们被推向前进。
- 切勿在非技术人员面前称其为"重构"或者"返回并清理"。取而代之的是将其称为"适应"系统以处理"新功能"。
基本上,不了解软件的人不会得到重新审视已经起作用的东西的概念。从他们的角度来看,开发人员就像是机械师,他们希望每次有人想要添加功能时都要拆开并重新组装整个汽车,这听起来很疯狂。
它有助于对日常事物进行类比。向他们解释,当我们制定临时解决方案时,如何做出适合快速构建而不是稳定,可维护等的选择。这就像选择木头而不是钢来构建,因为木材更容易切割,我们可以更快地构建临时解决方案。但是,木材根本无法支撑20层建筑的基础。
我们针对自己的"修复程序"提交了第二个非常有描述性的错误,并在受影响的区域中添加了待办事项注释,指出"该区域需要大量工作。请参见缺陷#555"(当然请使用正确的数字) 。那些说"别客气"的人似乎并不理解这个问题。假设我们有一个现在需要启动并运行的系统,非骇客解决方案需要8天的工作时间,骇客需要38分钟的工作时间,这种骇客可以让我们有时间进行工作,而不会浪费金钱你在做。
现在,我们仍然必须让客户或者管理层同意安排真正修复所需的N * 100分钟时间,而不是现在修复它所需的N分钟。如果我们必须在达成此类协议之前拒绝实施hack,那么也许这就是我们必须要做的,但是我在这方面已经与一些了解的人一起工作。
引入快速修复的实际价格是,当其他人需要引入第二快速修复时,他们将根据我们自己的快速修复进行介绍。因此,快速修复的时间越长,它就越牢固。通常,骇客只比做正确的事情花一点时间,直到遇到第二个以第一个为基础的骇客为止。
因此,显然有时(或者似乎是)有时需要引入快速修复方法。
假设版本控制支持它,一种可能的解决方案是每当我们进行此类黑客攻击时,从源头引入一个fork。如果鼓励人们避免在这些特殊的"完成任务"分支中编写新功能,那么与将新功能与分支进行集成相比,最终的工作要比摆脱黑手多。但是,"好"叉子更有可能被丢弃。而且,如果我们离发行版还很遥远,那么制作这样的fork将不切实际(因为不值得进行上述的双重集成),那么我们可能甚至不应该使用hack。
一个非常理想的方法。
一个更现实的解决方案是将程序划分为尽可能多的正交组件,并偶尔对某些组件进行完全重写。
一个更好的问题是,为什么hacky解决方案不好。如果由于降低灵活性而不好,请忽略它,直到需要灵活性为止。如果由于影响程序行为而造成不良影响,请忽略它,最终它将成为一个错误修复程序并将得到解决。如果由于外观难看而不好,则只要将hack本地化即可将其忽略。
我过去见过的一些解决方案:
- 在代码中用注释" HACK"标记它(或者类似的方案,例如" XXX")
- 运行自动报告,并每周通过电子邮件发送给关心的人,这会计算" HACK"评论出现的次数
- 在错误跟踪系统中添加一个新条目,其中包含行号和正确解决方案的说明(这样,在编写hack之前从研究中获得的知识不会丢失)
- 编写一个测试用例,以演示黑客如何失败(如果可能),并将其检入相应的测试套件(即,以便引发一些错误,最终有人希望清除该错误)
- 一旦安装了hack并且压力减轻了,请立即开始使用正确的解决方案
这是一个很好的问题。随着越来越多的经验,我注意到的一件事:骇客为我们节省了很短的时间,而且常常使我们付出更多的时间。紧密相关的是"快速修复",它解决了我们认为的问题-只是在发现问题根本不是问题时才发现。
搁置关于是否应该执行此操作的辩论,让我们假设我们必须执行此操作。现在的诀窍是以使远程影响最小化的方式进行操作,以后很容易将其删除,并使其自身成为麻烦,因此我们记得要修复它。
令人讨厌的部分很容易:每次执行kudge时都发出警告。
撕开的部分很容易:我喜欢这样做是将kudget放在子例程名称的后面。由于我们划分了代码,因此更新起来更容易。当我们获得永久性解决方案时,我们可以在子例程中实施该解决方案,也可以成为无操作人员。有时,子类也可以很好地工作。但是,不要让其他人依赖快速解决方案。不看情况就很难推荐任何特定的技术。
如果代码的其余部分不错,则最小化远距离影响应该很容易。始终通过发布的界面,依此类推。
尝试向企业人员明确黑客的损失。然后他们可以以任何一种方式做出明智的决定。
我们使用Java和Hudson进行持续集成。 "临时解决方案"必须带有以下注释:
// TODO: Better solution required.
每次Hudson运行构建时,它都会提供每个TODO项目的报告,这样我们就可以获得需要改进的所有未完成项目的最新,高度可见的记录。
我们可能故意以过于严格和单目的目的编写它,并且需要进行重写才能进行修改。