我发现自己一直在打破这种模式。
YAGNI - 你不需要它
我只是一名初级开发人员,但我发现即使是高级开发人员也在做同样的事情。
“好吧,这个系统可能会使用它,还有这个,所以让我们为它设计吧。”
有时,我会抓住自己,但大多数时候我会狂奔。有没有人有任何坚持 YAGNI 的提示,或者我在设计和编码时可以做些什么来更好地执行这种设计模式?
我发现自己一直在打破这种模式。
YAGNI - 你不需要它
我只是一名初级开发人员,但我发现即使是高级开发人员也在做同样的事情。
“好吧,这个系统可能会使用它,还有这个,所以让我们为它设计吧。”
有时,我会抓住自己,但大多数时候我会狂奔。有没有人有任何坚持 YAGNI 的提示,或者我在设计和编码时可以做些什么来更好地执行这种设计模式?
为某事设计
...完全不同于
设计一些东西。
为某事设计意味着您正在为将来的扩展构建应用程序,以防需要编写代码(这很好……这意味着您正在使您的软件可扩展且易于维护)。
设计一些东西意味着你现在正在写整篇文章……不管你认为有人真的会使用它。这不一定是一件坏事,但它可能会浪费大量时间。
小心你在做什么。
这与完美主义有关。让它变得完美,让它为所有可能的未来场景做好准备。
在决定需要它的机会时,务实和清醒会有所帮助:做出然后回答“是”或“否”。也许是一个不。
除非有明确的证据表明需要它(它在议程上,是客户要求的),并且您认为最好在当前设计中考虑未来的功能,暂时不考虑它。
只需使用 TDD !
您很少会发现自己为不需要的功能编写测试...
因为 YAGNI 是一个原则,而不是灵丹妙药
软件开发总是要平衡许多需求。这不是把一件事做对,而是把很多事情都做错。单独的 YAGNII 不会拯救你的屁股。
从这个意义上说,YAGNI可以避免以下陷阱:
平衡相互竞争的需求是困难的。但这就是为什么——正如 McConnell 尖锐地指出的——软件开发就是工程。
因为其他原则也是人
其他原则原则 - 更基本的 IMO - 是最不意外的原则和复杂性的封装:实体的公共接口/合同应该比它的实现更简单 - 否则,正确调用函数我我必须知道的比我自己做的要多。有时,这意味着您的实施需要完成。
一个例子(也许不是一个很好的例子):
/// Estimates the time required to comlete the batch.
/// If the time cannot be estimated reliably, the return value is -1
/// \returns [double] estimated time in seconds
double EstimateDuration(Batch & batch);
是一个简单的合同。哦,
/// Estimates the time required to comlete the batch.
/// If the time cannot be estimated reliably, the return value is -1.
/// This function does not support looping batches (right now, looping
/// batches only run under a servicve account with no UI, so it's not needed).
/// Also, when the moon is full, the return value is -1, but an estimate
/// is found in batch.JeffsEstimate. This value is used only by Jeff's core
/// build script which runs roughly once a month.
/// \returns [double] estimated time in seconds
double EstimateDuration(Batch & batch);
不是合同,而是对实施的描述。(有人可能会争辩说问题是由于过度热心的 YAGNI 还是仅仅是糟糕的设计 - 但也许那是因为你完全是 YAGNI 设计的)
设计不会伤害
敏捷的出现,“设计阶段”的名声不好。然而,比根本没有计划更糟糕的是,你的计划确实是灾难性的。最大的危险不是真正糟糕的计划,而是试图预测每一个问题和变更请求。YAGNI 在这里是无价的。
他们是资深的,毕竟 我不认识他们——他们的倾向可能是由于老派的瀑布灌输和对变化的恐惧。或者他们是老年人,因为他们知道自己的工作——他们已经知道哪些部分是你现在宁愿做的,哪些部分可以为未来的不确定性而牺牲。
执行 YAGNI 非常困难,因为我认为我们中的大多数人都被相反的问题所困扰,即找到一个过于复杂或脆弱而无法重构来做我们想做的事情的系统,但如果有更多的深思熟虑,它本可以允许它. 找到中间立场可能很困难。
一般来说,如果你发现自己在想,“它可能需要 [xyz]”,那么这应该在你的代码中明确地发挥作用。即使您不编码以支持 xyz,那么您也应该以这样一种方式进行编码,即重构以添加 xyz 支持是尽可能实用的。然而,有时这可能意味着做一些比它严格需要的更通用的东西。知道沿着这条路在哪里停下来可能只有特定领域的信息和经验才能告诉你。
“返璞归真”和“简单就是好”这两个短语突然出现,提醒我只关注手头的任务,并意识到我为什么要构建此功能或增强功能,而不是过度设计或计划一百万可能不会发生的事情。在我工作的地方,我的名字经常被用来描述过度设计或过于复杂的东西,例如“你 JBed 如何构建那个页面”。我试图更多地检查这一点,因为有时它很有用,但不足以让它成为我的常见做法。
用非技术术语写出要求有时也会有所帮助。这让我知道我必须在最后展示什么,而不必担心更精细的细节,例如,系统的用户不太可能阅读我的源代码并模拟我对我们使用的任何命名约定的使用。他们关心它的工作原理,做他们需要和希望它做的事情。
另一个要问的问题是,“他们真的要求这个吗?” 并尽量减少人们对功能的假设。如果它不在列表中,则将其排除在外,尽管询问他们是否想要它。
如果你正在构建一个库/工具包/平台/框架,YAGNI 有不同的含义。
您无法确定其他开发人员将如何使用您的工具,有时灵活设计会更有意义,这样您的产品可以在更广泛的场景中使用。前向兼容性也是一个重要的考虑因素。
YAGNI 仍然适用,但“它”往往处于元特征级别,而不是特征级别。
YAGNI 确实更像是一个要问的问题。作为资深开发者,我们一直都在违反 YAGNI。这真的是一个“需要”的问题。你需要它吗?定义“需要”。我见过使用 YAGNI 教条开发的可怕的泥球。
并不是说我认为 YAGNI 没有用……总是值得问“我需要这个吗”。
好吧,我昨天也遇到了同样的事情,和一位资深开发者大吵了一架。我总是尝试用“如果有人调用这个,如果它改变 tomm 等等……怎么办?”来设计。而他是另一个极端。“让它工作并快速!”
答案介于他的方法和我的方法之间。我们如何到达中间地带?在尝试做出人们会欣赏或讨厌的“完美”设计之间,如果他们将来必须改变某些东西。
恕我直言,至少在设计模块时,答案归结为面向对象编程的基本原则,例如定义清晰的接口。接口应与客户的要求相匹配。模块的主要接口不应该有任何“解决”任何东西的东西,除了需求中的东西。至少可以删除由于“如果这种情况发生变化,他们明天需要这个等”而添加的某种程度的“装饰”。
任何你打算放的东西,因为你认为明天可能会被其他人使用等等,都必须辩论几个小时!您应该有充分的理由为目前甚至没有名字的某人添加“免费赠品”!
对于这个问题,我仍然需要一个明确的答案。也许来自一些设计过大型应用程序并面临这种情况 100 次的架构师 :)
以一种使未来功能更易于实现的方式设计您的应用程序是很好的——但在您需要之前不要真正实现这些功能。如何去做这完全取决于你正在从事的项目。
我发现同行评审和同行编程对此有所帮助。另一双质疑你推理的眼睛会很快识别出你认为你需要的东西,但实际上并非如此。
我们公司有一条经验法则:当我们讨论设计新功能时,首先要回答的问题是“谁真正想要这个?” 如果客户支持它,那么我们会尝试了解他真正想要什么,以及是否有其他方法可以解决他的问题(而不是添加新功能)。如果团队成员要求这个新功能,他应该有充分的理由为了那个原因。其中包括性能问题和营销问题,我们再次尝试完全理解请求并讨论所有可能的替代方案,然后再向我们的代码库添加一些新功能。
提醒自己你正在尝试实施什么,不要做更多。你可以这样做
如果你遵循敏捷,那么你可以处理重要的事情,直到你得到你不需要的东西。当你接触到 YAGNI 的东西时,你应该已经有了一个差不多完成的产品。然后由企业来告诉你停止开发东西。
YAGNI 通常是事后诸葛亮。只要确保你足够敏捷,可以在“YAGNI”变得明显时移除“I”。