9 ms·
Probably good advice, but when was the last time a programmer accurately scoped a problem when they said it will take “an afternoon” to build?
by sparker72678 6y ago
Probably good advice, but when was the last time a programmer accurately scoped a problem when they said it will take “an afternoon” to build?
- tluyben2 6y agoThat is the issue I have with it: ‘an afternoon’ is already way too vague. Make it ‘5 minutes’ (left pad etc) then I think it works out.
- axlee 6y agoIs that really 5 minutes? (For when left-pad was relevant) var cache = [ '', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' ]; function leftPad (str, len, ch) { // convert `str` to a `string` str = str + ''; // `len` is the `pad`'s length now len = len - str.length; // doesn't need to pad if (len <= 0) return str; // `ch` defaults to `' '` if (!ch && ch !== 0) ch = ' '; // convert `ch` to a `string` cuz it could be a number ch = ch + ''; // cache common use cases if (ch === ' ' && len < 10) return cache[len] + str; // `pad` starts with an empty string var pad = ''; // loop while (true) { // add `ch` to `pad` if `len` is odd if (len & 1) pad += ch; // divide `len` by 2, ditch the remainder len >>= 1; // "double" the `ch` so this operation count grows logarithmically on `len` // each time `ch` is "doubled", the `len` would need to be "doubled" too // similar to finding a value in binary search tree, hence O(log(n)) if (len) ch += ch; // `len` is 0, exit the loop else break; } // pad `str`! return pad + str; }
- TheDong 6y agoOne of the points of the article is that when you code a dependency for your purpose, you get smaller code. For example, I know I'll always pass it a string, so I don't need to type-check. I started with the problem of "I need a function that pads a string with a character out to some length". Coding it up took under 1 minute, easily under 5 minutes. function leftPad (str, len, ch) { const neededPadding = len - str.length; if (neededPadding <= 0) { return str; } return ch.repeat(neededPadding) + str; } It took me longer to write this comment. The fact that the left-pad code has optimizations (which don't matter for the place I'm using it) and type-checks (which don't matter; my higher level unit tests would catch that mistake) is beside the point.
- scarface74 6y agoUnless you are doing front end code, why does the size of the code matter?
- TheDong 6y ago"smaller" there was short-hand for "more focused, purpose-built to your problem, less generic". All of those properties make the code easier to reason about and test. Being easier to reason about, and being a more exact abstraction for my needs, are both incredibly valuable properties.
- scarface74 6y agoMore generic is better. If I go into a codebase that uses standard libraries. Even if I don’t know how to do something about it someone on the Internet does. Your custom framework - not so much. I don’t have to care about how the underlying libraries work. I can treat them as a black box.
- TheDong 6y ago> I don’t have to care about how the underlying libraries work. I can treat them as a black box. When I wrote haskell, the majority of the libraries did just work, and I didn't have to dig into their code to find bugs often. When I wrote javascript, hundreds of the libraries I used did not just work. I usually had to care very much about their details because they were poorly implemented, full of bugs and incorrect abstractions, and often abandoned soon after. I agree that there's benefits in reusing some well-socialized and well-implemented generic frameworks and abstractions. It's not worth using generic abstractions that are not well understood, buggy, and don't match your needs closely. In that case, write your own. More generic is not always better. Above, I'm arguing that it's important for code to be easier to reason about. If a generic abstraction helps with that, cool, but it's not always going to be the case.
- scarface74 6y ago
- deleted 6y ago[deleted]
- cogman10 6y agoThis is actually a great example of why you SHOULDN'T use left pad. I've not profiled it, but I'm going to guess that now-a-days this will be faster than the current implementation on npm. function leftPad (str, len, ch) { str += ''; len = len - str.length; if (len <= 0) return str; if (!ch && ch !== 0) ch = ' '; ch += ''; return ch.repeat(len) + str; } Why? because the VM is (very likely) going to do exactly what the cache would have done. It can replace `ch.repeat(len) + str;` with a presized string allocation and a memcpy of ch + str characters.
- goldenkey 6y agoThis code checks for ch !== 0, yet the bypass branch afterwards calls .repeat. The Number type in JS has no repeat method. Static typing would actually fix this kind of coding error.
- reificator 6y ago> This code checks for ch !== 0, yet the Number type in JS has no repeat method. > Static typing would actually fix this kind of coding error. That's not a coding error, that's addressing an oddity of JS coercion rules that a less experienced developer could easily have missed. > if (!ch && ch !== 0) ch = ' ' That code says that if `ch` is falsey and not equal to 0, then set it to a space. The only arguable falsey value that should be excluded here is a literal `false`, but that's not a single character and is fairly ambiguous either way. I'd certainly fall on the side that a literal false should not be converted to `'false'` here. > ch += ''; The next line converts to to a string by adding it to the empty string. > return ch.repeat(len) + str; So by the time it gets to this line we know ch is a string. Static typing is great, but the bug you claim is there is not actually there.
- cogman10 6y agoFYI, I edited the post after the bug was pointed out to eliminate it and bring this method up to parity with leftpad.
- 6y ago
- reificator 6y agoYes, particularly when you know the types that will be passed in. True that typechecking in JS can be a little timeconsuming, so it might take 10-15 for me to write a general purpose left pad. Also I seem to remember that someone benchmarked the cached version and found it to be slower than the naive approach anyway. I could be mistaken there.
- ghj 6y agoThis is so hilariously overengineered: not failing when passed the wrong types, arbitrarily caching padding of len < 10, etc The most egregious is the bad big O analysis for a pointless binary search. The loop does indeed run O(log(n)) times but `ch += ch` still takes O(ch.length) which is growing exponentially. It ends up being a complicated way of still taking O(n) time while creating a lot of intermediate strings. It isn't any faster than just creating the padding with a loop or `new Array(len).fill(ch).join('')` or `ch.repeat(len)`
- Ajedi32 6y agoDid you run benchmarks to validate those assumptions? The developers of left-pad did: https://github.com/left-pad/left-pad/blob/master/perf/perf.js https://github.com/left-pad/left-pad/blob/master/perf/perf.j... It's not overengineered if thousands of downstream projects are relying on it, some of which might see significant benefits from those performance optimizations.
- cogman10 6y agoToo bad that's a flawed benchmarking methodolgy. JITs are notoriously hard to correctly profile and the benchmark lib isn't even sort of doing the right thing. For example, it's missing warmup. The results aren't being consumed in a way that wouldn't optimize them away. The framework itself imposes a pretty large amount of overhead (moreso than I'd expect from leftpad). It is somewhat likely that what they are measuring isn't leftpad performance, but rather how fast the JIT ends up optimizing the benchmark code. I'd suggest watching this video https://vimeo.com/78900556 https://vimeo.com/78900556 It's about microprofiling the JVM, but the principles are the same for other JIT based languages (such as javascript).
- Ajedi32 6y agoYeah benchmarks can be much more difficult to get right than it might seem at first glance. Good thing they didn't try to write their own benchmarking code, otherwise they might have fallen into those traps you just mentioned. Luckily, they didn't, and instead pulled in the the `benchmark` library as a development dependency. The author of said library works on V8, and already considered all those problems and much, much, more[1]. [1]: https://mathiasbynens.be/notes/javascript-benchmarking https://mathiasbynens.be/notes/javascript-benchmarking
- tluyben2 6y agoWell, my point was more that if a programmer thinks '5 minutes work' it's often 10+ minutes; so when a programmer thinks 'an afternoon', you can possibly lose a week. And then the article really doesn't work. And yeah, I would and do write leftpad myself it it's not in the stdlib. But if there is a large library full of similar (string) functions that I might need, I would include that library. Not a singular dependency for this type of function.
- mjevans 6y agoThe issue I have with this is a lack of specification. Left pad _what_? Numbers or ASCII-only-printing? OK that's a reasonable. Is there a desired overflow behavior? Past that it becomes more an issue of where and why. The suddenly not-trivial example includes questions about fonts, layout, and multi-byte characters. Emoji, etc. Incidentally, in pseudoscope: Create a valid full-space pad string (termination / etc), then decrement back from the end of the source string and over-write the pad characters from the end to the start of the string, exiting either on no more pad characters or no more input. A second algorithm might combine those two steps as one pass, fill the output buffer from back to front. Only for C style strings would this be an issue given the dynamic end point for the data structure.
- rlonn 6y agoThis is the wisest comment in the whole thread.
- acdha 6y agoThat was my first thought: I've seen these projects before — they're where you find 5 slightly different implementations of similar logic, no logging or tests, failures as soon as someone uses Unicode, etc. and I get an order of magnitude performance improvement by replacing that code with an external module which has had the other 19 afternoons' worth of work it actually takes.
- avmich 6y ago> and I get an order of magnitude performance improvement Have you heard the adage about premature optimization being the root of all evil? Yes, even with the second part. What is the premature optimization here, in your opinion? Most of cases developers create something new - that's the state of industry now, not too good but it's how it is. If you'd be refactoring the existing code - sure, find the problem, design the solution, have reasons going from A to B. If, however, you're writing new functionality, you don't know if you'll have problems of this kind with this code - so optimize for developmentality. You can remove those excessive crutches later - if and when you need them. In my experience, having them trumps looking into code and spending time figuring what it does mere months later - your own code, that is.
- acdha 6y agoThe point was that when something is large enough to be “an afternoon”, it's probably more work than you're expecting and you haven't yet discovered important details. If there's something which does what you need, it's far more likely that _other people_ have invested time sanding off the rough edges which you have yet to discover. If it's not hard to use that library you're probably better off unless it's a problem you understand very well and will see a real advantage to tackling differently. For example, if you use a library and don't like it that experience will still be useful for having clarified what exactly it is that you want to do and the rough size of what you're taking on.
- rini17 6y agoBut that is with ample benefit of hindsight.
- avmich 6y agoWell, when the programmer was burned too much by incorrect scoping before? Don't buy generalized statements like "programmers are always underestimate efforts needed", or even, for that matter, "a task always requires all the possible time it might take" (Parkinson's law). There are exceptions from them :) which sometimes, in a good team, look more than laws themselves.
- andrewstuart 6y agoI came here just to say this. "This'll take an afternoon" - three weeks later...... Programmers are notorious for this. BUT even apart from this problem ... you absolutely should use every dependency you can that will save you time. Try to write less code not more. When you write code you write bugs, add complexity, add scope increase need for testing, increase the cognitive load required to comprehend the software, introduce the need for documentation..... there's a vast array of reason to use existing code even if you truly could estimate it and build it in an afternoon. You also assume that you understand all the edge cases and fickle aspects of the dependency, all the weird ins and outs that the dependency author probably spent much resources understanding, fixing and bug hunting. There's a hard fact that proves the above poster to be wrong..... how many dependencies took only an afternoon of time in total to write? Hard to say (maybe look at the github commit history) but I'd guess almost none. It didn't take the dependency author an afternoon, so why will it take you an afternoon? Even worse .... you just lost an afternoon coding features for your core application. Multiply this by every dependency that "you could build in an afternoon" and you'll be in Duke Nukem Forever territory. I'd advise doing the opposite of this articles suggestion. Find a dependency that will save you an afternoon? Grab it.
- vlovich123 6y agoDepends how your dependency management system works. Some times it can take an afternoon or more just to integrate it into your build for c/c++
- avmich 6y agoSomehow here we assume a good programmer routinely makes errors in time estimation by an order of magnitude, yet conveniently forget cases when, say, a non-trivial GPL library is embedded as a dependency into a project, and customer is asking for code, and legal team runs with hairs on fire because company didn't plan to release the code...
- greyhair 6y agoBut that is a completely different topic. Licensing is an issue, yes, but that is part of the upfront decision process. In this day an age, this many years into open source licensing, if your team is not on top of that from day one, they have failed as a team. I worked at Bell Labs from the mid 1980s to 2000, and by early 1990s (1992? 1993?) they already had a full internal team dedicated to open source licensing issues, including training and consulting. That was 27 or 28 years ago. Before some of the developers on this thread were even born.
- m463 6y agoI think "don't use a dependency" is premature optimization anyway.
- _the_inflator 6y agoI agree with you. We all too often forget the scope: requirements, developing, testing, to say the least. My favorite example is NPM. While the author has a point, I tend to rely on the wisdom of the crowd. Sometimes there is a reason why a couple of million developers - in the case of NPM packages - seem to be lazy. In my experience, we ended up copy/pasting and modifying some code and syncing it with the "superfluous" package. Good intentions, badly executed. Leftpad was the right itch at the right time and people found better ways to deal with NPM. NPM got better after that, as well as native implementations. Better cope with NPM than fight it, my 2 cents.
- nitwit005 6y agoYou can tell it won't take just an afternoon 30-60 minutes in. Besides, in reality pulling in and using the dependency takes time as well. There's no real guarantee it's cheaper in terms of developer time.
- ogre_codes 6y ago> Probably good advice, but when was the last time a programmer accurately scoped a problem when they said it will take “an afternoon” to build? This is why you time box things. Spend XX hours trying to get a thing working and if you aren't close, you grab a library and move on.
- GoToRO 6y agoIt was yesterday.
- jeffbee 6y agoThis goes both ways. When was the last time someone properly scoped the maintenance effort of an external library? This goes double for external systems, like kafka or mysql. I've never seen anyone so far even get within two orders of magnitude of the real cost of operating kafka, much less an organization that accurately compared that to the cost of DIY.
- kiawe_fire 6y agoThe "don't reinvent the wheel" argument often acts as though using a 3rd party lib is "free", and building it yourself is costly with no benefit. This is sometimes true, but often not. From SFTP libraries to SVG rendering libraries, there have probably been about 3-5 major dependencies of my company's project that I have had to learn and extend or fix bugs in to make them work just in the last year. And sometimes this means using our own fork that we have to keep maintained. I'm not saying I would have rather written these particular dependencies from scratch, but they were definitely not cost free. Nor are they all of better quality than what I would have produced had I written them from scratch. That's the other common refrain - to "defer to the expertise of the crowd". Don't get me wrong, many 3rd party libraries are of great quality by amazing men and women who I am very thankful for. But certainly not all of them. There's no magic that says "every third party library is made by an expert with the highest standards".
- greggman3 6y agoIt really depends (haha) on what it is. I needed to copy a file in npm scripts. can't use `cp` because that fails on windows. I looked on npm to copy a file. First hit 197 dependencies, 1170 files, 47000 lines of JavaScript. all I needed was const fs = require('fs'); fs.copyFileSync(process.argv(2), process.args[3]); Taking 197 dependencies means 197 things that need updates several times a year at a minimum. Any of those updates could break my code, introduce a bug, add a vulnerability on top of the ones already in the packages. So it's not like adding more dependencies is magically free.
- hinkley 6y agoIn the interests of some sanity, I would like to say, all in one spot, that these are not mutually exclusive - You should absolutely use community-supported tools to solve your problems. - You should substitute idiomatic code for libraries. You have made an argument for the latter that does not detract from the former.
- greyhair 6y agoThis needs about 1000 upvotes. So much truth in so few words.
- benibela 6y agoCopying a file is really hard to implement Lots of things can go wrong when writing a file: https://danluu.com/deconstruct-files/ https://danluu.com/deconstruct-files/
- s3cur3 6y agoI’ve been working on the same medium-size (fewer than 1M LoC) codebase for about 7 years now. I feel like over the years, my estimates of how long something will take have gotten better for one reason: I’ve found the scaling factor I have to apply to my intuitive estimate that brings into the realm of reason. So, if I think something looks like about a day’s work, I’ll actually estimate it at about 3.5 or 4 days. Thus, for a project to qualify as “just an afternoon,” I’d have to naively estimate it at under an hour. I rarely have time to spare, but I also rarely go over by more than maybe a third. Your multiplier may vary depending on how horrifying your codebase is. On a side project with good test coverage, my multiplier is only about 2.
- klyrs 6y agoI do this all the time. My head tells me "five lines, tops" -- corresponding to about 10 minutes of "programming." Add in testing, bugs, another 10-20 lines of comments and docs, we're looking at an afternoon. Never do I give that raw 10-minute estimate to anybody, because it can be wrong by a factor of 10.
- sibane 6y agoIt takes an afternoon to do after one week of research and exploratory coding.
- phodge 6y agoIronically, these days with front end development I'm finding it hard to accurately scope how long it will take to incorporate 3rd-party dependencies. The docs make it seem straightforward enough, but they don't cover how to use it correctly under TypeScript instead of ES, or how to use it with Angular instead of React, or how to build it with Rollup instead of webpack, and I often spend an entire day googling obscure blog posts on how to get a dependency working in my own ecosystem.
- swiley 6y agoMost things really can be done in an afternoon if you’re in the right mood. They just won’t have unit tests, and they’ll probably have lots of defects and other technical debt.
- greyhair 6y agoNever