TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

On over-engineering; finding the right balance

92 点作者 BerislavLopac9 个月前

22 条评论

cbanek9 个月前
I&#x27;ve never been bitten by an interface that is too simple. I&#x27;ve never had a bug in code that I didn&#x27;t need to write.<p>Don&#x27;t try to solve future problems. You&#x27;ll have the problem that the problem you solved isn&#x27;t the problem that needs solving, and then the problem that needs solving.<p>If anything, overly simplistic solutions sometimes make me feel like I&#x27;m repeating myself, but really since the code is so easy it always feels possible to refactor it. That&#x27;s a good place to be, much better than afraid to change something because it&#x27;s too generic to know if you&#x27;ll break something.<p>Everyone has an opinion on an interface that is easy to understand, and not over-engineered (bikeshedding).
评论 #41509119 未加载
评论 #41511188 未加载
评论 #41511208 未加载
评论 #41509601 未加载
评论 #41510026 未加载
评论 #41510661 未加载
评论 #41509250 未加载
评论 #41511167 未加载
评论 #41509738 未加载
评论 #41513918 未加载
rkangel9 个月前
The key superpower is a culture of refactoring when needed.<p>I firmly believe you should design for the problem you&#x27;ve got now, and not worry too much about the future. You&#x27;re usually wrong about it anyway! This is fine, as long as you do refactor when more requirements come along.<p>In order to do that you need a few things though: people who notice and care &quot;oh we do the same in 5 places now, I should probably pull that out to a new module&quot;, a development culture that encourages and accepts that thinking, and a QA&#x2F;delivery approach that doesn&#x27;t say &quot;oooh, that&#x27;s a bit risky, do you <i>have</i> to refactor that?&quot;.
评论 #41510839 未加载
评论 #41510817 未加载
评论 #41512082 未加载
bjornsing9 个月前
The right balance will look very different in different parts of a system. In general the lower levels of a system need to be more generic, and higher levels more specific. One often overlooked guiding principle is that when you’re building large systems it’s important to be able to “finish” some parts of the system and be “done” with them, otherwise your mental “context window” will just grow forever until not even the brightest among us can make any progress.
gwd9 个月前
The thing missing from this analysis is how hard things will be to change later.<p>If it&#x27;s an internal function called in just one or two other files maintained by you our your team, then no need to spend a lot of time thinking too hard about it -- just write it as simply as possible, and then rewrite it when you find it no longer suits your needs.<p>If it&#x27;s going to go into an storage schema (database, json, whatever) which will require data migration if we need to refactor it -- or if it&#x27;s an library that will be used by large amounts of code maintained by other teams or other organizations -- then it&#x27;s worth spending a bit more time designing it. You&#x27;re balancing the risk of wasting effort over-engineering it against the risk of the effort invovled in having to modify it later.<p>If it&#x27;s going to be a public interface used by customers and supported indefinitely, then you&#x27;d better be very careful about getting it as close to perfect as you can, since now you&#x27;re balancing the risk of wasting effort over-engineering it against the risk of having to support a terrible API for years.
评论 #41509961 未加载
bubblebeard9 个月前
I constantly try to improve how I write my code. The last couple of years I’ve been involved in a couple of projects that’s required rebuilding a few times over because the project managers kept changing directions and was always in a hurry.<p>For one project I eventually managed to convince them to let our team write a more general purpose library to conserve time in the future. And this has paid itself of several times over.<p>When we write code it’s important we do consider how it may be utilised in the future. Since we cannot make exact predictions it’s better to make methods as small as possible instead to reduce the amount of time spent on refactoring (since this is unavoidable). This also helps us to create solid, more future proof, tests for our business logic.<p>You don’t need to follow a strict set of design rules, but general guidelines is a good idea. Like trying to follow SRP, avoiding more than x nubmer of lines for your method bodies and trying to avoid nestled code.
评论 #41510677 未加载
imron9 个月前
My favorite article on this topic is “semantic compression” [0] by Casey Muratori.<p>0: <a href="https:&#x2F;&#x2F;caseymuratori.com&#x2F;blog_0015" rel="nofollow">https:&#x2F;&#x2F;caseymuratori.com&#x2F;blog_0015</a>
vishnuharidas9 个月前
Experience and domain knowledge also matters when designing. For some parts, futuristic thinking is not at all needed while other parts may need it. If you are building a ticketing software, you should expect to add different kind of printers (dot matrix, thermal, inkjet, etc.) so you add an abstraction. The ticket text format may change, so some abstraction will help. The API service hander might not change at all, so an abstraction is unnecessary in that case.
shahzaibmushtaq9 个月前
History tells us that we cannot find the right balance until we go through the wrong balance, so is the case with engineering anything.<p>Learn, practice, and teach to understand what good enough-engineering is.
tommiegannert9 个月前
&gt; public void RemoveItems(Func&lt;Item, bool&gt; condition)<p>Tangential, but remember that you can&#x27;t generally push this condition over a database connection. You&#x27;ve just forced your code to be best-case O(n).<p>Using structured queries as far as possible is useful for performance. But consider what happens if you create a Predicate structure, and someone adds a field without updating the Remove function to match... Perhaps a recursive discriminated union is the way to go, for languages that fail compilation when not all branches are covered? Positional arguments in other languages?
vrnvu9 个月前
Why does introducing interfaces and vtables have to be the right abstraction? Given the example, the simplest solution is best. If you don’t need runtime semantics, just write simple code.<p>The Cons of a &quot;simple&quot; solution: &gt; As you add features, the class becomes more cluttered. But if it&#x27;s a feature, it shouldn&#x27;t be considered clutter. Clutter usually comes from over-engineering, not necessary additions.<p>&gt; Each method does one specific thing. That seems fine until you realize your interface is full of shallow, one-off methods. That makes it hard to maintain and extend. However, each method is efficient and tailored to solve a specific feature, which boosts performance. Plus, if there&#x27;s a bug, it&#x27;s easy to find and fix since each method has a clear path.<p>Also, to maintain a clear designed API, I suggest following some data-oriented recommendations, like designing functions to take lists of elements instead of single elements.
djtango9 个月前
For the Vehicle example the conclusion wasn&#x27;t that the Vehicle abstraction was premature, it was to prefer Composition and Interfaces over Inheritance which is a well discussed idea around OO design.<p>The Vehicle example is relatable to day to day work but for me the Vehicle abstraction seems reasonable. The mistake is not that a Bike is or isn&#x27;t a vehicle but rather that the abstractions should align with the business usage: a bicycle doesn&#x27;t share much in common with a car, the company just wants to rent them out.<p>So most the stuff that they will share in common will be around the sales&#x2F;rental part of the business logic. You wouldn&#x27;t expect them to really be displayed together anywhere either. So I wouldn&#x27;t be trying to force a Bike into a Vehicle, I&#x27;d be looking for the seams related to the truly common operations on Bikes vs Cars and look to extract interfaces or extend polymorphism there
mrkeen9 个月前
I think this is the common understanding of what &#x27;abstraction&#x27; and &#x27;generality&#x27; mean in the over-engineering sense, and I think it&#x27;s wrong.<p>You make things more general&#x2F;abstract&#x2F;future-proof by <i>removing things</i>, not <i>adding things</i>.<p>ShoppingCart is a collection. We have libraries and libraries of code which work over collections. So List&lt;Item&gt; is better.<p>Once you start hacking on the various ShoppingCart-related methods a bit more, you&#x27;ll probably realise you don&#x27;t depend on Item. Many of those methods should start operating on List&lt;A&gt; instead. And then chances are they&#x27;ll be pass-through methods that just forward the behaviour to some List implementation, at which point you can delete the passthrough methods, and directly call the List methods directly.
HelloNurse9 个月前
The vehicle example doesn&#x27;t make sense (start engine? At most, a notification from a rented vehicle to the server that the engine has been started, which is simply not going to arrive from a bicycle).<p>The shopping cart example is dangerous because it looks reasonable. Actually, the removal methods define atomic operations, so they are a big deal from an architectural point of view: can the cart change while we are testing elements for removal? Can the predicates look at other elements of the cart? Self-contained special case operations might be a more correct choice after all.
pc869 个月前
If anyone with more than six months experience submitted that vehicle PR with a half dozen interfaces we&#x27;d be having a chat outside of the review because that&#x27;s pretty bad. That might be the worst code in the entire article, and it&#x27;s supposed to be one of the good examples? This is even ignoring the fact that there are languages that don&#x27;t support multiple interface inheritance which isn&#x27;t that big of a deal but hints that the author may not have a ton of experience outside the couple languages he works in every day.
评论 #41512288 未加载
jscheel9 个月前
A structural engineer has to get it right the first time, or people may get hurt. Nobody is going to lose their life if a SWE has some inefficient code duplication in their POC that is tested by a maximum of 5 people. There is a time for improving your work and making it more robust, reliable, and efficient — but that can often be done iteratively. Code is malleable, just don’t let it fester and rot forever if you continue to build on top of it, or you will be drowning in tech debt.
mejutoco9 个月前
I always try to find the unspoken assumptions in these kind of articles. In this case, the need to use classes for everything, as the only hammer.<p>Pure functions with clearly typed inputs and outputs: you list all the available types and add when you need more supported, like data. They could be in a static class for convenience, or simply in a module.<p>or some finite state machine or a data-driven approach would make most of the examples much clearer.
mgaunard9 个月前
It&#x27;s simple: understand what the goals of your team are for the current year. Write the code with supporting those goals in mind.
评论 #41509235 未加载
yakkomajuri9 个月前
It&#x27;s quite a different thing when you think about just plain code that would need refactoring and data systems that need migrating. Probably an easier decision to keep code more focused on the present than data architecture, because rewriting code, while annoying, is a lot simpler than switching out data systems and migrating data in production.
tlonny9 个月前
Re: the shopping cart example<p>&gt; This approach is good for now. But, it will limit you later. Your code will quickly get out of control.<p>Will it? If we need to be able to “delete via X”, it sure feels more maintainable to have this feature supported via an explicit, named method vs. inlining it as an anonymous closure (as recommended by the “balanced” solution).
评论 #41509829 未加载
qnleigh9 个月前
I wish there were a big list of example situations like these to pour over. One could write a whole book on this topic (maybe someone already has). I spend so much time thinking about design decisions like these, but rarely get to hear someone else&#x27;s thought process.
dzonga9 个月前
problems created by being stuck in too much of an OOP &#x2F; Clean Code mindset.<p>if your initial entity is Map like - some call them records, data classes, objects etc - you can simply add &#x2F; remove properties willy nilly. and have functions that filter on those properties.
评论 #41510636 未加载
feoren9 个月前
Holy strawmans, Batman! This article sets up a false dichotomy (virtually nobody argues for designing for &quot;future needs&quot;, it&#x27;s more like varying degrees and definitions of &quot;cleanliness&quot;), then gives examples of terrible code at both ends of this dichotomy, and settles it with very mediocre code in the middle. IExternalRuleService? Is that supposed to resemble anything anyone sane would ever do? Do people literally write &quot;Car : Vehicle&quot; out of their 1st year of college anymore?<p>Let&#x27;s assume we&#x27;re all past those strawman examples and talk about the proposed &quot;Balanced Approaches&quot; instead.<p>ShoppingCart looks to be re-writing an in-memory collection. Is it actually adding anything beyond &quot;Collection&lt;Item&gt;&quot;? (Substitute with whatever collection type you like in your language). Do you actually need a class at all? There simply isn&#x27;t enough specified in the article to tell. Therefore there&#x27;s no lesson here: the code as-is shouldn&#x27;t exist, and we can&#x27;t tell what ShoppingCart is actually supposed to be doing. If it&#x27;s making database calls, you have multiple major problems. The best advice I can give in that situation is: don&#x27;t <i>do</i>, just <i>plan</i>. RemoveItems should take something that can be interpreted into SQL (or whatever&#x27;s appropriate for the persistence you&#x27;re using) and return a <i>plan</i> that can be composed with other plans.<p>IRefuelable, IParkable, IEngineOperable ... these seem to be missing the point that interfaces are defined by their <i>consumers</i>, not by their <i>implementations</i>. You write an interface when you have a need for its methods, <i>not</i> when you happen to have two implementations of them. If you find you&#x27;re writing classes like RealLifeObject : IFoo, IBar, IBaz, IBing, IBong, then your class is capturing the wrong thing. Most likely, those Foos and Bars are the <i>real</i> domain objects you care about, and Car should never have been a class. Go read Eric Lippert&#x27;s excellent Wizards and Warriors (all five posts): <a href="https:&#x2F;&#x2F;ericlippert.com&#x2F;2015&#x2F;04&#x2F;27&#x2F;wizards-and-warriors-part-one&#x2F;" rel="nofollow">https:&#x2F;&#x2F;ericlippert.com&#x2F;2015&#x2F;04&#x2F;27&#x2F;wizards-and-warriors-part...</a><p>It&#x27;s like this any time I see an argument about over-engineering, abstraction, etc. It just feels like everyone is missing the whole point. We argue about abstraction without even agreeing on what abstraction <i>actually is</i>. (Spoiler: it&#x27;s many different things.) We get bogged down in silly underspecified toy examples. So many articles are &quot;stop smearing shit all over your face! Smear it on your LEGS instead! Much better!&quot; Why are we smearing all this shit in the first place? It&#x27;s very hard to have an internet conversation about this.