6 ms·
Rich Hickey has a succinct quote that reveals the abuse that Hoare's "premature optimization" quote has undergone: the cheapest place to fix bugs is in design.
by omginternets 4y ago
Rich Hickey has a succinct quote that reveals the abuse that Hoare's "premature optimization" quote has undergone: the cheapest place to fix bugs is in design.
And if we're going to proceed by quotation, another one to consider is Linus Torvalds': Bad programmers worry about the code. Good programmers worry about data structures and their relationships.
IMHO, a big thing for junior programmers to unlearn is that one should begin by putting a sloppy version of the program in production, and incrementally improve it thereafter. A little bit of forethought and a bit of attention paid to big-O will do three things: (1) make incremental improvement easier (2) limit the amount of incremental improvement needed, and (3) avoid painting you into a corner wherein significant refactors are needed.
As it turns out, thinking ahead of time is not antithetical to shipping code. Quite the contrary.
- jasmer 4y agoI beg to differ. What has to be 'unlearned' is that Engineers think they have a clear vision of what they need to do ... but in reality they don't. This largely has to do with 'unknown unknowns', particularly in requirements. It's actually a much harder concept to learn/unlearn - because we are trained to design towards specs, towards a kind of perfection, to solve problems in which the conditions are contrived. Almost nothing in the academic world prepares people for having to build something whereupon the objectives are a bit fuzzy - especially when said objectives are not presented as fuzzy. It almost requires the 'experience of failure' to grasp that most things that can be incrementally built - should be. The process of building something, is actually a process of 'discovery'. I think that the only time 'prepping far ahead' becomes useful, is when Engineers are building on very foundational, learned knowledge derived from a relevant situation, in which case they are actually acting in an iterative fashion, leveraging knowledge from the previous 'situation' - but even then, I would not consider that for optimization, rather, just keeping a lid on arbitrary complexity. And finally, of course some things are reasonable. You probably are not going to build a high performance codec in Python.
- trashtester 4y agoAgree with almost everything above, except the first line. In military strategy there is the quote "Plans Are Worthless, But Planning Is Everything" [1]. I think the same goes for software design. To believe that one can do a complete design of some piece of software, and then implement it precisely as planned, is folly, just as for military plans. Still, it is imperative to do some deep thinking about the purpose of the software as one starts coding, especially about things like the purpose of the software (and what is NOT the purpose), design and design philosophy and expected challenges (including performance challenges) including ideas about how to find solutions to those challenges. Also, after starting to code, it's important to revisit these aspects. Is the purpose changing? If it is, is that good or bad? How do we define the new purpose? Are we making changes to the design? Why? Do we need to refine the design concepts? And so on. Just like for military strategy, this is an art more than a science. We have strong limitations dictated by what is possible, yet it is essential to have an overall idea of what direction we want to move in. Otherwise we, more likely than not, end up with a useless patchwork. [1] https://quoteinvestigator.com/2017/11/18/planning/ https://quoteinvestigator.com/2017/11/18/planning/
- agentultra 4y agoI think a part of this is that engineering concepts are not often introduced in a lot of CS courses. It is more often introduced in software engineering courses... of which there seem to be few? In my experience it's not well known that requirements are different than specifications and what those differences are. Knowing the difference makes understanding why planning is useful but "plans" are useless. Requirements state the desired properties of a system that affect our world. They are written from the perspective of humans interacting with a desired system to achieve some goal. It's useful to know that we need a system that does X so that we can Y. We need to ensure that Y eventually happens when the user initiates X. If the system cannot succeed at Y within Z minutes we need to notify the user so that they can initiate procedure J and halt the process manually. Requirements don't have to tell us how to implement such a system. They're more like the end state. It's the job of specifications to tell us, technically, how such a system operates. We, the developers building the system, don't have to write the full specifications up front. That's a bit of a mistake. Because the gap, as the GP points out, is quite large from the starting point and the end result. It's quite normal to start with the bare minimum specification and iterate towards an ideal one. As you learn about the problem space we can build more certainty about how such a system can be implemented. We can update the specifications, verify the code implements the specification, and hopefully in the end arrive at a system that fulfills our original requirements. It's rare that one can write the full, complete specification and leave it up to the developers to implement it.
- Koshkin 4y ago> This largely has to do with 'unknown unknowns' What has been much, much more worrisome in my practice, is ‘unknown knowns’ which is something developers should know but do not. Poor design and engineering skills, low level of professionalism is what keeps plaguing software industry. (Remarkably, most other industries, those that have to deal with material things, do not suffer from this - not on this scale anyway.)
- samvher 4y agoIn my experience refactors and big-O thinking tend to show up in pretty different places. Refactors are more about which components are talking to each other, and what is the content and shape of that communication. Big-O is more about how a component does its work internally. Both of the quotes in your comment resonate with me, in the sense that they can avoid unnecessary refactors. But IMO the big-O/performance part should really mostly just be done on a by-need basis (i.e. you've identified your bottleneck and determined its worth the time to improve it). I've seen way too much unreadable code because "otherwise it wouldn't be performant" in places where performance doesn't matter, and similarly I've seen people sacrifice a clean modular interface for the sake of performance. Sure thinking ahead of time makes sense, as does choosing a design which puts flexibility in the right places (i.e. gives you room to adapt to what you learn in the future which is currently uncertain). But that same uncertainty also means you're probably not going to get the design fully right now, so don't overthink it.
- trashtester 4y ago> But IMO the big-O/performance part should really mostly just be done on a by-need basis I agree >(i.e. you've identified your bottleneck and determined its worth the time to improve it). For this, though, it is often possible to identify this BEFORE writing any code. As opposed to refactoring an inner loop, Big-O challenges may often stem from (at least in my experience) the top level design of the code, and changing it may require rewriting almost everything. This is particularly true in a microservices world, where the problem may often be how you split a computation task up into services in the first place.
- chriswarbo 4y agoBig-O is often used for speed, but it applies to anything satisfying Blum's axioms. API requests are a good example, especially when using a throttled or pay-for API. I was in a design discussion this morning for some new functionality which requires hitting a pay-for API; and much of the design was based on minimising how many calls we'll make. A naive polling approach would require O(t×e×u) requests, where t is time, e is the number of the events we care about and u is the number of users affected by those events. The design we came up with uses a separate data source to identify when those events occur, which removes the factor of t. We also came up with some de-duplication logic which reduces the constants.
- UK-AL 4y agoI find BigO performance is very rarely the cause of bad performance. It's normally slow IO, timeouts, deadlocks etc I think I've actually seen performance decreases by people trying to decrease BigO. BigO only starts to really dominate time when N reaches higher levels. At lower levels of N your performance on your constant operations matter more, and by implementing more complex algorithms actually decreased real time performance. Obviously a lot of nuance in this. But first you need to measure.
- eyelidlessness 4y agoI work on an application with some serious performance issues. Nearly all of them involve some combination of: - inefficient design around data dependencies and their identification - redundant work performed around said dependencies - excessive reliance on APIs for uses they’re not intended, which frequently causes… - …excessive VM deoptimizations and excessive GC Granted this is one application and I certainly don’t think it’s representative of anything. But it’s definitely a counterpoint to inefficient concurrency (where this app generally performs quite well).
- trashtester 4y agoKnowing WHEN to do BigO optimizations is part of knowing how to optimize. And for cases that _could_ ble NlogN, it may be perfectly fine to chose N^2 for moderate N, while N^3 can kill the solution. As for timeouts, IO and deadlocks, those problems can also be a case of poor up front optimization (before codigng).
- omginternets 4y agoI find that big-O is exactly the performance problem that remains after you've tuned your IO, picked sensible timeouts, and eliminated deadlocks (which are straight-up bugs). The things you list are the low-hanging fruit, after which most engineering teams throw their arms up and say "I guess we've gone as far as we can on this!"
- UK-AL 4y agoIt means you're mostly compute limited. Which i find is rare in web api's.
- intelVISA 4y agoYeah, not sure if it's a mental roadblock but it's painful to incrementally improve something especially if you've learned of better ways (which happens a frequently as a jr) as you end up fighting with your own idiot self. Bad code is laid down at day 1 and everything past that is just worsening it even if it appears to be a positive refactor. Torch the canvas and embrace the cycle. Not that it has to be preplanned, per se, as the absence of planning before day 0 still becomes plans on the day IME.
- anyonecancode 4y agoI find the key is to always write your published code so that it's intention is clear. This doesn't require knowing what the ideal code is or an attempt to have all the requirements pinned down, and it can result in code that is "sloppy" as in "not aligned with what we actually want," -- but we should avoid being sloppy in terms of "this is what I intend this code to do." Requirements and understanding change, and so code should change as well, and this happens with greater frequency earlier in a project. But code that clearly expresses its intention makes that much easier -- someone reading it (maybe even the original author!) can better see "does this code actually do, what it thinks it does, does it do what we need it to do". Optimizing for speed or memory footprint might or might not be premature optimization. It's not always easy to tell. But optimizing for _clarity_ is never a premature optimization.
- hinkley 4y agoOptimization gets treated a lot like AI: When we find a concrete use for some facility of it, we give it is own name and disassociate it with the source. What once was Optimization gets called Information Architecture, Capacity Planning, and so on, and all of these things should be done as soon in the project as possible. > IMHO, a big thing for junior programmers to unlearn is that one should begin by putting a sloppy version of the program in production This is the closest we've gotten to a Just Do It mantra but we have a lot of space for improvement. An instructor can't help you get better at public speaking if you won't speak at all until you can speak well. However once you can speak well you shouldn't rush important speeches because you can't walk some things back once they're out there. We will always have a steady stream of brand new developers who need simple rules of thumb, but some senior developers never grow out of it. What I see is a lot of these aphorisms being used as a way to censor others who might be suggesting work in the "hard but valuable" quadrant of cost vs value. At some point in my career I got so fed up with coworkers trying to go against business needs (customers are complaining about responsiveness) by shaming coworkers into not touching performance and essentially running a work slowdown, that I began identifying types of code refactoring that improve clarity and speed. Sometimes what confuses the developer confuses the compiler. Particularly in the realm of strength reduction.