28 ms·
I love this! It's the "jazz music" of software development. Something which breaks all the "rules" but does so purposefully and explicitly so that it can become
by Klathmon 8y ago
I love this! It's the "jazz music" of software development. Something which breaks all the "rules" but does so purposefully and explicitly so that it can become better than the "rules" allow.
A naive look at this and my head is screaming that this file is way too big, has way too many branches and nested if statements, has a lot of "pointless comments" that just describe what the line or few lines around it is doing, and has a lot of "logic" in the comments which could quickly become outdated or wrong compared to the actual code.
Yet at the same time, it's probably a hell of a lot easier to maintain and manage than splitting the logic up among tens or hundreds of files, it contains a lot of the inherently complex work it's doing to this file, and it is so well and heavily commented that it should be pretty easy to ensure that any changes also keep the comments up to date (after all, any change without changing the resulting comments should show up like a sore thumb, and will most likely prompt the reviewer to look into it at the very least).
- afarrell 8y ago> probably a hell of a lot easier to maintain and manage than splitting the logic up among tens or hundreds of files I'm only halfway through John Ousterhout's book Philosophy of Software Design but I think it agrees with you on this -- that smallness-of-file or smallness-of-function is not a target to shoot for because it prevents the things you build from being deep. That you should strive to build modules which have deep functionality and small interfaces and should contain their complexity within them so the users don't have to know that complexity.
- Klathmon 8y agoNow i'm squarely in frontend web-app development right now which definitely changes things (mainly the complexity is centered around enabling fast changes/additions to the codebase, and not the actual business logic for the most part), but while "deep functionality and small interfaces" sounds good on paper, most of the time giant files with a few functions exported aren't a good way to manage that. Sure, it solves the problem when viewed from the outside. "users" of the software (users being other devs in this case) get a nice small interface and docs that explain how to use it, but internally it's much harder to work with. Having everything in one file like this without breaking it into "sub modules" for various parts of the module means that you need to almost have a complete understanding of the module before working on it. In this case, I have a feeling that is a pro not a con. Because this file is so core to the system, and has so much complexity, that breaking it up into smaller parts could cause a dev to feel like they understand it only to find out they don't after it's released. And it means that any devs that truly do understand it top-to-bottom would just waste a lot of time switching around files if it were split up. Putting it all in the same file here nudges you to really make sure you understand it top to bottom before making any big changes. It's intimidating and scary for a reason, because at its core it is a complex piece of code, and dressing it up in "simple code's clothing" won't help.
- TeMPOraL 8y ago> Having everything in one file like this without breaking it into "sub modules" for various parts of the module means that you need to almost have a complete understanding of the module before working on it. There's a balance to be struck here; you want to minimize the size of the code a developer has to understand to work on (or with) a given abstraction, but you don't want to split beyond that point, as it only makes the developer jump around files. In my experience (with Java in particular), current development trends involve splitting the code too much.
- usrusr 8y agoJava (like many other languages of that generation) suffers from a lack of idiomatic 1:1 visibility. Whenever you split something up in Java it litters a namespace that is much bigger than necessary. Even private is too big when the class is full of tiny methods most of which most will never be meaningful to any of their peers except for that one call site. Sure, you can create inner function objects and with 8+ it's not even completely weird anymore, but that's still a far cry from the nested functions goodness of Pascal style "structured programming".
- TeMPOraL 8y agoAgreed. I don't remember much from my brief exposure to Pascal years ago, but spending a good chunk of my programming years in Common Lisp has spoiled me; the simple ability to nest functions is something I sorely missed when working on Java codebases.
- deleted 8y ago[deleted]
- mrgriffin 8y agoOne of my favorite features of Haskell is that I can use where to write the auxiliary function definitions after the code that use them, but keep them scoped inside of a single function. A relatively bad example here: https://wiki.haskell.org/Worker_wrapper#Hiding_the_worker https://wiki.haskell.org/Worker_wrapper#Hiding_the_worker
- TeMPOraL 8y agoI just finished his book yesterday; he has a lot to say about size and comments. For size, your summary is spot-on. I'd only add that he notes overeager splitting of methods and classes makes code involved in a particular abstraction to be no longer in one place, leading developers to constantly jump around files, which makes it more difficult to understand the code and increases the chances of making bugs. As for comments, this file is essentially Ousterhout taken to the extreme. Still, I think he would have like it, given how critical this file is. In the book, he encourages writing more comments than the current trends would suggest, pointing out that you can't fully express abstractions in code, so all the things the code doesn't contain - the high-level concepts, the rationale, the caveats - should be documented in comments in appropriate places. Overall, I'm extremely impressed by the book, and its focus on reducing and mitigating complexity.
- sitkack 8y agoThere is a review and discussion of that book here, https://news.ycombinator.com/item?id=18331219 https://news.ycombinator.com/item?id=18331219
- TeMPOraL 8y agoThat is the exact thread that prompted me to buy the book.
- thaumaturgy 8y ago> I'd only add that he notes overeager splitting of methods and classes makes code involved in a particular abstraction to be no longer in one place, leading developers to constantly jump around files, which makes it more difficult to understand the code and increases the chances of making bugs. Another way of describing this, that I ran across recently, is that this increases the cognitive load for developers working on the code, and cognitive load is one metric by which code can be measured as "good" or "bad". Things like excessive scrolling and switching between files increases cognitive load. So heavily-commented code can decrease cognitive load if the programmer can read the comments and the related code block at the same time and if the comments help to explain why the code exists the way it does. Or, heavily-commented code can increase cognitive load if the comments don't accurately describe the code, or if they're so verbose that the programmer has to scroll up and down to digest both the comments and the code together.
- partycoder 8y ago> that smallness-of-file or smallness-of-function is not a target to shoot for I disagree. Unless you are methodical and know what you are doing (like NASA or the authors of Kubernetes), it is hard to create large functions and files by keeping levels of detail consistent and not repeating code. How do you test a function that has 100 unique outcomes? How do you safely maintain it to ensure it won't break? How do you even know it's working?
- sdenton4 8y agoWrite 100 tests?
- AstralStorm 8y agoWrite automated proofs? Probably best but tooling is junk - it's rarely supported. Constraint or property based tests as opposed to value tests? (That depends on how unique.) Usually the only thing that does unique things is a direct mapping, everything else is either an equation or has other preconditions, postconditions and internal or external properties. It also makes the logic more directly visible and refactoring easier presuming tests are reasonably written. Ensure coverage with a tool to be sure.
- iamrohitbanga 8y agoHaven't read the book. Goes to my Wish List. I completely agree about encapsulation of complexity inside the module. Similar to UX design principles. Just like google or other successful sites present a very simple interface (in case of google the home page is quite clean) but behind it lies very complex code. IMO, encapsulating complexity and handling all edge cases also lends to total functional programming [1] even without using pure functional languages. [1] https://softwareengineering.stackexchange.com/a/334876/38285 https://softwareengineering.stackexchange.com/a/334876/38285
- loneranger_11x 8y ago> That you should strive to build modules which have deep functionality and small interfaces... this. interfaces are not the most important thing in a software but definitely one of the most. you can live with a crappy implementation but your interfaces must cater to the usecase and should only hcange when core premises change.
- hueving 8y agoThis is what a lot of Go code looks like. This "space shuttle" code honestly isn't much more verbose than most Go code I interact with. The main difference is they have more comments here.
- thefounder 8y ago"else/else if" is not used very often in Go.
- efaref 8y agoIt's not used very often here, either. Despite their claims at the top of the code of every `if` having a corresponding `else`, there are 140 `if`s, but only 20 `else`s and a mere 5 `else if`s.
- AstralStorm 8y agoMost of the ifs are for early termination. Using an else on one of these is wasteful.
- bouncycastle 8y agoLikewise, I've seen a lot of Go that is far away from the idomatic Go heaven that is the standard library. Although that happens in every language as soon as there's reasonable complexity. As for the "space shuttle" term, it's more of a euphemism for "do not even attempt to refactor this mess, it's so complex that you'll surely fuck up if you do, and you'll make it harder for the one guy who can actually understand this"
- EB66 8y agoI completely agree. For code that is unavoidably complex, I love this style too. I am all for code that is concise and whose syntax/naming is expressive, but sometimes comments are necessary to clearly spell out the logic or business use case. Expressive code can only go so far. Well-crafted comments significantly reduce the amount of time required for other developers to dive in and become productive with an unfamiliar code base. The key is keeping the comments up to date. There's nothing worse than an inaccurate comment. One's code review process must include a review of the comments accompanying the modified lines.
- chrisweekly 8y agoAgreed. Also, comments that emphasize the "why" over the "what" are not obviated by descriptive names.
- tetha 8y agoI've found it very helpful to rubber duck comments to get into the right why mode. Consider how you'd explain this piece of code to a less experienced guy on the team - and write down exactly that. Why was this added? Why was the old thing in place changed, what broke and had to be changed? Why didn't you do the other obvious thing? And remember - usually you should explain something until it's clear for you, and then one more step.
- ascar 8y agoOutdated comments that explain the business use case or purpose are still better than no comments. It gives you background information how the code evolved or what it was supposed to do. It's probably because reading comments only is worse than reading code without comments, that some devs developed an aversion towards outdated comments and thus comments in general. Comments are additional information and no source of truth, always take them as that and read code and comments.
- chris_wot 8y agoIt’s much worse in codebases that predate version control. At least a commit shows the context of why it was added.
- bb88 8y ago> A naive look at this and my head is screaming that this file is way too big, has way too many branches and nested if statements. A naive look at this and my head is screaming that this file is way too big, has way too many branches and nested if statements, has a lot of "pointless comments" that just describe what the line or few lines around it is doing, and has a lot of "logic" in the comments which could quickly become outdated or wrong compared to the actual code. I actually understood it immediately when I read it. There's something to be said when you have a decent roadmap for debugging critical code segments. It's not just software maintenance here, it's for the "holy crap something went wrong and we can't understand how we got here!" moments.
- erikpukinskis 8y agoThis is why I prefer older JavaScript to the more recent stuff, with transpiration pipelines and AI-like optimizing compilers that gnaw on giant state trees. That stuff is lovely and terse when it works. And when it doesn’t you are at a total loss. The beauty of imperative code is there’s a beginning and an end, and at every step you can easily see what follows and without too much work what came before.
- iseeyoubydesign 8y agooh no the "I love it" first post ... the fact this type of post is the top of so many threads leads me to believe theres more behind these posts. the positive renforcement cheerleading to start off so that people dont get demoralized in the hateful comments, while effective also seems fake.
- fogetti 8y agoI also noticed this. I wouldn't be surprised if there was some editor-in-chief who basically scores the comments based on his taste.
- dkarl 8y agoAbout five years ago I worked on a codebase with a similar bit of code. It wasn't nearly this big, but it was branchy, procedural, and verbosely commented. I didn't write the initial version but worked on it quite a bit and learned to appreciate the advantages of the style for the nasty bit of logic it implemented. I ended up having to vigorously defend it against another developer's half-cocked attempt at "refactoring", which got deployed more than once (behind my back) and thankfully broke in pretty obvious ways each time. I warned him at the beginning that he needed to spend a couple of hours wrapping his head around the code before trying to modify it. I warned him that the code arrived at its current state after long and painful experience. I warned him that other developers had worked on the code, had exactly his initial reaction, and eventually admitted they couldn't find a way to improve it. I warned him, after he confidently declared that it just needed to be "more object-oriented," that I had written a lot of object-oriented code, was open to writing it in an object-oriented style if it would improve things, and could not think of any way to do so that would not make things worse. But he could not even bring himself to read the existing code. He never did. He worked on it for three weeks, wrote thousands of lines of code, and even tried to deploy his own version without ever spending a single contiguous, focused, hour-long block of time reading the existing code. He was "refactoring" the whole time. The code he produced was exactly how you imagine it. The code went from several hundred lines in a single file with no classes (just a containing class acting basically as a namespace) to thousands of lines scattered over half a dozen files with at least that many classes. The guy kept adding classes and kept adding test after test after test. He couldn't get his code to pass the test cases I had written, so several times he declared that my test cases were wrong and changed or removed assertions. He also claimed that the existing code couldn't "pass" his tests because he had hundreds of lines of tests for components that only existed in his code. According to him, this proved that there was a ton of "hidden untested functionality" in the existing code, which was therefore "dangerous." I was pretty busy with other things, and this was his project now, so if he had been a little bit more clever he probably could have made his abomination stable enough to replace the existing version over my objections. Thankfully it was never good enough to survive in production.
- busterarm 8y agoHonestly, after the experience described, I would consider the person a work hazard and try to make sure they didn't touch anything business critical.
- munchbunny 8y agoI agree. My day job is working on code that isn't this level of critical, but also has the characteristic of being low level, both closer to the metal than typical backend code and also called by so much frontend and backend code that if there was such a thing as "even backend-ier code" this would be a good example. If you miss a nuance, a horde of angry developers will show up at your desk the moment the build deploys, and if you don't fix it fast, that turns into a horde of angry customers. With code that far down in the inner loops of mission critical code, the dominant time cost isn't really writing or even understanding the code, it's ramping up on the nuances of the scenarios you serve, checking the direct effects of your changes, and then checking the second order and third order effects of your changes. When your code is so far down that every change you make has third order effects, it's a lot easier to maintain code that is meticulously commented and written for maximum thoroughness explicitly baked into the file in front of you, because you're much more likely to miss the nuances if you just finished piecing together ten different files to figure out how the happy path works.
- punnerud 8y agoNote to others: 3rd order is not necessarily 10^10^10. It can easily be 10^100^1000.
- deleted 8y ago[deleted]
- aloisamae 8y agoInteresting! Is there a convention to follow?
- aardvark291 8y agoI hope you mean x (multiplication) rather than ^ (exponentiation). If we're talking about fanout (each of 10 items of has 10 subitems, each of which has 10 subitems), multiplication is the relevant operation. And 10^(10^10) or (10^10)^10 is a hopelessly, uselessly, inconceivably large number. (Exponentiation is not associative; 3^(3^3)=3^9= 19683 while (3^3)^3=9^3=729.)
- coding123 8y agoIt's freaking kubernetes, so obviously it will be a well understood file by a LOT of people. However this is the kind of file that also exists in unknown, non-open source products, and while may have been understood by lots of people (at the respective companies) sometimes a period of 5 years of inactivity and layoffs and hiring goes by... By gosh darn it- this is it. It's the reason we have shitty code but great products.
- dragonwriter 8y ago> I love this! It's the "jazz music" of software development. Something which breaks all the "rules" but does so purposefully and explicitly so that it can become better than the "rules" allow. I kind of see it as the opposite: “space shuttle style” is code that adheres to heavyweight rules that most software development has abandoned in favor of a more improvisational style. But in either case it illustrates that code style rules are desirable, or not, based on the context; you need to understand what purpose rules serve and what trade-offs they involve to understand which rules to use; there's no one-size-fits-all solution.
- foobar1962 8y ago> I kind of see it as the opposite: “space shuttle style” is code that adheres to heavyweight rules that most software development has abandoned in favor of a more improvisational style. Most software other than that written for space shuttles and other seriously critical applications. A colleague of mine works for a telco, and changes to software that runs on satellites takes months to approve and goes through verification stages that include running on simulators and duplicate hardware that is on the ground.
- 3pt14159 8y agoI've had this latent thought for a while that I'm finally putting to words: The complexity goes somewhere. It's either into lots tests, or it's into something like shuttle style with lots of comments, or it's into a huge QA department, or it's into the type system / DB schema. It could even be going into the org structure! But something, somewhere is handling the complexity and it is doing so as a partial function to the economic importance of the software to the stakeholders of the software.
- jrumbut 8y agoI would say this is the fundamental problem of making software, and the reason that the whole world could learn JavaScript and yet being able to make great software would still be a rare skill and why even CRUD apps tax the minds of smart people. Making the right component tackle the right amount of the right complexity is just a hard problem, and I think when it looks easy it's only because your options were constrained.
- skybrian 8y agoI don't understand the objection to having more, smaller files, at least in Go where they can all be in the same package. Once two functions are too far apart to be on screen at the same time, jumping back and forth between two functions in the same file doesn't seem any easier than switching between different files. If anything, switching between two different files is easier since they each get an editor tab. On the other hand, being wary about extracting functions (which moves things logically together further apart) makes more sense.
- EpicEng 8y agoFor me, it's less about number of files than it is "hoeany files do I have to open to figure out how something works? How many levels of indirection do I have to keep in my head?" I started out writing low-ish level code. Motion control, image processing, digital imaging, and the application level code that coordinated it all. I've steadily moved up the abstraction tree over the last 13 years and there's one thing I hate about it; too many fundtions and classes which do far too little. Abstraction for abstraction's sake. It's a bit subjective of course and not everyone has their own style, but there is a Starbucks difference in philosophy between the two types of groups.
- deleted 8y ago[deleted]
- AlexCoventry 8y ago> [how many] files do I have to open to figure out how something works? With jump-to-definition editor integration, who cares?
- TeMPOraL 8y agoAnyone who actually worked with code requiring so much jumping. I did, in an IDE, and I can tell you, jump-to-definition removes the problem of "what file do I have to open now?", but still leaves you with the questions like "where am I?", "how did I get here?" and "what was I trying to understand, again?", which you start asking yourself after ~sixth jump.
- pacifika 8y agoStraightforward concepts should be coded consisely and complex concepts should be coded verbosely. The commenting in this file slows down the thinking of the reader to an appropriate level.
- koehler 8y agoTo be honest, I remember myself back during my first dev seps struggling a lot reading code since a lot of the senior programmers tend to code very effectively, so it was extremely hard to follow an end-to-end solution without terribles headaches. This is just brilliant, not only because as educational exercises explains perfectly what's doing but the business/thinking process/context. Never read 1k lines so quickly before, never enjoyed so much.
- oftenwrong 8y ago> It's the "jazz music" of software development. If Anthony Braxton wrote software...