过度工程的具体症状
最近我发现自己能够解释我写给我公司喜欢雇用的两位候选人的(内部)申请,以协助维护和添加次要function。
这是我写的第一个“制作”应用程序,它有45k的LOC,我花了将近两年的“独立”开发。 我还很年轻(18),并从头开始编写应用程序,同时作为离开公司的前开发人员的替身。 在devise这种尺寸的应用程序方面没有经验,我尝试使用常见的体系结构和devise模式。
今天,我知道我已经做了一些严重的过度工程,例如,使用断开连接的变更跟踪架构,而不是工作单元模式,所select的ORM已经实现了。 我可能永远不必去“真正的”三层。
两位候选人都有10年以上的内部应用开发背景和相关平台。 虽然年龄只有他们的一半,没有经验,但我尊重他们的意见。 当我向他们解释应用程序架构时,评论的内容大致如下:
- 呃,没有人会付钱给我做这样的事情,我必须把事情做完
- 坚持框架做什么,不要使用花哨的图书馆/技术
- 不要包装框架代码。 在团队中,每个人都会自己写封装代码。
- 你正在使用.NET 3.5? 那么,我们正在使用2.0。
- 那LINQ的东西给我买什么? 所有这些查询组合和投影似乎太复杂了。
现在我在问自己:
我是一名build筑宇航员吗? 我怎么知道我在build筑方面太过分了? 什么是过度工程的常见症状?
什么是过度工程的常见症状?
解决你没有的问题的代码。
过度工程的一个非常强有力的警告标志是,当所有事情都经历了如此多的间接性,很难find实际上实现了一些具体的领域级function的代码段。 如果你发现你的大部分函数只做很less的具体工作,只是调用其他的虚函数,你可能会遇到一些问题。
无聊
无聊是过度devise的代码的好前兆。 我承认,当我第一份工作的时候,我感到如此不足。 我只是无聊。 当我感到无聊的时候,我写了代码。 不只是任何代码 – 代码的通用。
没有人认真,我把自己的代码和抽象看作是金色尖顶的大塔,玻璃玛瑙的悬空拱形,拱形圆顶支撑的美妙的拱顶,上面有美丽的几何图案等等。
看到这些模式为我自己一起工作真的很吸引人,但回想起来,我完全为我留下的不负责任的混乱感到羞愧。
如果你正在编写自己的框架和DSL代码,以消除工作中刺激性较小的时间,就停下来。 花时间阅读Wards Wiki ,或者写一本开源书 ,或者你可能只想要求pipe理层做更多的工作。
至于如果你是一个build筑宇航员的问题:如果你意识到危险,让你领先很多人。 你也不想走你牛仔的路,听起来好像他们中的一些人已经变成了一个老旧的wh </s>。
过度工程是一个优先级问题的结果,导致系统的某些部分得到太多的关注。 所以过度工程的最明显的症状是,你可以看到系统的其他部分因缺乏关注而受到伤害。
(还有一种倾向,即过度devise使系统暴露于不良devise风险的增加,这是因为增加了复杂性以及决定过度devise哪些方面所涉及的错误倾向的数量,但正如评论指出的那样,不会自动跟随。)
对于大多数内部业务应用程序,大部分代码应该关注实现业务问题,而不是与业务无关的技术问题(如“断开的变更跟踪架构”)。 目前可用的框架相当成熟,支持最常见的用例。 如果你正在发明新技术或(在商业应用程序开发的背景下),只是为了包装而包装一些其他现有的框架或库,你可能是做错了。 理想情况下,您构build的每一个架构都应该追溯到一些业务需求。 把事情简单化。
编写你自己的框架
可能性是,有人已经做到了。 更重要的是,他们已经做到了1000倍,比以往任何时候都要好。 更重要的是,无论他们做了什么,可能已经是一个行业标准,所以学习这项技术将会使你在其他工作上更具竞争力。
在我工作的最后一个公司,一个程序员在他任职期间的大部分工作中独自一人工作。 他写了一个公司更stream行的应用程序,被广泛认为是最好的团队 – 但在我看来,他有一个从头开始写所需的一切恶习。
他编写了自己的dependency injection框架,他自己的ORM,一个unit testing框架(这令人难以置信地看起来和NUnit非常相似 – 为什么他不使用NUnit?),一个创build工厂对象的框架(a “ 工厂 ”我会打电话)。
请注意,代码实际上是非凡的,但是有什么意义呢?
编写一个更好的核心库
在我现在的公司里,似乎程序员总是编写无用的代码来复制已经存在于.NET框架中的function。
除其他外,他们写道:
- ASP.NET webforms中用于表单身份validation的活动目录框架 – 由于ASP.NET内置了此function,因此无法解释。
- 可热插拔的主题和网站皮肤 – 也是莫名其妙的,因为代码function不如内置ASP.NET主题,需要1000%以上的膨胀。
- 他们莫名其妙地编写了自己的types化的数据集和数据适配器。 这些对象提供的function比VS自动生成的types化数据集要less,同时需要比NHibernate域对象更多的样板代码。
要么他们不了解这个框架,要么他们认为这个框架是不合适的。
关于重新实现的库比原来更好的地方,我只能想到很less的例子(请参阅Jane Street Core Library , .NET的C5generics集合 , 一个真正的货币类 ),但可能性不大,你不会写一个更好的标准库。
回想起那些过度devise的东西时,避免使用YAGNI , DRY和KISS 。 如果有很多部分似乎是部分完成的,而且许多部分代码似乎有一个“如果发生了什么?如果发生了什么呢?” 感觉到,这将是另一个点。 忽略OOdevise或SOLID原则的 良好原则将是另一个需要注意的。 如果你认为自己已经写出了完美的代码,这将是另一个麻烦的迹象,因为任何人写出一些无法以某种方式改进的东西是极其罕见的。
海事组织,注意有些人可能过分批评你的工作,因为任何代码基础的一部分可能涉及人们喜欢的东西以某种方式,例如命名方法,testing和variables的约定。 就是那样子。 现在,你可能要弄清楚的是,如何处理冲突或说服/影响等突发事件的人,哪里有工具可以帮助。
当一个同事的电脑飞过你的脑袋,因为他们已经花了最后6个小时尝试,并没有使用你的荒谬的框架出现一个惊人的对话框,这是一个非常具体的症状就在那里。
恕我直言,大多数关于你的应用程序的评论并不是真正的过度工程,因为过度工程不是关于技术。 这是关于build筑。 新技术可以在合理的时间内学习和理解。 了解过度devise的应用程序通常要困难得多,有时甚至是不可能的。 这使点2,4和5无效。 第一点并不是真的有效,因为你明显得到了编写应用程序的支付,如果它有效,你在这里没有任何问题。
这是我的“快速testing”,以查明应用程序是否过度devise:
- “一切”包装器:包装是有用的,但它很容易过分。 检查你是否只包裹真正需要包装的东西。 (我基本上包装了自己的包装,我知道我在说什么;-)。)
- 重塑车轮:经典。 这是很常见的,你已经提到过。 你有没有实现一些function,因为你需要你想要 ? 你的框架是做什么其他可用的库不?
- “感觉”过度devise:这是最重要的一点,但也是最难看的一点。 看看你的代码,看看哪个部分感觉过于复杂。 问问你的自我,如果有一个更简单的方法来实现它,为什么你没有select这种方式。 如果你没有好的答案,这个部分可能是过度devise的。
这些只是我用于我的应用程序的快速提示。 它们不能保证成为“过度工程检测”的全部和terminal。
你不是一个build筑宇航员。 LINQ相当简单,基本和有用的,一个。 .NET 3.5也一样。
同时,即使他们喜欢你所做的,你也是团队中的新手,并会受到某种束缚。
把这一切都用一粒盐。 接受他们的批评,点头,然后和他们一起喝啤酒。
如果他们要求你改变它,那么你的评论是“jeez ..我知道我做错了,但是它的工作,这将是太麻烦改变”。
为您的应用提供内在function的插件
让我们面对现实,可插拔的架构只是性感和乐趣写。 然而,这是另一个需要自问的领域,“我真的需要这样做吗?”。
如果您不需要临时增加应用程序,不要期望任何人编写第三方扩展,并且应用程序的定义范围相当明确,您不需要可插拔的体系结构。
你不应该编写插件来支持应用程序的内部function。 假设你正在写一个Paint程序, 您可能会支持插件以多种格式保存文件,但是您不需要可插入的撤消pipe理器或文件浏览对话框。
尝试评估您是否已经完成了代码的预期使用期限内的工作。 这包括维护和开发。
在代码生命周期中,请记住代码的生命与应用程序的生命是不一样的。 最好先写一个快速原型,然后在更好地理解域之后重新devise/重构。 在这种情况下,生命周期很短,所以保持简单。
另外,不同的应用需要不同的工程。 如果这个应用程序失败,这个应用程序会花费多less生命 也就是说,它是机械心脏的控制器,还是航天飞机导航火箭? 或者它是无聊青less年的联系人列表?
在更通用和高层次的方法上,
I feel that when there's a gap between the complexity of the problem and the complexity of the solution, then you have a clear case of overengineering.
有什么办法来实现这一目标? 解决你没有的问题,看到问题更加复杂,试图在未来预测太多,build立过于普通的东西,等等。
你是否在合理的时间内实施你的项目? 现在工作吗? 如果是这样的话,你可能会放松一点,因为工程上最糟糕的问题之一,实际上从来没有得到任何有用的东西。
你们所说的家伙也可能有一些好的build议,但这并不意味着你需要把他们所说的一切都当作福音。 例如,在新项目上使用最新版本的.NET并不会让我担心任何事情。 他们真的在抱怨吗?
其中一半是老男人(我在他们的联盟)坚持他们已经知道的试验和真正的蒸汽机。 另一半是真实的,但不是真的告诉你什么新的东西。
除此之外,Jeff Sternal的回答是非常好的。
作为一个专业开发软件已有24年的人,我只能说,这个问题 – 在特定问题所需的抽象层次上进行调用 – 是我日常devise和编程工作中最困难的部分。
在“旧”的时代,工程devise非常stream行,导致了所有结构化的方法和实践。 现在,看来我们已经走了另一条路。 没有简单的答案,有时你只会知道,如果你后知后觉或以后工程;)
我曾经有过多次事后的经历,我对自己说:“我希望我没有这么多复杂”,而且“如果我只听到我脑后的小声音,并使它更多一点通用在这里“。
我还没有提出打电话的绝对规则…
我怀疑我很容易过度工程,所以我把这张照片作为我的电脑壁纸
这种想要提供一个不同的观点,但我认为这个词应该被删除,取而代之的是一个更合适的描述。 这首先是“工程”的耻辱,首先是简单性,可维护性和实用性,当许多被称为“过度工程”的东西具有完全相反的特性时(就好像工程太多了,或者太重视了,应该产生最复杂的解决scheme)。 例如,看到过度工程和差的testing程序之间的相关性(至less我经常看到这两者是相辅相成的),以及工程中的哪种超额执行最less量的正式testing是很常见的?
我认为,从我经常听到软件经理和偶然的恐龙这些并不那么聪明的恐龙这个angular度来看,这个术语来自任何试图降低系统维护成本或者提出更好的testing程序的人,安全标准。 对于外行人来说,认为任何一种认真的关注声音工程,testing,而不是以短暂的快速捕捉速度生成代码都是“过度工程”。
有很多人真正认识到“过度工程”的真实气味,但这个术语至less可以说是有误导性的。
至于症状,除了已经提供的优秀performance之外,对我来说,这是能够正确评估和重新评估面前工作的盲点。 程序员可以在他们的想象中build立世界,迷恋和吸收高耸的概念。 虽然这是一个无聊的和一般的答案,这可能会导致不再看到我们的鼻子下的实际问题和优先事项的危险。 把概念化太深,就是往往在我们面前发现一个什么是对的问题,过于复杂的问题,与现实和实际的用户端需求失去联系。 对我来说,最大的症状要注意围绕心理学。
各种围绕着生产的行业都容易受到这个基本趋势的影响,因为人们对于概念(例如:电影导演)过于沉迷而忽视了实际和即时的问题。 随着程序devise的普遍化,一种对付它的方法是在把事物概念化到第n个程度之前,更早地倾向简单性,简单性,实用性,编码(在我们深深地爱上任何想法之前,让我们有更多的时间去重新评估)。 一个人抱着这些理想不可能走得太远,但是关键是要避免发展那个盲点,使我们无法正确评价自己的工作。 一个过度工程化的代码库并不是一朝一夕就能够实现的 – 在错误的方向上花费大量的精力去完成它,对我来说,最大的可预防问题并不是与远见有关,而往往是后发太迟。