• mihaic 17 hours ago

    When I first heard the maxim that an intelligent person should be able to hold two opposing thoughts at the same time, I was naive to think it meant weighing them for pros and cons. Over time I realized that it means balancing contradictory actions, and the main purpose of experience is knowing when to apply each.

    Concretely related to the topic, I've often found myself inlining short pieces of one-time code that made functions more explicit, while at other times I'll spend days just breaking up thousand line functions into simpler blocks just to be able to follow what's going on. In both cases I was creating inconsistencies that younger developers nitpick -- I know I did.

    My goal in most cases now is to optimize code for the limits of the human mind (my own in low-effort mode) and like to be able to treat rules as guidelines. The trouble is how can you scale this to millions of developers, and what are those limits of the human mind when more and more AI-generated code will be used?

    • tetha 7 hours ago

      I had exactly this discussion today in an architectural discussion about an infrastructure extension today. As our newest team member noted, we planned to follow the reference architecture of a system in some places, and chose not to follow the reference architecture in other places.

      And this led to a really good discussion pulling the reference architecture of this system apart and understanding what it optimizes for (resilience and fault tolerance), what it sacrifices (cost, number of systems to maintain) and what we need. And yes, following the reference architecture in one place and breaking it in another place makes sense.

      And I think that understanding the different options, as well as the optimization goals setting them apart, allows you to make a more informed decision and allows you to make a stronger argument why this is a good decision. In fact, understanding the optimization criteria someone cares about allows you to avoid losing them in topics they neither understand nor care about.

      For example, our CEO will not understand the technical details why the reference architecture is resilient, or why other choices are less resilient. And he would be annoyed about his time being wasted if you tried. But he is currently very aware of customer impacts due to outages. And like this, we can offer a very good argument to invest money in one place for resilience, and why we can save money in other places without risking a customer impact.

      We sometimes follow rules, and in other situations, we might not.

      • mandevil 7 hours ago

        Yes, and it is the engineering experience/skill to know when to follow the "rules" of the reference architecture, and when you're better off breaking them, that's the entire thing that makes someone a senior engineer/manager/architect whatever your company calls it.

        • jschrf 3 hours ago

          Your newest team member sounds like someone worth holding onto.

        • lifeisstillgood 7 hours ago

          I often Bang on about “software is a new form of literacy”. And this I feel is a classic example - software is a form of literacy that not only can be executed by a CPU but also at the same time is a way to transmit concepts from one humans head to another (just like writing)

          And so asking “will AI generated code help” is like asking “will AI generated blog spam help”?

          No - companies with GitHub copilot are basically asking how do I self-spam my codebase

          It’s great to get from zero to something in some new JS framework but for your core competancy - it’s like outsourcing your thinking - always comes a cropper

          (Book still being written)

          • debit-freak 7 hours ago

            I think a lot of the traditional teachings of "rhetoric" can apply to coding very naturally—there's often practically unlimited ways to communicate the same semantics precisely, but how you lay the code out and frame it can make the human struggle to read it straightforward to overcome (or near-impossible, if you look at obfuscation).

            • davidw 5 hours ago

              > is a way to transmit concepts from one humans head to another (just like writing)

              That's almost its primary purpose in my opinion... the CPU does not care about Ruby vs Python vs Rust, it's just executing some binary code instructions. The code is so that other people can change and extend what the system is doing over time and share that with others.

              • rileymat2 3 hours ago

                I get your point, but often the binary code instructions between those is vastly different.

                • davidw 26 minutes ago

                  The fact that we work with the high level languages rather than the binary code, despite all their inefficiencies, speaks to the human aspect being pretty important in the equation.

              • j7ake 4 hours ago

                Computational thinking is more important than software per se.

                Computational thinking is the mathematical thinking.

              • ragnese 11 hours ago

                > My goal in most cases now is to optimize code for the limits of the human mind (my own in low-effort mode) and like to be able to treat rules as guidelines. The trouble is how can you scale this to millions of developers, and what are those limits of the human mind when more and more AI-generated code will be used?

                I think the truth is that we just CAN'T scale that way with the current programming languages/models/paradigms. I can't PROVE that hypothesis, but it's not hard to find examples of big software projects with lots of protocols, conventions, failsafes, QA teams, etc, etc that are either still hugely difficult to contribute to (Linux kernel, web browsers, etc) or still have plenty of bugs (macOS is produced by the richest company on Earth and a few years ago the CALCULATOR app had a bug that made it give the wrong answers...).

                I feel like our programming tools are pretty good for programming in the small, but I suspect we're still waiting for a breakthrough for being able to actually make complex software reliably. (And, no, I don't just mean yet another "framework" or another language that's just C with a fancier type system or novel memory management)

                Just my navel gazing for the morning.

                • twh270 11 hours ago

                  I think the only way this gets better is with software development tools that make it impossible to create invalid states.

                  In the physical world, when we build something complex like a car engine, a microprocessor, or bookcase, the laws of physics guide us and help prevent invalid states. Not all of them -- an upside down bookcase still works -- but a lot of them.

                  Of course, part of the problem is that when we build the software equivalent of an upside down bookcase, we 'patch' it by creating trim and shims to make it look better and more structurally sound instead of tossing it and making another one the right way.

                  But mostly, we write software in a way that allows for a ton of incorrect states. As a trivial example, expressing a person's age as an 'int', allowing for negative numbers. As a more complicated example, allowing for setting a coupon's redemption date when it has not yet been clipped.

                  • bunderbunder 7 hours ago

                    John Backus's Turing Award lecture meditated on this idea, and concluded that the best way to do this at scale is to simply minimize the creation of states in the first place, and be careful and thoughtful about where and how we create the states that can't be avoided.

                    I would argue that that's actually a better guide to how we manage complexity in the physical world. Mechanical engineers generally like to minimize the number of moving parts in a system. When they can't avoid moving parts, they tend to fixate on them, and put a lot of effort into creating linkages and failsafes to try to prevent them from interacting in catastrophic ways.

                    The software engineering way would be to create extra moving parts just because complicated things make us feel smart, and deal with potential adverse interactions among them by posting signs that say "Careful, now!" without clearly explaining what the reader is supposed to be careful of. 50 years later, people who try to stick to the (very sound!) principles that Backus proposed are still regularly dismissed as being hipsters and pedants.

                    • int_19h an hour ago

                      I'd say that the extra moving parts are there in most cases not because someone wanted to "feel smart" (not that it doesn't happen), but to make the pre-existing moving parts do something that they weren't originally supposed to do, because nobody understands how those pre-existing parts work well enough to re-engineer them properly on the schedule that they are given. We are an industry that builds bridges out of matchsticks, duck tape, and glue, and many of our processes are basically about how to make the result of that "good enough".

                    • james_marks 9 hours ago

                      To determine what states should be possible is the act of writing software.

                    • bluGill 10 hours ago

                      I don't think we will ever get the breakthrough you are looking for. Things like design patterns and abstractions are our attempt at this. Eventually you need to trust that however wrote the other code you have to deal with is sane. This assumption is false (and it might be you who is insane thinking they could/would make it work they way you think it does).

                      We will never get rid of the need for QA. Automated tests are great, I believe in them (Note that I didn't say unit tests or integration tests). Formal proofs appear great (I have never figured out how to prove my code), but as Knuth said "Beware of bugs in the above code; I have only proved it correct, not tried it". There are many ways code can be meet the spec and yet wrong because in the real world you rarely understand the problem well enough to write a correct spec in the first place. QA should understand the problem well enough to say "this isn't what I expected to happen."

                      • austin-cheney 11 hours ago

                        I suppose that depends on the language and the elegance of your programming paradigm. This is where primitive simplicity becomes important, because when your foundation is composed of very few things that are not dependent upon each other you can scale almost indefinitely in every direction.

                        Imagine you are limited to only a few ingredients in programming: statements, expressions, functions, objects, arrays, and operators that are not overloaded. That list does not contain classes, inheritance, declarative helpers, or a bunch of other things. With a list of ingredients so small no internal structure or paradigm is imposed on you, so you are free to create any design decisions that you want. Those creative decisions about the organization of things is how you dictate the scale of it all.

                        Most people, though, cannot operate like that. They claim to want the freedom of infinite scale, but they just need a little help. With more help supplied by the language, framework, whatever the less freedom you have to make your own decisions. Eventually there is so much help that all you do as a programmer is contend with that helpful goodness without any chance to scale things in any direction.

                        • mgsouth 3 hours ago

                          We've been there, done that. CRUD apps on mainframes and minis had incredibly powerful and productive languages and frameworks (Quick, Quiz, QTP: you're remembered and missed.) Problem is, they were TUI (terminal UI), isolated, and extremely focused; i.e. limited. They functioned, but would be like straight-jackets to modern users.

                          (Speaking of... has anyone done a 80x24 TUI client for HN? That would be interesting to play with.)

                          • DSMan195276 10 hours ago

                            > protocols, conventions, failsafes, QA teams, etc, etc that are either still hugely difficult to contribute to (Linux kernel, web browsers, etc)

                            To be fair here, I don't think it's reasonable to expect that once you have "software development skills" it automatically gives you the ability to fix any code out there. The Linux Kernel and web browsers are not hard to contribute to because of conventions, they're hard because most of that code requires a lot of outside knowledge of things like hardware or HTML spec, etc.

                            The actual submitting part isn't the easiest, but it's well documented if you go looking, I'm pretty sure most people could handle it if they really had a fix they wanted to submit.

                            • ragnese 10 hours ago

                              There are multiple reasons that contributing to various projects may be difficult. But, I was replying to a specific comment about writing code in a way that is easy to understand, and the comment author's acknowledgement that this idea/practice is hard to scale to a large number of developers (presumably because everyone's skills are different and because we each have different ideas about what is "clear", etc).

                              So, my comment was specifically about code. Yes, developing a kernel driver requires knowledge of the hardware and its quirks. But, if we're just talking about the code, why shouldn't a competent C developer be able to read the code for an existing hardware driver and come away understanding the hardware?

                              And what about the parts that are NOT related to fiddly hardware? For example, look at all of the recent drama with the Linux filesystem maintainer(s) and interfacing with Rust code. Forget the actual human drama aspect, but just think about the technical code aspect: The Rust devs can't even figure out what the C code's semantics are, and the lead filesystem guy made some embarrassing outbursts saying that he wasn't going to help them by explaining what the actual interface contracts are. It's probably because he doesn't even know what his own section of the kernel does in the kind of detail that they're asking for... That last part is my own speculation, but these Rust guys are also competent at working with C code and they can't figure out what assumptions are baked into the C APIs.

                              Web browser code has less to do with nitty gritty hardware. Yet, even a very competent C++ dev is going to have a ton of trouble figuring out the Chromium code base. It's just too hard to keep trying to use our current tools for these giant, complex, software projects. No amount of convention or linting or writing your classes and functions to be "easy to understand" is going to really matter in the big picture. Naming variables is hard and important to do well, but at the scale of these projects, individual variable names simply don't matter. It's hard to even figure out what code is being executed in a given context/operation.

                              • tomjakubowski 5 hours ago

                                > Yet, even a very competent C++ dev is going to have a ton of trouble figuring out the Chromium code base.

                                I don't think this is true, or at least it wasn't circa 2018 when I was writing C++ professionally and semi-competently. I sometimes had to read, understand and change parts of the Chromium code base since I was working on a component which integrated CEF. Over time I began to think of Chromium as a good reference for how to maintain a well-organized C++ code base. It's remarkably plain and understandable, greppable even. Eventually I was able to contribute a patch or two back to CEF.

                                The hardest thing by far with respect to making those contributions wasn't understanding the C++, it was understanding how to work the build system for development tasks.

                                • TheHegemon 2 hours ago

                                  Also agree that the example code base is not the best example to use.

                                  The Chromium code base is a joy to read and I would routinely spend hours just reading it to understand deeper topics relating to the JS runtime.

                                  Compared to my company's much smaller code base that would take hours just to understand the most simplest things because it was written so terribly.

                            • madisp 8 hours ago

                              calculator app on latest macos (sequoia) has a bug today - if you write FF_16 AND FF_16 in the programmer mode and press =, it'll display the correct result - FF_16, but the history view displays 0_16 AND FF_16 for some reason.

                              • JadeNB 2 hours ago

                                > macOS is produced by the richest company on Earth and a few years ago the CALCULATOR app had a bug that made it give the wrong answers...

                                This is stated as if surprising, presumably because we think of a calculator app as a simple thing, but it probably shouldn't be that surprising--surely the calculator app isn't used that often, and so doesn't get much in-the-field testing. Maybe you've occasionally used the calculator in Spotlight, but have you ever opened the app? I don't think I have in 20 years.

                                • knodi 9 hours ago

                                  > I feel like our programming tools are pretty good for programming in the small, but I suspect we're still waiting for a breakthrough for being able to actually make complex software reliably. (And, no, I don't just mean yet another "framework" or another language that's just C with a fancier type system or novel memory management)

                                  Readability is for human optimization for self or for other people's posterity and code comprehension to the readers mind. We need a new way to visualize/comprehension code that doesn't involve heavy reading and the read's personal capabilities of syntax parsing/comprehension.

                                  This is something we will likely never be able to get right with our current man machine interfaces; keyboard, mouse/touch, video and audio.

                                  Just a thought. As always I reserve the right to be wrong.

                                  • skydhash 7 hours ago

                                    Reading is more than enough. What’s often lacking is usually the why? I can understand the code and what it’s doing, but I may not understand the problem (and sub problems) it’s solving . When you can find explanations for that (links to PR discussions, archives of mail threads, and forums post), it’s great. But some don’t bother or it’s somewhere in chat logs.

                                • codeflo 16 hours ago

                                  There’s also the effect that a certain code structure that’s clearer for a senior dev might be less clear for a junior dev and vice versa.

                                  • rob74 16 hours ago

                                    Or rather, senior devs have learned to care more for having clear code rather than (over-)applying principles like DRY, separation of concerns etc., while juniors haven't (yet)...

                                    • JauntyHatAngle 16 hours ago

                                      I know it's overused, but I do find myself saying YAGNI to my junior devs more and more often, as I find they go off on a quest for the perfect abstraction and spend days yak shaving as a result.

                                      • silisili 15 hours ago

                                        Yes! I work with many folks objectively way younger and smarter than me. The two bad habits I try to break them of are abstractions and what ifs.

                                        They spend so much time chasing perfection that it negatively affects their output. Multiple times a day I find myself saying 'is that a realistic problem for our use case?'

                                        I don't blame them, it's admirable. But I feel like we need to teach YAGNI. Anymore I feel like a saboteur, polluting our codebase with suboptimal solutions.

                                        It's weird because my own career was different. I was a code spammer who learned to wrangle it into something more thoughtful. But I'm dealing with overly thoughtful folks I'm trying to get to spam more code out, so to speak.

                                        • reshlo 15 hours ago

                                          I’ve had the opposite experience before. As a young developer, there were a number of times where I advocated for doing something “the right way” instead of “the good enough way”, was overruled by seniors, and then later I had to fix a bug by doing it “the right way” like I’d wanted to in the first place.

                                          Doing it the right way from the start would have saved so much time.

                                          • spinningslate 15 hours ago

                                            This thread is a great illustration of the reality that there are no hard rules, judgement matters, and we don't always get things right.

                                            I'm pretty long-in-the-tooth and feel like I've gone through 3 stages in my career:

                                            1. Junior dev where everything was new, and did "the simplest thing that could possibly work" because I wasn't capable of anything else (I was barely capable of the simple thing).

                                            2. Mid-experience, where I'd learned the basics and thought I knew everything. This is probably where I wrote my worst code: over-abstracted, using every cool language/library feature I knew, justified on the basis of "yeah, but it's reusable and will solve lots of stuff in future even though I don't know what it is yet".

                                            3. Older and hopefully a bit wiser. A visceral rejection of speculative reuse as a justification for solving anything beyond the current problem. Much more focus on really understanding the underlying problem that actually needs solved: less interest in the latest and greatest technology to do that with, and a much larger appreciation of "boring technology" (aka stuff that's proven and reliable).

                                            The focus on really understanding the problem tends to create more stable abstractions which do get reused. But that's emergent, not speculative ahead-of-time. There are judgements all the way through that: sometimes deciding to invest in more foundational code, but by default sticking to YAGNI. Most of all is seeing my value not as weilding techno armageddon, but solving problems for users and customers.

                                            I still have a deep fascination with exploring and understanding new tech developments and techniques. I just have a much higher bar to adopting them for production use.

                                            • ryandrake 11 hours ago

                                              We all go through that cycle. I think the key is to get yourself through that "complex = good" phase as quickly as possible so you do the least damage and don't end up in charge of projects while you're in it. Get your "Second System" (as Brooks[1] put it) out of the way as quick as you can, and move on to the more focused, wise phase.

                                              Don't let yourself fester in phase 2 and become (as Joel put it) an Architecture Astronaut[2].

                                              1: https://en.wikipedia.org/wiki/Second-system_effect

                                              2: https://www.joelonsoftware.com/2001/04/21/dont-let-architect...

                                              • lcnPylGDnU4H9OF 3 hours ago

                                                Heh, I've read [2] before but another reading just now had this passage stand out:

                                                > Another common thing Architecture Astronauts like to do is invent some new architecture and claim it solves something. Java, XML, Soap, XmlRpc, Hailstorm, .NET, Jini, oh lord I can’t keep up. And that’s just in the last 12 months!

                                                > I’m not saying there’s anything wrong with these architectures… by no means. They are quite good architectures. What bugs me is the stupendous amount of millennial hype that surrounds them. Remember the Microsoft Dot Net white paper?

                                                Nearly word-for-word the same thing could be said about JS frameworks less than 10 years ago.

                                                • whstl an hour ago

                                                  Both React and Vue are older than 10 years old at this point. Both are older than jQuery was when they were released, and both have a better backward compatibility story. The only two real competitors not that far behind. It's about time for this crappy frontend meme to die.

                                                  Even SOAP didn't really live that long before it started getting abandoned en masse for REST.

                                                  As someone who was there in the "last 12 months" Joel mentions, what happened in enterprise is like a different planet altogether. Some of this technology had a completely different level of complexity that to this day I am not able to grasp, and the hype was totally unwarranted, unlike actual useful tech like React and Vue (or, out of that list, Java and .NET).

                                              • jcgrillo 10 hours ago

                                                > The focus on really understanding the problem tends to create more stable abstractions which do get reused. But that's emergent, not speculative ahead-of-time.

                                                I think this takes a kind of humility you can't teach. At least it did for me. To learn this lesson I had to experience in reality what it's actually like to work on software where I'd piled up a bunch of clever ideas and "general solutions". After doing this enough times I realized that there are very few general solutions to real problems, and likely I'm not smart enough to game them out ahead of time, so better to focus on things I can actually control.

                                                • james_marks 9 hours ago

                                                  > Most of all is seeing my value not as wielding techno armageddon, but solving problems for users and customers

                                                  Also later in my career, I now know: change begets change.

                                                  That big piece of new code that “fixes everything” will have bugs that will only be discovered by users, and stability is achieved over time through small, targeted fixes.

                                                • pjmlp 12 hours ago

                                                  Here is an unwanted senior tip, in many consulting projects without the “the good enough way” first, there isn't anything left for doing “the right way” later on.

                                                  • wild_egg 14 hours ago

                                                    The important bit is figuring out if those times where "the right way" would have helped outweigh the time saved by defaulting to "good enough".

                                                    There are always exceptions, but there's typically order of magnitude differences between globally doing "the right thing" vs "good enough" and going back to fix the few cases where "good enough" wasn't actually good enough.

                                                    • bluGill 10 hours ago

                                                      Only long experience can help you figure this out. All projects should have at least 20% of the developers who have been there for more than 10 years so they have background context to figure out what you will really need. You then need at least 30% of your developers to be intended to be long term employees but they have less than 10 years. In turn that means never more than 50% of your project should be short term contractors. Nothing wrong with short term contractors - they often can write code faster than the long term employees (who end up spending a lot more time in meetings) - but their lack of context means that they can't make those decisions correctly and so need to ask (in turn slowing down the long term employees even more)

                                                      If you are on a true green field project - your organization has never done this before good luck. Do the best you can but beware that you will regret a lot. Even if you have those long term employees you will do things you regret - just not as much.

                                                    • sdeframond 14 hours ago

                                                      > then later I had to fix a bug

                                                      How much later? Is it possible that by delivering sooner your team was able to gain insight and/or provide value sooner? That matters!

                                                      • JasserInicide 11 hours ago

                                                        Everything in moderation, even moderation.

                                                      • pastaguy1 3 hours ago

                                                        This isn't meant to be taken too literally or objectively, but I view YAGNI as almost a meta principle with respect to the other popular ones. It's like an admission that you won't always get them right, so in the words of Bukowski, "don't try".

                                                      • james_marks 9 hours ago

                                                        Agreed. I’ve been trying to dial in a rule of thumb:

                                                        If you aren’t using the abstraction on 3 cases when you build it, it’s too early.

                                                        Even two turns into a higher bar than I expected.

                                                        • randomdata 8 hours ago

                                                          Your documentation will tell when you need an abstraction. Where there is something relevant to document, there is a relevant abstraction. If its not worth documenting, it is not worth abstracting. Of course, the hard part is determining what is actually relevant to document.

                                                          The good news is that programmers generally hate writing documentation and will avoid it to the greatest extent possible, so if one is able to overcome that friction to start writing documentation, it is probably worthwhile.

                                                          Thus we can sum the rule of thumb up to: If you have already started writing documentation for something, you are ready for an abstraction in your code.

                                                          • recursive 8 hours ago

                                                            It's more case by case for me. A magic number should get a named constant on its first use. That's an abstraction.

                                                            • spc476 2 hours ago

                                                              C++ programmers decided against NULL, and for well over a decade, recommended using a plain 0. It was only recently that they came up with a new name: nullptr. Sigh.

                                                              • int_19h an hour ago

                                                                That had to do with the way NULL was defined, and the implications of that. The implication carried over from C was that NULL would always be null pointer as opposed to 0, but in practice the standard defined it simply as 0 - because C-style (void*)0 wasn't compatible with all pointer types anymore - so stuff like:

                                                                   void foo(void*);
                                                                
                                                                   void foo(int); 
                                                                
                                                                   foo(NULL);
                                                                
                                                                would resolve to foo(int), which is very much contrary to expectations for a null pointer; and worse yet, the wrong call happens silently. With foo(0) that behavior is clearer, so that was the justification to prefer it.

                                                                On the other hand, if you accept the fact that NULL is really just an alias for 0 and not specifically a null pointer, then it has no semantic meaning as a named constant (you're literally just spelling the numeric value with words instead of digits!), and then it's about as useful as #define ONE 1

                                                                And at the same time, that was the only definition of NULL that was backwards compatible with C, so they couldn't just redefine it. It had to be a new thing like nullptr.

                                                                It is very unfortunate that nullptr didn't ship in C++98, but then again that was hardly the biggest wart in the language at the time...

                                                        • stahorn 16 hours ago

                                                          When you thought you made "smart" solutions and many years later you have to go in and fix bugs in it, is usually when you learn this.

                                                          • newswasboring 14 hours ago

                                                            There is a human side to this which I am going through right now. The first full framework I made is proving to be developer unfriendly in the long run, I put more emphasis on performance than readability (performance was the KPI we were trying to improve at the time). Now I am working with people who are new to the codebase, and I observed they were hesitant to criticize it in front of me. I had to actively start saying "lets remove <frame work name>, its outdated and bad". Eventually I found it liberating, it also helped me detach my self worth from my work, something I struggle with day to day.

                                                          • zeroq 10 hours ago

                                                            As someone who recently had to go over a large chunk of code written by myself some 10-15 years ago I strongly agree with this sentiment. Despite being a mature programmer already at that time, I found a lot of magic and gotchas that were supposed to be, and felt at the time, super clever, but now, without a context, or prior version to compare, they are simply overcomplicated.

                                                            • orwin 16 hours ago

                                                              My 'principle' for DRY is : twice is fine, trice is worth an abstraction (if you think it has a small to moderate chance to happen again). I used to apply it no matter what, soi guess it's progress...

                                                              • whstl 15 hours ago

                                                                I really dislike how this principle ends up being used in practice.

                                                                A good abstraction that makes actual sense is perfectly good even when it's used only once.

                                                                On the other hand, the idea of deduplicating code by creating an indirection is often not worth it for long-term maintenance, and is precisely the kind of thing that will cause maintenance headaches and anti-patterns.

                                                                For example: don't mix file system or low-level database access with your business code, just create a proper abstraction. But deduplicating very small fragments of same-abstraction-level can have detrimental effects in the long run.

                                                                • ahoka 15 hours ago

                                                                  I think the main problem with these abstractions that they are merely indirections in most cases, limiting the usefulness to several use cases (sometimes to things that never going to be needed).

                                                                  To quote Dijsktra: "The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise."

                                                                  • n0w 15 hours ago

                                                                    I can't remember where I picked it up from, but nowadays I try to be mindful of when things are "accidentally" repeated and when they are "necessarily" repeated. Abstractions that encapsulate the latter tend to be a good idea regardless of how many times you've repeated a piece of code in practice.

                                                                    • codeflo 15 hours ago

                                                                      Exactly, but distinguishing the two that requires an excellent understanding of the problem space, and can’t at all be figured out in the solution space (i.e., by only looking at the code). But less experienced people only look at the code. In theory, a thousand repetitions would be fine if each one encodes an independent bit of information in the problem space.

                                                                    • loup-vaillant 10 hours ago

                                                                      The overarching criterion really is how it affects locality of behaviour: repeating myself and adding an indirection are both bad, the trick is to pick the one that will affect locality of behaviour the least.

                                                                      https://loup-vaillant.fr/articles/source-of-readability#avoi...

                                                                    • syntaxfree 11 hours ago

                                                                      WET, write everything twice

                                                                      • aitchnyu 9 hours ago

                                                                        Do you use a copy paste detector to find third copy?

                                                                        • tomohawk 14 hours ago

                                                                          "Better a little copying than a little dependency" - Russ Cox

                                                                          • ikari_pl 15 hours ago

                                                                            twice is fine... except some senior devs apply it to the entire file (today I found the second entire file/class copied and pasted over to another place... the newer copy is not used either)

                                                                          • devjab 9 hours ago

                                                                            I find that it’s typically the other way around as things like DRY, SOLID and most things “clean code” are hopeless anti-patterns peddled by people like Uncle Bob who haven’t actually worked in software development since Fortran was the most popular language. Not that a lot of these things are bad as a principle. They come with a lot of “okish” ideas, but if you follow them religiously you’re going to write really bad code.

                                                                            I think the only principle in programming I think can be followed at all times is YAGNI (you aren’t going to need it). I think every programming course, book, whatever should start by telling you to never, ever, abstract things before you absolutely can’t avoid it. This includes DRY. It’s a billion times better to have similar code in multiple locations that are isolated in their purpose, so that down the line, two-hundred developers later you’re not sitting with code where you’ll need to “go to definition” fifteen times before you get to the code you actually need to find.

                                                                            Of course the flip-side is that, sometimes, it’s ok to abstract or reuse code. But if you don’t have to, you should never ever do either. Which is exactly the opposite of what junior developers do, because juniors are taught all these “hopeless” OOP practices and they are taught to mindlessly follow them by the book. Then 10 years later (or like 50 years in the case of Uncle Bob) they realise that functional programming is just easier to maintain and more fun to work with because everything you need to know is happening right next to each other and not in some obscure service class deep in some ridiculous inheritance tree.

                                                                            • int_19h 29 minutes ago

                                                                              The problem with repeating code in multiple places is that when you find a bug in said code, it won't actually be fixed in all the places where it needs to be fixed. For larger projects especially, it is usually a worthwhile tradeoff versus having to peel off some extra abstraction layers when reading the code.

                                                                              The problems usually start when people take this as an opportunity to go nuts on generalizing the abstraction right away - that is, instead of refactoring the common piece of code into a simple function, it becomes a generic class hierarchy to cover all conceivable future cases (but, somehow, rarely the actual future use case, should one arise in practice).

                                                                              Most of this is just cargo cult thinking. OOP is a valid tool on the belt, and it is genuinely good at modelling certain things - but one needs to understand why it is useful there to know when to reach for it and when to leave it alone. That is rarely taught well (if at all), though, and even if it is, it can be hard to grok without hands-on experience.

                                                                            • sgu999 12 hours ago

                                                                              good devs*, not all senior devs have learned that, sadly. As a junior dev I've worked under the rule of senior devs who were over-applying arbitrary principles, and that wasn't fun. Some absolute nerds have a hard time understanding where their narrow expertise is meant to fit, and they usually don't get better with age.

                                                                            • kolinko 16 hours ago

                                                                              I bumped into that issue, and it caused a lot of friction between me and 3 young developers I had to manage.

                                                                              Ideas on how to overcome that?

                                                                              • whstl 15 hours ago

                                                                                Teaching.

                                                                                I had this problem with an overzealous junior developer and the solution was showing some different perspectives. For example John Ousterhout's A Philosophy of Software Design.

                                                                                • thelastparadise 13 hours ago

                                                                                  I tried this but they just come back with retorts like "OK boomer" which tends to make the situation even worse.

                                                                                  How do you respond to that?

                                                                                  • garblegarble 12 hours ago

                                                                                    The sibling comment says "fire them". That sounds glib, but it's the correct solution here.

                                                                                    From what you've described, you have a coworker who is not open to learning and considering alternative solutions. They are not able to defend their approach, and are instead dismissive (and using an ageist joke to do it). This is toxic to a collaborative work environment.

                                                                                    I give some leeway to assholes who can justify their reasoning. Assholes who just want their way because it's their way aren't worth it and won't make your product better.

                                                                                    • yodsanklai 11 hours ago

                                                                                      This is a discriminatory statement and it should be taken seriously.

                                                                                      • cbrozefsky 12 hours ago

                                                                                        Fire them.

                                                                                        • AnimalMuppet 9 hours ago

                                                                                          Or, perhaps better, just let that hang for a moment - long enough to become uncomfortable - and then say "Try again."

                                                                                          As others have said, if they can't or won't get that that's unacceptable behavior, fire them. (jerf is more patient than I am...)

                                                                                          • jerf 10 hours ago

                                                                                            To be honest, at the point where they are being insulting I also agree firing them is a very viable alternative.

                                                                                            However, to answer the question more generally, I've had some success first acknowledging that I agree the situation is suboptimal, and giving some of the reasons. These reasons vary; we were strapped for time, we simply didn't know better yet, we had this and that specific problem to deal with, sometimes it's just straight up "yeah I inherited that code and would never have done that", honestly.

                                                                                            I then indicate my willingness to spend some time fixing the issues, but make it clear that there isn't going to be a Big Bang rewriting session, but that we're going to do it incrementally, with the system working the whole time, and they need to conceive of it that way. (Unless the situation is in the rare situation where a rewrite is needed.) This tends to limit the blast radius of any specific suggestion.

                                                                                            Also, as a senior engineer, I do not 100% prioritize "fixing every single problem in exactly the way I'd do it". I will selectively let certain types of bad code through so that the engineer can have experience of it. I may not let true architecture astronautics through, but as long as it is not entirely unreasonable I will let a bit more architecture than perhaps I would have used through. I think it's a common fallacy of code review to think that the purpose of code review is to get the code to be exactly as "I" would have written it, but that's not really it.

                                                                                            Many people, when they see this degree of flexibility, and that you are not riding to the defense of every coding decision made in the past, and are willing to take reasonable risks to upgrade things, will calm down and start working with you. (This is also one of the subtle reasons automated tests are super super important; it is far better for them to start their refactoring and have the automated tests explain the difficulties of the local landscape to them than a developer just blathering.)

                                                                                            There will be a set that do not. Ultimately, that's a time to admit the hire was a mistake and rectify it appropriately. I don't believe in the 10x developer, but not for the usual egalitarian reasons... for me the problem is I firmly, firmly believe in the existence of the net-negative developer, and when you have those the entire 10x question disappears. Net negative is not a permanent stamp, the developer has the opportunity to work their way out of it, and arguably, we all start there both as a new developer and whenever we start a new job/position, so let me sooth the egalitarian impulse by saying this is a description of someone at a point in time, not a permanent label to be applied to anyone. Nevertheless, someone who insists on massive changes, who deploys morale-sapping insults to get their way, whose ego is tied up in some specific stack that you're not using and basically insists either that we drop everything and rewrite now "or else", who one way or another refuses to leave "net negative" status... well, it's time to take them up on the "or else". I've exaggerated here to paint the picture clearly in prose, but, then again, of the hundreds of developers I've interacted with to some degree at some point, there's a couple that match every phrase I gave, so it's not like they don't exist at all either.

                                                                                            • ziml77 9 hours ago

                                                                                              You mean they literally say "ok boomer"? If so they are not mature enough for the job. That phrase is equivalent to "fuck off" with some ageism slapped on top and is totally unacceptable for a workplace.

                                                                                      • peepee1982 14 hours ago

                                                                                        That's exactly what I try to do. I think it's an unpopular opinion though, because there are no strict rules that can be applied, unlike with pure ideologies. You have to go by feel and make continuous adjustments, and there's no way to know if you did the right thing or not, because not only do different human minds have different limits, but different challenges don't tax every human mind to the same proportional extent.

                                                                                        I get the impressions that programmers don't like ambiguity in general, let alone in things they have to confront in real life.

                                                                                        • mr_toad 13 hours ago

                                                                                          > there are no strict rules that can be applied

                                                                                          The rules are there for a reason. The tricky part is making sure you’re applying them for that reason.

                                                                                          • peepee1982 13 hours ago

                                                                                            I don't know what your comment has to do with my comment.

                                                                                        • hnuser123456 6 hours ago

                                                                                          On a positive note, most AI-gen code will follow a style that is very "average" of everything it's seen. It will have its own preferred way of laying out the code that happens to look like how most people using that language (and sharing the code online publicly), use it.

                                                                                          • gspencley 8 hours ago

                                                                                            My intro to programming was that I wanted to be a game developer in the 90s. Carmack and the others at Id were my literal heroes.

                                                                                            Back then, a lot of code optimizations was magic to me. I still just barely understand the famous inverse square root optimization in the Quake III Arena source code. But I wanted to be able to do what those guys were doing. I wanted to learn assembly and to be able to drop down to assembly and to know where and when that would help and why.

                                                                                            And I wasn't alone. This is because these optimizations are not obvious. There is a "mystique" to them. Which makes it cool. So virtually ALL young, aspiring game programmers wanted to learn how to do this crazy stuff.

                                                                                            What did the old timers tell us?

                                                                                            Stop. Don't. Learn how to write clean, readable, maintainable code FIRST and then learn how to profile your application in order to discover the major bottlenecks and then you can optimize appropriately in order of greatest impact descending.

                                                                                            If writing the easiest code to maintain and understand also meant writing the most performant code, then the concept of code optimization wouldn't even exist. The two are mutually exclusive, except in specific cases where it's not and then it's not even worth discussing because there is no conflict.

                                                                                            Carmack seems to acknowledge this in his email. He realizes that inlining functions needs to be done with careful judgment, and the rationale is both performance and bug mitigation. But that if inlining were adopted as a matter of course, a policy of "always inline first", the results would quickly be an unmaintainable, impossible to comprehend mess that would swing so far in the other direction that bugs become more prominent because you can't touch anything in isolation.

                                                                                            And that's the bane of software development: touch one thing and end up breaking a dozen other things that you didn't even think about because of interdependence.

                                                                                            So we've come up with design patterns and "best practices" that allow us to isolate our moving parts, but that has its own set of trade-offs which is what Carmack is discussing.

                                                                                            Being a 26 year veteran in the industry now (not making games btw), I think this is the type of topic that you need to be very experienced to be able to appreciate, let alone to be able to make the judgment calls to know when inlining is the better option and why.

                                                                                            • SoftTalker 6 hours ago

                                                                                              > other times I'll spend days just breaking up thousand line functions into simpler blocks just to be able to follow what's going on

                                                                                              Absolutely, I'll break up a long block of code into several functions, even if there is nowhere else they will be called, just to make things easier to understand (and potentially easier to test). If a function or procedure does not fit on one screen, I will almost always break it up.

                                                                                              Obviously "one screen" is an approximation, not all screens/windows are the same size, but in practice for me this is about 20-30 lines.

                                                                                              • skummetmaelk 16 hours ago

                                                                                                That doesn't seem like holding two opposing thoughts. Why is balancing contradictory actions to optimize an outcome different to weighing pros and cons?

                                                                                                • mihaic 16 hours ago

                                                                                                  What I meant to say was that when people encounter contradictory statements like "always inline one-time functions" and "breakdown functions into easy to understand blocks", they try to only pick one single rule, even if they consider the pros and cons of each rule.

                                                                                                  After a while they consider both rules as useful, and will move to a more granular case-by-base analysis. Some people get stuck at rule-based thinking though, and they'll even accuse you of being inconsistent if you try to do case-by-case analysis.

                                                                                                • j7ake 7 hours ago

                                                                                                  To make an advance in a field, you must simultaneously believe in what’s currently known as well as distrust that the paradigm is all true.

                                                                                                  This gives you the right mindset to focus on advancing the field in a significant way.

                                                                                                  Believing in the paradigm too much will lead to only incremental results, and not believing enough will not provide enough footholds for you to work on a problem productively.

                                                                                                  • xnx 4 hours ago

                                                                                                    > I was creating inconsistencies that younger developers nitpick

                                                                                                    Obligatory: “A foolish consistency is the hobgoblin of little minds"

                                                                                                    Continued because I'd never read the full passage: "... adored by little statesmen and philosophers and divines. With consistency a great soul has simply nothing to do. He may as well concern himself with his shadow on the wall. Speak what you think now in hard words, and to-morrow speak what to-morrow thinks in hard words again, though it contradict every thing you said to-day. — 'Ah, so you shall be sure to be misunderstood.' — Is it so bad, then, to be misunderstood? Pythagoras was misunderstood, and Socrates, and Jesus, and Luther, and Copernicus, and Galileo, and Newton, and every pure and wise spirit that ever took flesh. To be great is to be misunderstood.” ― Ralph Waldo Emerson, Self-Reliance: An Excerpt from Collected Essays, First Series

                                                                                                    • leoh 15 hours ago

                                                                                                      You are probably reaching for Hegel’s concept of dialectical reconciliation

                                                                                                      • mihaic 14 hours ago

                                                                                                        Not sure, didn't Hegel say that there should be a synthesis step at some point? My view is that there should never be a synthesis when using these principles as tools, as both conflicting principles need to always maintain opposites.

                                                                                                        So, more like Heraclitus's union of opposites maybe if you really want to label it?

                                                                                                        • greenie_beans 11 hours ago

                                                                                                          the synthesis would be the outcome maybe? writing code that doesn't follow either rule strictly:

                                                                                                          > Concretely related to the topic, I've often found myself inlining short pieces of one-time code that made functions more explicit, while at other times I'll spend days just breaking up thousand line functions into simpler blocks just to be able to follow what's going on. In both cases I was creating inconsistencies that younger developers nitpick -- I know I did.

                                                                                                      • tomohawk 14 hours ago

                                                                                                        What makes an apprentice successful is learning the rules of thumb and following them.

                                                                                                        What makes a journeyman successful is sticking to the rules of thumb, unless directed by a master.

                                                                                                        What makes a master successful is knowing why the rules of thumb exist, what their limits are, when to not follow them, and being able to make up new rules.

                                                                                                        • moss2 14 hours ago

                                                                                                          That maxim ("an intelligent person should be able to hold two opposing thoughts at the same time") is also used by religious groups to indoctrinate people. Be careful!

                                                                                                          • navane 13 hours ago

                                                                                                            So this maxim can both be used for good and for bad. Extra points for this maxim.

                                                                                                            • wrasee 12 hours ago

                                                                                                              A metamaxim?

                                                                                                            • inopinatus 12 hours ago

                                                                                                              Stretch goal: hold three

                                                                                                              • oofoe 11 hours ago

                                                                                                                Nah. That's what the Monk is for.

                                                                                                                • divs1210 9 hours ago

                                                                                                                  A person of culture, I see.

                                                                                                                  Electric Monks were made for a reason.

                                                                                                                  Surprisingly pertinent to the current discussion.

                                                                                                                  • inopinatus an hour ago

                                                                                                                    apposite to the opposite

                                                                                                                • j_bum 11 hours ago

                                                                                                                  A safe work contribution plan for the year: Hold 1+ (stretch 3) opposing thoughts at a time.

                                                                                                                  • inopinatus an hour ago

                                                                                                                    "hold one opposing thought" could be a zen koan

                                                                                                                • andrewmcwatters 13 hours ago

                                                                                                                  Indoctrination is the exact opposite.

                                                                                                                  • ykonstant 13 hours ago

                                                                                                                    Sure, but the maxim can be used to inject this 'exact opposite', in perfect accordance with the maxim!

                                                                                                                    • crabbone 11 hours ago

                                                                                                                      Maybe "indoctrination" was a poor choice of word here. The problem with this maxim is that it welcomes moral relativism.

                                                                                                                      This can be bad on the assumption that whoever is exposed to the maxim is not a proponent of "virtue ethics" (I use this as a catch-all term for various religious ethics doctrines, the underlying idea is that moral truths are given to people by a divine authority rather than discovered by studying human behavior, needs and happiness). In this situation, the maxim is an invitation to embrace ideas that aren't contradictory to one's own, but that live "outside the system", to put them on equal footing.

                                                                                                                      To make this more concrete, let's suppose the subject of child brides. Some religions have no problem with marrying girls of any age to men of any age. Now, the maxim suggests that no matter what your moral framework looks like, you should accept that under some circumstances it's OK to have child marriages. But, this isn't a contradiction. There's no ethical theory that's not based on divine revelation that would accept such a thing. And that's why, by and large, the Western society came to treat child marriages as a crime.

                                                                                                                      Contradictions are only possible when two parties agree on the premises that led to contradicting conclusion, and, in principle, should be possible to be resolved by figuring out which party had a faulty process that derived a contradicting opinion. Resolving such contradictions is a productive way forward. But, the kind of "disagreement" between religious ethics and "derived" ethics is where the premises are different. So, there can be no way forward in an argument between the two, because the only way the two can agree is if one completely abandons their premises.

                                                                                                                      Essentially, you can think about it as if two teams wanted to compete in some sport. If both are playing soccer, then there's a meaning to winning / losing, keeping the score, being good or bad at the game. But, if one team plays soccer while another team is playing chess... it just doesn't make sense to pit them against each other.

                                                                                                                      • wavemode 10 hours ago

                                                                                                                        > maxim suggests that no matter what your moral framework looks like, you should accept that under some circumstances it's OK to have child marriages

                                                                                                                        You seem to have either misread the maxim, or misunderstood it.

                                                                                                                        The maxim is not that an intelligent person -must- hold two contradictory thoughts in their head at once - rather, that they should be able to. Being "able to" do something, does not mean one does it in all cases.

                                                                                                                        To say that the maxim suggests that someone "should" accept that something that is bad, is sometimes good, is a plain misreading of the text. All it's saying is that people -can- do this, if they so choose.

                                                                                                                        • crabbone 9 hours ago

                                                                                                                          In this context, it doesn't matter if they "must" or "should be able to". No, I didn't misunderstand the maxim. No, I didn't mean that it has to happen in all cases. You are reading something into what I wrote that I didn't.

                                                                                                                          The maxim is not used by religious people to its intended effect. Please read again, if you didn't see it the first time. The maxim is used as a challenge that can be rephrased as: "if you are as intelligent as you claim, then you should be able to accept both what you believe to be true and whatever nonsense I want you to believe to be true."

                                                                                                                    • echelon 13 hours ago

                                                                                                                      As a tool, it's a wedge to break indoctrination and overcome bias. It leads to more pragmatic and less ideological thinking. The subject is compelled to contrast opposing views and consider the merits of each.

                                                                                                                      Any use by ideological groups twists the purpose of the phrase on its head. The quote encourages thinking and consideration. You'd have to turn off your brain for this to have the opposite effect.

                                                                                                                      • darkwater 13 hours ago

                                                                                                                        > Any use by ideological groups twists the purpose of the phrase on its head. The quote encourages thinking and consideration. You'd have to turn off your brain for this to have the opposite effect.

                                                                                                                        Well, it would not be too surprising that it can be used to, for example, make people think that they can trust science and also believe in some almighty, unexplainable by science divine entity.

                                                                                                                        • asenchi 12 hours ago

                                                                                                                          Thoughts like this miss the purpose and significance of the maxim being discussed. Science doesn't disprove an "almighty, unexplainable divine entity" any more than an "almighty, unexplainable divine entity" could also provide science as a means to understand the nature of things.

                                                                                                                          Careful you don't fall into the trap of indoctrination. :)

                                                                                                                          • desdenova 13 hours ago

                                                                                                                            You can trust science, but science doesn't cover all of reality.

                                                                                                                            My imaginary friend does, buy my magic book.

                                                                                                                          • kevin_thibedeau 13 hours ago

                                                                                                                            The US has a statutory rapist and someone who believes in active weather manipulation seated in Congress. It's easy to get the masses to turn off their brains.

                                                                                                                          • astrolx 12 hours ago

                                                                                                                            Doublethink!

                                                                                                                          • hinkley 4 hours ago

                                                                                                                            That’s undoubtedly a Zelda Fitzgerald quote (her husband plagiarized her shamelessly).

                                                                                                                            As a consequence of the Rule of Three, you are allowed to have rules that have one exception without having to rethink the law. All X are Y except for Z.

                                                                                                                            I sometimes call this the Rule of Two. Because it deserves more eyeballs than just being a subtext of another rule.

                                                                                                                            • defaultcompany 10 hours ago

                                                                                                                              > My goal in most cases now is to optimize code for the limits of the human mind (my own in low-effort mode)

                                                                                                                              I think you would appreciate the philosophy of the Grug Brained Developer: https://grugbrain.dev

                                                                                                                              • mjburgess 16 hours ago

                                                                                                                                To a certain sort of person, conversation is a game of arriving at these antithesis statements:

                                                                                                                                   * Inlining code is the best form of breaking up code. 
                                                                                                                                   * Love is evil.
                                                                                                                                   * Rightwing populism is a return to leftwing politics. 
                                                                                                                                   * etc.
                                                                                                                                
                                                                                                                                
                                                                                                                                The purpose is to induce aporia (puzzlement), and hence make it possible to evaluate apparent contradictions. However, a lot of people resent feeling uncertain, and so, people who speak this way are often disliked.
                                                                                                                                • grbsh 10 hours ago

                                                                                                                                  > limits of the human mind when more and more AI-generated code will be used

                                                                                                                                  We already have a technology which scales infinitely with the human mind: abstraction and composition of those abstractions into other abstractions.

                                                                                                                                  Until now, we’ve focused on getting AI to produce correct code. Now that this is beginning to be successful, I think a necessary next step for it to be useful is to ensure it produces well-abstracted and clean code (such that it scales infinitely)

                                                                                                                                  • hibernator149 12 hours ago

                                                                                                                                    Wait, isn't that just Doublethink from 1984? Holding two opposing thoughts is a sign that your mental model of the world is wrong and that it needs to be fixed. Where have you heard that maxim?

                                                                                                                                    • perrygeo 8 hours ago

                                                                                                                                      No you've got it completely backwards. Reality has multiple facets (different statements, all of which can be true) and a mental model that insists on a singular judgement is reductionist, missing the forest for the trees. Light is a wave and a particle. People are capable of good and bad. The modern world is both amazing and unsustainable. etc.

                                                                                                                                      Holding multiple truths is a sign that you understand the problem. Insisting on a singular judgement is a sign that you're just parroting catchy phrases as a short cut to thinking; the real world is rarely so cut and dry.

                                                                                                                                      • HKH2 12 hours ago

                                                                                                                                        It's not referring to cognitive dissonance.

                                                                                                                                    • dang 6 hours ago

                                                                                                                                      Related:

                                                                                                                                      John Carmack on Inlined Code - https://news.ycombinator.com/item?id=39008678 - Jan 2024 (2 comments)

                                                                                                                                      John Carmack on Inlined Code (2014) - https://news.ycombinator.com/item?id=33679163 - Nov 2022 (1 comment)

                                                                                                                                      John Carmack on Inlined Code (2014) - https://news.ycombinator.com/item?id=25263488 - Dec 2020 (169 comments)

                                                                                                                                      John Carmack on Inlined Code (2014) - https://news.ycombinator.com/item?id=18959636 - Jan 2019 (105 comments)

                                                                                                                                      John Carmack on Inlined Code (2014) - https://news.ycombinator.com/item?id=14333115 - May 2017 (2 comments)

                                                                                                                                      John Carmack on Inlined Code (2014) - https://news.ycombinator.com/item?id=12120752 - July 2016 (199 comments)

                                                                                                                                      John Carmack on Inlined Code - https://news.ycombinator.com/item?id=8374345 - Sept 2014 (260 comments)

                                                                                                                                      • kragen 6 hours ago

                                                                                                                                        There is a longer version of this thought-provoking post, also including Carmack's thoughts in 02012, at https://cbarrete.com/carmack.html. But maybe that version has not also had threads about it.

                                                                                                                                        • dang 4 hours ago

                                                                                                                                          It doesn't seem to have, since https://news.ycombinator.com/from?site=cbarrete.com is empty.

                                                                                                                                          Should we change the top link to that URL?

                                                                                                                                          • kragen 4 hours ago

                                                                                                                                            I do think it's a better page, but I wouldn't change the link if I were in charge. On the other hand, I think everyone is grateful that you're in charge of HN and not me. Especially not me. So I think you should use your judgment.

                                                                                                                                      • VyseofArcadia 10 hours ago

                                                                                                                                        > That was a cold-sweat moment for me: after all of my harping about latency and responsiveness, I almost shipped a title with a completely unnecessary frame of latency.

                                                                                                                                        In this era of 3-5 frame latency being the norm (at least on e.g. the Nintendo Switch), I really appreciate a game developer having anxiety over a single frame.

                                                                                                                                        • kllrnohj 4 hours ago

                                                                                                                                          You're over-crediting Carmack and under-crediting current game devs. 3-5 frames might be current end-to-end latency, but that's not what Carmack is talking about. He's just talking about the game loop latency. Even at ~4 frames of end-to-end latency, he'd be talking about an easily avoided 20% regression. That's still huge.

                                                                                                                                          • pragma_x 7 hours ago

                                                                                                                                            To be fair, back in 2014 that was one frame at 60Hz or slower for some titles. At 80-120Hz, 3-5 frames is comparatively similar time.

                                                                                                                                            • 01HNNWZ0MV43FF 7 hours ago

                                                                                                                                              I don't think high frame rates are common outside of PC gaming yet.

                                                                                                                                              Wikipedia indicates the Switch maxes out at 1080p60, and the newest Zelda only at 900p30 even when docked

                                                                                                                                              https://en.m.wikipedia.org/wiki/Nintendo_Switch

                                                                                                                                              • marxisttemp 5 hours ago

                                                                                                                                                I play Fortnite and Call of Duty at 120Hz VRR on Xbox Series X.

                                                                                                                                                • VyseofArcadia 7 hours ago

                                                                                                                                                  I believe both the PS5 and whatever nonsense string of Xs, numbers, and descriptors MS named this gen's console can do 144Hz output. I don't know how many games take advantage of that or whether that refresh rate is common on TVs.

                                                                                                                                                  • philistine 6 hours ago

                                                                                                                                                    60 FPS isn't even promised on PS5 Pro. Most graphically demanding titles still aim for 30 FPS on consoles, with any game able to support 60 FPS consistently worth noting.

                                                                                                                                                    • Narishma 2 hours ago

                                                                                                                                                      What they said is true. There are some games with 120 FPS modes on PS5 and Series X, maybe even series S. That doesn't mean every game (or even most) are like that, just that the hardware supports it. At the end of the day you can't stop developers targeting whatever framerate they want.

                                                                                                                                                • astrobe_ 6 hours ago

                                                                                                                                                  I've heard that a good reaction time is around 200 ms, some experiments seem to confirm this figure [1]. At 60Hz, a frame is displayed every 17 ms.

                                                                                                                                                  So it would take a 12 frames animation and a trained gamer for a couple of frames to make a difference (e.g. push the right button before the animation ends and the opponent's action takes effect).

                                                                                                                                                  [1] https://humanbenchmark.com/tests/reactiontime/statistics

                                                                                                                                                  • sjm 6 hours ago

                                                                                                                                                    Reaction time is completely different to the input latency Carmack is worrying about in his scenario. Imagine if you thought I'm going to move my arm, and 200ms later your arm actually moved. Apply the same to a first-person shooter --- imagine you nudge your mouse slightly, and 200ms later you get some movement on screen. That is ___hugely___ noticeable.

                                                                                                                                                    • harrison_clarke 4 hours ago

                                                                                                                                                      https://www.researchgate.net/publication/266655520_In_the_bl...

                                                                                                                                                      this is for a stylus, but people can detect input latency as low as 1ms (possibly lower)

                                                                                                                                                      with VR, they use the term "motion to photon latency", and if it's over ~20ms, people start getting dizzy. at 200ms, nobody is going to be keeping their lunch down

                                                                                                                                                      google noticed people making fewer searches if they delayed the result by 100ms

                                                                                                                                                      edit: if you want an easy demo, open up vim/nano over ssh, and type something. then try it locally

                                                                                                                                                      • underdeserver 6 hours ago

                                                                                                                                                        I'm not sure this is the right way to look at it. I can't find stats right now, but I recall reading top players making frame-perfect moves in games like Smash Bros. Melee and Rocket League.

                                                                                                                                                        • mywittyname 5 hours ago

                                                                                                                                                          The mistake with focusing on reaction time is that humans can anticipate actions and can perform complex sequences of actions pretty quickly (we have two hands and 10 fingers). So someone playing one of those "test your reaction time" games might only score like 30ms. But someone playing a musical instrument can still play a 64th note at 120BPM.

                                                                                                                                                          Imagine playing a drum that took between 0 and 5 extra frames at 60FPS between striking the head and it producing a sound. Most people would notice that kind of delay, even if they can't "react" that quickly.

                                                                                                                                                          In games, frame delay translates to having to hold down a key (or wait before pressing the next one) for longer than is strickly necessary in order to produce an effect. Since fighting games are all about key sequences, the difference between needing to hold key for 0 frames and 5 frames is massive when you consider key combinations might be sequences of up to 5 key presses. 5 frames of delay x five sequential key presses x 8ms a frame = 1600ms vs 1 frame x 5 seq. key presses x 8ms = 40ms.

                                                                                                                                                          There's a massive difference between taking 1.6s to execute a complex move and 0.040s.

                                                                                                                                                          • leni536 4 hours ago

                                                                                                                                                            Another example is music (and relatedly, rythm games). With memorized music you have maximal anticipation of actions. The regular rithm only amplifies that anticipation. Musicians can be very consistent at timing (especially rithm section), and very little latency or jitter can throw that off.

                                                                                                                                                          • philistine 6 hours ago

                                                                                                                                                            Frame perfect moves are exceedingly common in most top fields. Just watch any video about the latest speedruns.

                                                                                                                                                            The thing with latency is it needs to be consistent. If your latency is between 3 to 5 frames you blew it because you can't guarantee the same experience on every button press. If you always have 3 frames of latency, with modern screens, analog controls, and game design aware of those limitations, that's much better. Look at modern games like Celeste, who has introduced Coyote Time to account for all the latency of our modern hardware.

                                                                                                                                                        • munificent 6 hours ago

                                                                                                                                                          Why would you even bother running at a game at 120Hz if the user's response to what's being drawn is effectively 24-30 FPS?

                                                                                                                                                          • kragen 5 hours ago

                                                                                                                                                            You've seen games running at 120Hz and at 60Hz. The difference is obvious, isn't it? The difference between 24Hz and 60Hz is certainly obvious: that's the visual difference between movies and TV sitcoms.

                                                                                                                                                            I can type about 90 words per minute on QWERTY, which is about 8 keystrokes per second. That means that the average interval between keystrokes is about 120 milliseconds, already significantly less than my 200-millisecond reaction time, and many keystrokes are closer together than that—but I rarely make typographical errors. Fast typists can hit 150 words per minute. Performing musicians consistently nail note timing to within about 40 milliseconds. So it turns out that people do routinely time their physical movements a lot more precisely than their reaction time. Their jitter is much lower than their latency, a phenomenon you are surely familiar with in other contexts, such as netcode for games.

                                                                                                                                                            If someone's latency is 200 milliseconds but its jitter (measured as standard deviation) is 10 milliseconds, then reducing the frame latency from a worst-case 16.7 milliseconds (or 33.3 milliseconds in your 30Hz example) to a worst-case 8.3 milliseconds, and average-case 8.3 milliseconds to average-case 4.2 milliseconds, you're knocking off a whole 0.42 standard deviations off their latency. If they're playing against someone else with the same latency, that 0.42σ advantage is very significant! I think they'll win almost 61% of the time, but I'm not sure of my statistics†.

                                                                                                                                                            See also https://danluu.com/input-lag/#appendix-why-measure-latency:

                                                                                                                                                            > Latency matters! For very simple tasks, people can perceive latencies down to 2 ms or less. Moreover, increasing latency is not only noticeable to users, it causes users to execute simple tasks less accurately. If you want a visual demonstration of what latency looks like and you don’t have a super-fast old computer lying around, check out this MSR demo on touchscreen latency.

                                                                                                                                                            > The most commonly cited document on response time is the nielsen group[sic] article on response times, which claims that latncies[sic] below 100ms feel equivalent and perceived[sic] as instantaneous. One easy way to see that this is false is to go into your terminal and try sleep 0; echo "pong" vs. sleep 0.1; echo "test" (or for that matter, try playing an old game that doesn't have latency compensation, like quake 1, with 100 ms ping, or even 30 ms ping, or try typing in a terminal with 30 ms ping). For more info on this and other latency fallacies, see this document on common misconceptions about latency.

                                                                                                                                                            (The original contains several links substantiating those claims.)

                                                                                                                                                            https://danluu.com/keyboard-latency/#appendix-counter-argume... has a longer explanation.

                                                                                                                                                            ______

                                                                                                                                                            † First I tried sum(rnorm(100000) < rnorm(100000) + 0.42)/1000, which comes to about 61.7 (%). But it's not a consistent 0.42σ of latency being added; it's a random latency of up to 0.83σ, so I tried sum(rnorm(100000) < rnorm(100000) + runif(100000, max=0.83))/1000, which gave the same result. But that's not taking into account that actually both players have latency, so if we model random latency of up to a frame for the 60Hz player with sum(rnorm(100000) + runif(100000, max=1.67) > rnorm(100000) + runif(100000, max=0.83))/1000, we get more like a 60.8% chance that the 120fps player will out-twitch them. I'm sure someone who actually knows statistics can tell me the correct way to model this to get the right answer in closed form, but I'm not sure I could tell the correct closed-form formula from an incorrect one, so I resorted to brute force.

                                                                                                                                                            • munificent 4 hours ago

                                                                                                                                                              > You've seen games running at 120Hz and at 60Hz. The difference is obvious, isn't it?

                                                                                                                                                              Honestly, I have not. I'm not much of a gamer, even though I used to be a game developer.

                                                                                                                                                              Certainly the difference between 30Hz and 60Hz is noticeable.

                                                                                                                                                              Maybe this is just because I'm old school but if it were me, I would absolutely prioritize low latency over high frame rate. When you played an early console game, the controls felt like they were concretely wired to the character on screen in a way that most games I play today lack. There's a really annoying spongey-ness to how games feel that I attribute largely to latency.

                                                                                                                                                              I don't really give a shit about fancy graphics and animation (I prefer 2D games). But I want the controls to feel solid and snappy.

                                                                                                                                                              I also make electronic music and it's the same thing there. Making music on a computer is wonderful and powerful in many ways, but it doesn't have the same immediacy as pushing a button on a hardware synth (well, on most hardware synths).

                                                                                                                                                              • kragen 3 hours ago

                                                                                                                                                                Oh! I assumed that because you were a famous game developer you would hang out with gamers who would proudly show off their 120Hz monitor setups.

                                                                                                                                                                I agree that low latency is more important than high frame rate, and I agree about the snappiness. But low jitter is even more important for that than low latency, and a sufficiently low frame rate imposes a minimum of jitter.

                                                                                                                                                                Music is even less tolerant of latency, and PCM measures its jitter tolerance in single-digit microseconds.

                                                                                                                                                            • marxisttemp 5 hours ago

                                                                                                                                                              You’re still getting more information, which allows you to be more accurate with your inputs e.g. tracking a moving target.

                                                                                                                                                            • chandler5555 6 hours ago

                                                                                                                                                              yeah but when people talk about input lag for consoles its generally still in the 60hz sense, rare for games to be 120hz

                                                                                                                                                              smash brothers ultimate for example runs at 60fps and has 5-6 frames of input lag

                                                                                                                                                            • doctorpangloss 5 hours ago

                                                                                                                                                              > In this era of 3-5 frame latency being the norm (at least on e.g. the Nintendo Switch)

                                                                                                                                                              Which titles is this true for? Have you or anyone else measured?

                                                                                                                                                            • low_tech_love 5 hours ago

                                                                                                                                                              Interesting: this is a 2014 post from Jonathan Blow reproducing a 2014 comment by John Carmack reproducing a 2007 e-mail by the same Carmack reproducing a 2006 conversation (I assume also via e-mail) he had with a Henry Spencer reproducing something else the same Spencer read a while ago and was trying to remember (possibly inaccurately?).

                                                                                                                                                              I wonder what is the actual original source (from Saab, maybe?), and if this indeed holds true?

                                                                                                                                                              • EGreg 3 hours ago

                                                                                                                                                                Is this kind of like 300 was a movie about a Frank Miller novel about a Greek legend about the actual Battle of Thermopylae?

                                                                                                                                                              • ninetyninenine 16 hours ago

                                                                                                                                                                His overall solution highlighted in the intro is that he's moved on from inlining and now does pure functional programming. Inlining is only relevant for him during IO or state changes which he does as minimally as possible and segregates this from his core logic.

                                                                                                                                                                Pure functional programming is the bigger insight here that most programmers will just never understand why there's a benefit there. In fact most programmers don't even completely understand what FP is. To most people FP is just a bunch of functional patterns like map, reduce, filter, etc. They never grasp the true nature of "purity" in functional programming.

                                                                                                                                                                You see this lack of insight in this thread. Most responders literally ignore the fact that Carmack called his email completely outdated and that he mostly does pure FP now.

                                                                                                                                                                • wmanley 14 hours ago

                                                                                                                                                                  Here's the link where he discusses functional programming style:

                                                                                                                                                                  https://web.archive.org/web/20170116040923/http://gamasutra....

                                                                                                                                                                  He does not say that that his email is completely outdated - he just says that calling pure functions is exempt from the inlining rule.

                                                                                                                                                                  He's not off writing pure FP now. His approach is still deeply pragmatic. In the link above he discusses degrees of function purity. "Pure FP" has a whole different connotation - where whole programs are written in that constrained style.

                                                                                                                                                                  • pragma_x 6 hours ago

                                                                                                                                                                    Thank you for this. I appreciate that this (classic) article lays bare the essence of FP without the usual pomp and "use Lisp/Scheme/Haskell already" rhetoric. My takeaway is that FP is mostly about using functions w/o side effects (pure), which can be achieved in any programming language provided you're diligent about it.

                                                                                                                                                                    • ninetyninenine 3 hours ago

                                                                                                                                                                      No functional programming is about programming as if your code is a math equation.

                                                                                                                                                                      In math people never use procedures. They write definitions in math in terms of formulas and expressions.

                                                                                                                                                                      If you can get everything to fit on one line in your programming. Then you are doing functional programming.

                                                                                                                                                                      The lack of side effects, lack of mutation and high modularity are the beneficial outcome of fp, it is not the core of what you're doing. The core of what you're doing is your defining your program as a formula/equation/expression rather then a list of procedures or steps. Of course, why you would write your program this way is because of the beneficial outcomes.

                                                                                                                                                                      By coincidence if you write your code in a way where you just account for the side effects like deliberately avoiding mutation, IO and side effects... then your program will become isomorphic to a mathematical function. So it goes both ways.

                                                                                                                                                                      Another thing you will note and most people don't get this is that for loops don't exist in FP. The fundamental unit of "looping" in fp is always done with recursion, just like how they would do it in mathematical expressions.

                                                                                                                                                                      • int_19h 19 minutes ago

                                                                                                                                                                        For-loops do exist, they just need to not have side effects, which in practice means the likes of map/filter/reduce (ideally promoted to a first class language feature like sequence comprehensions).

                                                                                                                                                                        You could argue that those are still desugared to recursion, but I think at that point it's kinda moot - the construct is still readily recognizable as a loop, and it's most likely also implemented under the hood as an imperative loop with encapsulated local state; not that it matters so long as semantics stay the same.

                                                                                                                                                                        In general, so long as mutation can be encapsulated in modules that only expose pure functional interfaces, I think it should still count as FP for practical purposes.

                                                                                                                                                                      • zelphirkalt 4 hours ago

                                                                                                                                                                        This is a bit naive though. It depends on what you want to do and whether the language you are using offers the required primitives and other things like persistent functional data structures. Without those, you will find yourself hard-pressed to make FP happen. It is of course usually possible with most languages (except those where primitives are already mutating and therefore infectiously prevent you from writing pure functions), but it might not be idiomatic at all, or might not be feasible to roll all things your own, to replace any mutating basics. For example imagine having to copy a data structure all over the place again and again, because its methods are mutating its internal state. That would be inefficient, much more inefficient than a well written corresponding functional data structure, and it would be ugly code.

                                                                                                                                                                        Are you going to write that extra data structure, when your task is actually something else? Some management breathing down your neck, asking when something will be done? Or not so well versed coworkers complaining about you adding a lot of code that might need to be maintained by them, while they don't understand FP? Do you even have the knowledge to implement that data structure in the first place, or will you need to study a couple of papers and carefully translate their code, if any, i to your language and then verify expected performance in meaningful benchmarks?

                                                                                                                                                                        Lots of problems can arise there in practice.

                                                                                                                                                                      • solomonb 7 hours ago

                                                                                                                                                                        The original article literally starts with this:

                                                                                                                                                                        > In the years since I wrote this, I have gotten much more bullish about pure functional programming, even in C/C++ where reasonable: (link) > >The real enemy addressed by inlining is unexpected dependency and mutation of state, which functional programming solves more directly and completely. However, if you are going to make a lot of state changes, having them all happen inline does have advantages; you should be made constantly aware of the full horror of what you are doing.

                                                                                                                                                                        He explicitly says that functional programming solves the same issue as inlining but more directly and completely.

                                                                                                                                                                        • ninetyninenine 8 hours ago

                                                                                                                                                                          He literally said he’s bullish on pure fp. Which means he is off writing pure fp. His own article about it never explicitly or implicitly implies a “pragmatic approach”.

                                                                                                                                                                          I never said he said his email was completely outdated. He for sure implies it’s outdated and updates us on his views of inlining which I also mentioned.

                                                                                                                                                                          • coob 7 hours ago

                                                                                                                                                                            > I never said he said his email was completely outdated.

                                                                                                                                                                            From your prior message:

                                                                                                                                                                            > Carmack called his email completely outdated

                                                                                                                                                                            • ninetyninenine 7 hours ago

                                                                                                                                                                              Ok my bad but I did mention what he’s doing with inlining. So I contradicted myself in the original message which you didn’t identify.

                                                                                                                                                                              He still does inlining.

                                                                                                                                                                        • eyelidlessness 8 hours ago

                                                                                                                                                                          I think more people grasp functional programming all the time, or at least the most salient detail: referential transparency. It’s easy to show the benefits in the small, without getting heavy on academics: pure functions are easier to test, understand, and change with confidence. All three of these reinforce each other, but they’re each independently beneficial as well.

                                                                                                                                                                          There are tons of benefits to get from learning this lesson in a more intentional way—I know that I changed my entire outlook on programming after some time working in Clojure!—but I’ve seen other devs take the same lessons in multi-paradigm contexts as well.

                                                                                                                                                                          • ninetyninenine 7 hours ago

                                                                                                                                                                            Not just this. Modularity is the main insight as well. The reason why oop doesn’t work is because methods can’t be broken down. Your atom is oop is literally a collection of methods tied to mutating state. You cannot break down that collection further.

                                                                                                                                                                            In pure fp. You can break your function down into the smallest computational unit possible. This is what prevents technical debt of the architectural nature as you can rewrite your code as simply recomposing your modular logic.

                                                                                                                                                                          • Hendrikto 14 hours ago

                                                                                                                                                                            > he's moved on from inlining and now does pure functional programming

                                                                                                                                                                            Neither of those are true. He does more FP ”where reasonable“, and that decreases the need for inlining. He does not do pure FP, and he still inlines.

                                                                                                                                                                            • solomonb 6 hours ago

                                                                                                                                                                              "pure FP" does not mean only writing in a functional style. Purity refers to referential transparency, ie., functions do not depend on or modify some global state.

                                                                                                                                                                              • zelphirkalt 4 hours ago

                                                                                                                                                                                Actually even further: They also don't modify/mutate any arguments. If they did, then that could raise problems with concurrency.

                                                                                                                                                                              • ninetyninenine 8 hours ago

                                                                                                                                                                                He literally says he’s more bullish on pure fp. Read it. And I also wrote about where he still inlines.

                                                                                                                                                                              • zelphirkalt 5 hours ago

                                                                                                                                                                                That most wont get it is due to the fact that most are kind of "industrial programmers", who only learn and use mainstream OOP languages amd as such never actually use a mainly FP language a lot. Maybe on HN the ratio is better than on the whole market though.

                                                                                                                                                                                • wruza 15 hours ago

                                                                                                                                                                                  Some grasp it but see its trade-off contract, which is demanding.

                                                                                                                                                                                  • ninetyninenine 8 hours ago

                                                                                                                                                                                    With practice it just becomes another paradigm of programming. The trade off is really a skill issue from this perspective.

                                                                                                                                                                                    The larger issue is performance which is a legitimate reason for not using fp in many cases. But additionally in many cases there is no performance trade off.

                                                                                                                                                                                  • nuancebydefault 14 hours ago

                                                                                                                                                                                    I've never seen pure FP...

                                                                                                                                                                                • BenoitEssiambre 10 hours ago

                                                                                                                                                                                  Here are some information theoretic arguments why inlining code is often beneficial:

                                                                                                                                                                                  https://benoitessiambre.com/entropy.html

                                                                                                                                                                                  In short, it reduces scope of logic.

                                                                                                                                                                                  The more logic you have broken out to wider scopes, the more things will try to reuse it before it is designed and hardened for broader use cases. When this logic later needs to be updated or refactored, more things will be tied to it and the effects will be more unpredictable and chaotic.

                                                                                                                                                                                  Prematurely breaking out code is not unlike using a lot of global variables instead of variables with tighter scopes. It's more difficult to track the effects of change.

                                                                                                                                                                                  There's more to it. Read the link above for the spicy details.

                                                                                                                                                                                  • norir 8 hours ago

                                                                                                                                                                                    This is why I think it's a mistake that many popular languages, including standard c/c++, do not support nested function definitions. This for me is the happy medium where code can be broken into clear chunks, but cannot be called outside of the intended scope. A good compiler can also detect if the nested function is only called once and inline it.

                                                                                                                                                                                    • badmintonbaseba 8 hours ago

                                                                                                                                                                                      C++ has lambdas and local classes. Local classes have some annoying arbitrary limitations, but they are otherwise useful.

                                                                                                                                                                                      • knodi123 7 hours ago

                                                                                                                                                                                        After spending a lot of time writing idiomatic React components in es6, I've found my love of locally declared lambdas to really grow. If I give the lambdas really good names, I find that the main body of my component is very, very readable, even more so than if I'd used a more traditional style liberally sprinkled with comments.

                                                                                                                                                                                        • marcosdumay 4 hours ago

                                                                                                                                                                                          > If I give the lambdas really good names

                                                                                                                                                                                          That's a really funny way to say it.

                                                                                                                                                                                          • zelphirkalt 5 hours ago

                                                                                                                                                                                            Giving your lambdas names defeats part of their purpose though.

                                                                                                                                                                                            • int_19h 14 minutes ago

                                                                                                                                                                                              They have two distinct purposes: anonymous functions, and closures. Those often go together, but there are many scenarios where you only care about the latter, and don't actually need the former. Named lambdas (i.e. lambdas assigned to local consts) covers this case if the language doesn't have dedicated syntax for it.

                                                                                                                                                                                        • humanfromearth9 4 hours ago

                                                                                                                                                                                          In Java, a local function reference (defined inside a method and never used outside of this method ) is possible. Notre that this function is not really tied to an object, which is why I don't call it a method, and I don't use the expression "method reference", it is just tired to the function that contains it, which may be a method - or not.

                                                                                                                                                                                          • kccqzy 4 hours ago

                                                                                                                                                                                            Code can always be called outside of that scope just by returning function pointers or closures. The point is not to restrict calling that code, but to restrict the ability to refer to that piece of code by name.

                                                                                                                                                                                            As mentioned by others, C++ has lambdas. Even if you don't use lambdas, people used to achieve the same effect by using plenty of private functions inside classes, even though the class might have zero variables and simply holds functions. In even older C code, people are used to making one separate .c file for each public function and then define plenty of static functions within each file.

                                                                                                                                                                                          • zelphirkalt 5 hours ago

                                                                                                                                                                                            Of course all this needs to be weighed against maintainability and readability of the code. If the code base is not mainly about something very performance critical and this kind of thing shows to be a bottleneck, then changing things away from more readable towards performance optimized implementation would require a very good justification. I doubt, that this kind of optimization is justified in most cases. For that reason I find the wording "prematurely breaking out code" to be misleading. In most cases one should probably prioritize readability and maintainability and if breaking code out helps those, then it cannot be premature. It could only be premature from a performance limited perspective, which might have not much to do with the use case/purpose of the code.

                                                                                                                                                                                            It is nice, if a performance optimization manages to keep the same degree of readability and maintainability. Those concerns covered, sure we should go ahead and make the performance optimization.

                                                                                                                                                                                            • BenoitEssiambre 3 hours ago

                                                                                                                                                                                              What I'm advocating here is only coincidentally a performance optimization. Readability and maintainability (and improved abstraction) are the primary concern and benefit of (sometimes) keeping things inline or more specifically of reducing entropy.

                                                                                                                                                                                          • gorgoiler 17 hours ago

                                                                                                                                                                                            > Inlining functions also has the benefit of not making it possible to call the function from other places.

                                                                                                                                                                                            I’ve really gone to town with this in Python.

                                                                                                                                                                                              def parse_news_email(…):
                                                                                                                                                                                                def parse_link(…):
                                                                                                                                                                                                  …
                                                                                                                                                                                            
                                                                                                                                                                                                def parse_subjet(…):
                                                                                                                                                                                                  …
                                                                                                                                                                                            
                                                                                                                                                                                                …
                                                                                                                                                                                            
                                                                                                                                                                                            If you are careful, you can rely on the outer function’s variables being available inside the inner functions as well. Something like a logger or a db connection can be passed in once and then used without having to pass it as an argument all the time:

                                                                                                                                                                                              # sad
                                                                                                                                                                                              def f1(x, db, logger): …
                                                                                                                                                                                              def f2(x, db, logger): …
                                                                                                                                                                                              def f3(x, db, logger): …
                                                                                                                                                                                              def g(xs, db, logger):
                                                                                                                                                                                                for x0 in xs:
                                                                                                                                                                                                  x1 = f1(x0, db, logger)
                                                                                                                                                                                                  x2 = f2(x1, db, logger)
                                                                                                                                                                                                  x3 = f3(x2, db, logger)
                                                                                                                                                                                                  yikes x3
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                              # happy
                                                                                                                                                                                              def g(xs, db, logger):
                                                                                                                                                                                                def f1(x): …
                                                                                                                                                                                                def f2(x): …
                                                                                                                                                                                                def f3(x): …
                                                                                                                                                                                                for x in xs:
                                                                                                                                                                                                  yield f3(f2(f1(x)))
                                                                                                                                                                                            
                                                                                                                                                                                            Carmack commented his inline functions as if they were actual functions. Making actual functions enforces this :)

                                                                                                                                                                                            Classes and “constants” can also quite happily live inside a function but those are a bit more jarring to see, and classes usually need to be visible so they can be referred to by the type annotations.

                                                                                                                                                                                            • grumbel 15 hours ago

                                                                                                                                                                                              That's not an improvement, as it screws up the code flow. The point of inline blocks is that you can read the code the same way as it is executed. No surprised that code might be called twice or that a function call could be missed or reordered. Adding real functions causes exactly the indirection that one wanted to avoid in the first place. If the block has no name you know that it will only be executed right where it is written.

                                                                                                                                                                                              • gorgoiler 14 hours ago

                                                                                                                                                                                                Yeah that’s a valid point. I tend to have in mind that as soon as I pull any of the inner functions out to the publicly visible module level I can say goodbye to ever trying to stop people reusing the code when I don’t really want them to.

                                                                                                                                                                                                For example, if your function has an implicit, undocumented contract such as assuming the DB is only a few milliseconds away, but they then reuse the code for logging to DBs over the internet, then they find it’s slow and speed it up with caching. Now your DB writing code has to suffer their cache logic bugs when it didn’t have to.

                                                                                                                                                                                              • scbrg 16 hours ago

                                                                                                                                                                                                Not sure I believe the benefit of this approach outweighs the added difficulty wrt testing, but I certainly agree that Python needs a yikes keyword :-)

                                                                                                                                                                                                • nuancebydefault 14 hours ago

                                                                                                                                                                                                  What is the benefit of such a yikes? Or do you consider it a yikes language as a whole?

                                                                                                                                                                                                  Personally I like that functions can be inside functions, as a trade off between inlining and functional seperation in C++.

                                                                                                                                                                                                  The scope reduction makes it easier to track bugs while it has the benefits of separation of concern.

                                                                                                                                                                                                  • scbrg 10 hours ago

                                                                                                                                                                                                    > What is the benefit of such a yikes? Or do you consider it a yikes language as a whole?

                                                                                                                                                                                                    None, it was just a simple joke based on the typo in the post I replied to. I like Python, and have in fact been happily using it as my main language for over 20 years.

                                                                                                                                                                                                    • gorgoiler 5 hours ago

                                                                                                                                                                                                      Ahhh, now I (top level author) get it :)

                                                                                                                                                                                                • wraptile 15 hours ago

                                                                                                                                                                                                  The latter pattern is very popular in Python web scraping and data parsing niches as the code is quite verbose and specific and I'm very happy with this approach. Easy to read and debug and the maintenance is naturally organized.

                                                                                                                                                                                                  • toenail 16 hours ago

                                                                                                                                                                                                    > Inlining functions also has the benefit of not making it possible to call the function from other places.

                                                                                                                                                                                                    Congrats, you've got an untestable unit.

                                                                                                                                                                                                    • ahoka 15 hours ago

                                                                                                                                                                                                      Congratulations, you are writing test for things that would not need test if weren't put behind a under-defined interface. Meanwhile sprint goals are not met and overall product quality is embarrassing, but you have 100% MC/DC coverage of your addNumbersOrThrowIfAbove(a, b, c).

                                                                                                                                                                                                      • jeltz 16 hours ago

                                                                                                                                                                                                        Which is usually a positive. Testing tiny subunits usually just makes refactoring and adding new features hard while not improving test quality.

                                                                                                                                                                                                        • hansvm 11 hours ago

                                                                                                                                                                                                          Testing is a tool that sometimes makes your life easier. IME, many (not all) tiny subunits do actually have better tests when examined at that level. You just want to avoid tests which will need to be updated for unrelated changes, and try to avoid writing code which propagates that sort of minutia throughout the codebase:

                                                                                                                                                                                                          > while not improving test quality

                                                                                                                                                                                                          The big wins from fine-grained testing are

                                                                                                                                                                                                          1. Knowing _where_ your program is broken

                                                                                                                                                                                                          2. Testing "rare" edge cases

                                                                                                                                                                                                          Elaborating on (2), your code probably works well enough on some sort of input or you wouldn't ship it. Tests allow you to cheaply test all four Turkish "i"s and some unicode combining marks, test empty inputs, test what happens when a clock runs backward ever or forward too slowly/quickly, .... You'll hit some of those cases eventually in prod, where pressures are high and debugging/triaging is slow, and integration tests won't usually save you. I'm also a huge fan of testing timing-based logic with pure functions operating on the state being passed in (so it's tested, better than an integration test would accomplish, and you never have to wait for anything godawful like an actual futex or sleep or whatever).

                                                                                                                                                                                                          > makes refactoring and adding new features hard

                                                                                                                                                                                                          What you're describing is a world where accomplishing a single task (refactoring, adding a new feature) has ripple effects through the rest of the system, or else the tests are examining proxy metrics rather than invariants the tiny subunits should actually adhere to. Testing being hard is a symptom of that design, and squashing the symptom (avoiding tests on tiny subunits) won't fix any of the other problems it causes.

                                                                                                                                                                                                          If you're stuck in some codebase with that property and without the ability to change it, by all means, don't test every little setup_redis_for_db_payment_handling_special_case_hulu method. Do, however, test things with sensible, time-invariant names -- data structures, algorithms, anything that if you squint a bit looks kind of like parsing or serialization, .... If you have a finicky loop with a bunch of backoff-related state, pull the backoff into its own code unit and test how it behaves with clocks that run backward or other edge cases. The loop itself (or any other confluence of many disparate coding concepts) probably doesn't need to be unit tested for the reasons you mention, but you usually can and should pull out some of the components into testable units.

                                                                                                                                                                                                          • bluGill 10 hours ago

                                                                                                                                                                                                            The problem is there is rarely a clear interface for your subunit. As such you will want to refactor that interface in ways that break tests in the future. If you are writing another string you can probably come up with a good interface and then write good tests that won't make refactoring hard - but string should be a solved problem for most of us (unless you are writing a new programming language) and instead we are working on problems that are not as clear and only our competitors work on so we can't even learn from others.

                                                                                                                                                                                                          • sunshowers 15 hours ago

                                                                                                                                                                                                            Like most things being talked about here, so much depends on the specifics.

                                                                                                                                                                                                            I think developers should generally try and aim for, at every scale, the outputs of a system to be pure functions of the inputs (whether by reducing the scope of the system or expanding the set of things considered inputs). Beyond that there are so many decisions at the margin that are going to be based on personal inclination.

                                                                                                                                                                                                            • ninetyninenine 15 hours ago

                                                                                                                                                                                                              Not according to jon carmack. He stated he switched to pure functional programming in the intro which is basically stating all his logic is in the form of unit testable pure functions.

                                                                                                                                                                                                              • nuancebydefault 11 hours ago

                                                                                                                                                                                                                I found this [] article of Carmack. While reading, I understood there is a large set of gray shades to pureness of "pure functional" code. He calls being functional a useful abstraction, a function() is never purely functional.

                                                                                                                                                                                                                [] https://web.archive.org/web/20120501221535/http://gamasutra....

                                                                                                                                                                                                                • ninetyninenine 8 hours ago

                                                                                                                                                                                                                  When people say pure functional programming they never mean the entire program is like this.

                                                                                                                                                                                                                  Because if it were your program would have no changing state and no output.

                                                                                                                                                                                                                  What they mean is that your code is purely functional as much as possible. And there is high segregation between functional code and non functional code in the sense that state and IO is segregated as much as possible away into very small very general functionality.

                                                                                                                                                                                                                • foldr 14 hours ago

                                                                                                                                                                                                                  Nothing about pure functional programming requires unit testing all of your functions. You can decide to unit test larger or smaller units of code, just as you can in any other paradigm.

                                                                                                                                                                                                                  • ninetyninenine 8 hours ago

                                                                                                                                                                                                                    In pure functional programming a pure function is unit testable by definition of what a pure function is. I never said it requires functions to be tested. Just that it requires functions to be testable.

                                                                                                                                                                                                                    In other paradigms do not do this. As soon as a module touches IO or state it becomes entangled with that and NOT unit testable.

                                                                                                                                                                                                                    Is it still testable? Possibly. But not as a unit.

                                                                                                                                                                                                                    • int_19h 12 minutes ago

                                                                                                                                                                                                                      How do you unit test a local function that is a closure in pure functional code?

                                                                                                                                                                                                                  • tightbookkeeper 10 hours ago

                                                                                                                                                                                                                    > pure fp

                                                                                                                                                                                                                    No he didn’t.

                                                                                                                                                                                                                • jayd16 7 hours ago

                                                                                                                                                                                                                  Ideally, you've just moved the unit boundary to where it logically should be instead of many small implementation details that should not be exposed.

                                                                                                                                                                                                                  • xboxnolifes 16 hours ago

                                                                                                                                                                                                                    The unit here is the email, not the email's link or subjects. Those are implementation details.

                                                                                                                                                                                                                    • mgsouth 14 hours ago

                                                                                                                                                                                                                      What do you use unit tests for, other than verifying implementation details?

                                                                                                                                                                                                                      Perhaps we have a difference in definition. To me, a unit test for a function such as "parse_news_email" would explore variations in parameters and states. Because of combinatorial explosion, that often means at least some white-box testing. I'm not going to generate random subjects and senders, and received-froms, I'm going to target based on internal details. Are we doing smart things with the message ID hostname? Then what happens if two messages come in with the same message ID but from different relays? The objective is that the unit test wrings out the implementation details, and the caller's unit test doesn't need to exhaustively test them.

                                                                                                                                                                                                                      This white-box texting may require directly poking at or mocking internal functions or at least abusing how they're called. For example, parsing the news item might entail pulling up and modifying conversation thread cache entries or state. For some of the tests you may need hand-crafted cache state, it's not feasible to create unique states for each parameter combination you're testing, and testing a combination will pollute the state for the following combinations. Or maybe the function depends upon an external resource you can't beat to death with a million identical requests. So the least-bad, simplest solution may be to freeze or back out part of the normal state update in the unit test. Which would usually involve directly invoking the internal routines.

                                                                                                                                                                                                                      Can this lead to fragile, false-positve to the point of useless tests? You betcha. That's where entertaining two contrary viewpoints is needed :) Use experience and good judgement about pros and cons in the particular situation.

                                                                                                                                                                                                                      • randomdata 7 hours ago

                                                                                                                                                                                                                        Unit tests are for documenting the API contract for the user. You are going to target based on what you are willing to forevermore commit to for those who will use what you have created. Indeed, what happens when two messages come in with the same message ID is something the user needs to be aware of and how it functions needs to remain stable no matter what you do behind the scenes in the future, so you would absolutely want to document that behaviour. How it is implemented is irrelevant, though. The only thing that matters is that, from the public API perspective, it is handled appropriately.

                                                                                                                                                                                                                        There is a time and place for other types of tests, of course. You are right that unit tests are not the be all and end all. A good testing framework will allow you to mark for future developers which tests are "set in stone" and which are deemed throwaway.

                                                                                                                                                                                                                        • mgsouth 3 hours ago

                                                                                                                                                                                                                          We're in general agreement about the purpose of unit tests. I disagree on a couple of points.

                                                                                                                                                                                                                          Tests do not document the API. No test is complete, and for that reason alone can't completely document anything. For example, a good API might specify that "the sender must be non-null, and must be valid per RFC blah." There's no way to test that inclusively, to check all possible inputs. You can't use the test cases to deduce "we must meet RFC blah." You might suspect it, but you'd be risking undefined behavior it you stray from input that doesn't exactly match the test cases. And before anyone objects "the API docs can be incomplete too," well, that true. But the point is that a written API has vastly more descriptive power than a set of test cases. (The same applies to "self-documenting code". Bah humbug.) There's also the objection "but you can't guarentee cases you don't test!" Also true. That's reality. _You can never test all your intended behavior._ You pick your test cases to do the best you can, and change your cases as problems pop up.

                                                                                                                                                                                                                          The other thing I would shy away from is including throwaway tests in the framework. Throwaways are a thing, developers use them all the time, but don't make them unwanted stepchildren--poorly (incompletely?) designed, slapped together, limited scope, confusing for another developer (including time-traveling self) to wade through and decide whether this is a real failure or just bogus test. They're tech debt. Less frequently used tests are another matter. For example, release-engineering tests that only get run against release candidates. But these should be just as much real, set in stone, as any other deliverable.

                                                                                                                                                                                                                          Which I guess is a third viewpoint nuance difference. I treat tests as being part of the package just as much as any other deliverable. They morph and shift as APIs change, or dependencies mutate, or bugs are found. They aren't something that can be put to the side and left to vegetate.

                                                                                                                                                                                                                          • randomdata 3 hours ago

                                                                                                                                                                                                                            > There's no way to test that inclusively, to check all possible inputs.

                                                                                                                                                                                                                            Which means the RFC claim is false and should not be asserted in the first place. The API may incidentally accept valid RFC input, but there is no way to know that it does for sure for all inputs. You might suspect it conforms to the RFC, but to claim that it does with certainty is incorrect. Only what is documented in the tests is known to be true.

                                                                                                                                                                                                                            Everything else is undefined behaviour. Even if you do happen to conform to an RFC in one version, without testing to verify that continues to hold true, it probably won’t.

                                                                                                                                                                                                                            This is exactly why unit tests are the expected documentation by users. It prevents you, the author, from make spurious claims. If you try, the computer will catch you in your lies.

                                                                                                                                                                                                                            > The other thing I would shy away from is including throwaway tests in the framework.

                                                                                                                                                                                                                            What does that mean? I suspect you are thinking of something completely different as this doesn't quite make sense with respect to what I said. It probably makes sense in another context, and if I have inferred that context correctly, I'd agree... But, again, untreated to our discussion.

                                                                                                                                                                                                                            • mgsouth 2 hours ago

                                                                                                                                                                                                                              OK, one more round. An API spec is a contract, not a guarentee of correctness. You, as the client, are free to pass me any data that fits the spec. If my parsing library does the wrong thing, then I've got a bug and need to fix it. My tests are also defective and need to be adjusted.

                                                                                                                                                                                                                              If you passed 3.974737373 to cos(x), and got back 200.0, would you be mollified if the developers told you "that value clearly isn't in the unit test cases, so you're in undefined behavior"? Of course not. The spec might be "x is a single-float by value, 0.0 <= x < 2.0 * PI, result is the cosine of X as a single-float." That's a contract, an intent--an API.

                                                                                                                                                                                                                              The same for a mail parser. If my library croaks with a valid (per RFC) address then I've got a problem. If I try to provide some long, custom, set of cases I will or won't support, then my customer developers are going to be rightfully annoyed. What are they supposed to do when they get a valid but unsupported address? Note we're not talking about carving out broad exceptions reasonable in context ("RFC 5322 except we don't support raw IP addresses foo@[1.2.3.4]", "we treat all usernames as case-insensitive"). And we're not talking about "Our spec (intent) is foo, but we've only tested blah blah blah."

                                                                                                                                                                                                                              Early in my career I would get pretty frustrated by users who were not concerned with arranging their data and procedures the right way, clueless about what they really were doing. OK, so I still get frustrated by stupid :) But it's gradually seeped into my head that what matters is the user's intentions. Specs are an imperfect simplificaton of those very complex things, APIs are imperfect simplifcations of the specs, and our beautiful code and distributed clusters and redundant networks are extremely limited and imperfect implementations of the APIs. Some especially harmful potential flaws get extra attention during arch, implementation, and testing. When things get too far out we fix them.

                                                                                                                                                                                                                        • Spivak 6 hours ago

                                                                                                                                                                                                                          > What do you use unit tests for, other than verifying implementation details?

                                                                                                                                                                                                                          1. Determining when the observable behavior of the program changes.

                                                                                                                                                                                                                          2. Codifying only the specific behaviors that are known to be relied on by callers.

                                                                                                                                                                                                                          3. Preventing regressions after bugs are fixed.

                                                                                                                                                                                                                          Failing tests are alarm bells, when do you want them to grab your attention?

                                                                                                                                                                                                                          • mgsouth 4 hours ago

                                                                                                                                                                                                                            Excellent points, violently agree, my question was poorly worded. The purpose of units tests is to verify the contracted API is actually being provided by the implementation details. A clearer question might have been "what are unit tests for if not to exercise the implementation details, verifying they adhere to the API?" Unit tests validate implementation details, integration tests validate APIs.

                                                                                                                                                                                                                            To me, a good unit test beats the stuffing out of the unit. It's as much a part of the unit as the public functions, so should take full advantage of internal details (keeping test fragility in mind); of course that implies the unit test needs ongoing maintenance just as much as the public functions. If you're passing a small set of inputs and checking the outputs, well that's a smoke test, not a unit test.

                                                                                                                                                                                                                            To answer your last question, I want the alarm bells to ring whenever the implementation details don't hold up. That's whether the function code changed, a code or state dependency changed, or the testing process itself changed. If at all feasible all the unit tests run every time the the complete suite is run, in full meat-grinder mode. "Complete suite" is hand-wavy; e.g. it might be the suite for a major library, but not the end-to-end application.

                                                                                                                                                                                                                            • int_19h 10 minutes ago

                                                                                                                                                                                                                              All that doesn't mean that you have to consider artificial boundaries that you yourself have introduced for convenience when deciding on the proper boundaries for what constitutes a "unit". Not every instance of code reuse makes for a good unit to test.

                                                                                                                                                                                                                      • ninetyninenine 15 hours ago

                                                                                                                                                                                                                        This is a major insight. Defining a local function isn't a big deal you can always just copy and pasta it out to global scope.

                                                                                                                                                                                                                        Any time you merge state with function you can no longer move the function. This is the same problem as OOP. Closures can't be modular the same way methods in objects can't be modular.

                                                                                                                                                                                                                        The smallest unit of testable module is the combinator. John Carmack literally mentioned he does pure functional programming now which basically everyone in this entire thread is completely ignoring.

                                                                                                                                                                                                                        • gorgoiler 14 hours ago

                                                                                                                                                                                                                          Yup, and I should have called this out as a downside. Thank you for raising it.

                                                                                                                                                                                                                          On visibility, one of the patterns I’ve always liked in Java is using package level visibility to limit functions to that code’s package and that packages tests, where they are in the same package (but possibly defined elsewhere.)

                                                                                                                                                                                                                          (This doesn’t help though with the reduction in argument verbosity, of course.)

                                                                                                                                                                                                                        • eru 17 hours ago

                                                                                                                                                                                                                          Funny enough, the equivalent of your Python example is how Haskell 'fakes' all functions with more than one argument (at least by default).

                                                                                                                                                                                                                          Imperative blocks of code in Haskell (do-notation) also work like this.

                                                                                                                                                                                                                          • zelphirkalt 12 hours ago

                                                                                                                                                                                                                            Where is the part, where this is "careful"? This is just how scopes work. I don't see what is special about the inner functions using things in the scope of the outer functions.

                                                                                                                                                                                                                            • int_19h 7 minutes ago

                                                                                                                                                                                                                              Excessive use of external bindings in a closure can make it hard to reason about lifetimes in cases where that matters (e.g. when you find out that a huge object graph is alive solely because some callback somewhere is a lambda that closed over one of the objects in said graph).

                                                                                                                                                                                                                            • InDubioProRubio 16 hours ago

                                                                                                                                                                                                                              So inlining is the private of functions without a object. Pop it all to stack, add arguments, set functionpointer to instructionstart of inline code, challenge accepted, lets go to..

                                                                                                                                                                                                                              • orf 13 hours ago

                                                                                                                                                                                                                                That’s gonna be quite expensive, don’t do this in hot loops. You’re re-defining and re-creating the function object each time the outer function is called.

                                                                                                                                                                                                                                • gorgoiler 4 hours ago

                                                                                                                                                                                                                                  Good point. I measured it for 10^6 loops:

                                                                                                                                                                                                                                  (1) 40ms for inline code;

                                                                                                                                                                                                                                  (2) 150ms for an inner function with one expression;

                                                                                                                                                                                                                                  (3) 200ms for a slightly more complex inner function; and

                                                                                                                                                                                                                                  (4) 4000ms+ for an inner function and an inner class.

                                                                                                                                                                                                                                    def f1(n: int) -> int:
                                                                                                                                                                                                                                        return n * 2
                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                    def f2(n: int) -> int:
                                                                                                                                                                                                                                        def g():
                                                                                                                                                                                                                                            return n * 2
                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                        return g()
                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                    def f3(n: int) -> int:
                                                                                                                                                                                                                                        def g():
                                                                                                                                                                                                                                            for _ in range(0):
                                                                                                                                                                                                                                                try:
                                                                                                                                                                                                                                                    pass
                                                                                                                                                                                                                                                except Exception as exc:
                                                                                                                                                                                                                                                    if isinstance(exc, 1):
                                                                                                                                                                                                                                                        pass
                                                                                                                                                                                                                                                    else:
                                                                                                                                                                                                                                                        while True:
                                                                                                                                                                                                                                                            pass
                                                                                                                                                                                                                                                    raise Exception()
                                                                                                                                                                                                                                            return n * 2
                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                        return g()
                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                    def f4(n: int) -> int:
                                                                                                                                                                                                                                        class X:
                                                                                                                                                                                                                                            def __init__(self, a, b, c):
                                                                                                                                                                                                                                                pass
                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                            def _(self) -> float:
                                                                                                                                                                                                                                                return 1.23
                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                        def g():
                                                                                                                                                                                                                                            for _ in range(0):
                                                                                                                                                                                                                                                try:
                                                                                                                                                                                                                                                    pass
                                                                                                                                                                                                                                                except Exception as exc:
                                                                                                                                                                                                                                                    if isinstance(exc, 1):
                                                                                                                                                                                                                                                        pass
                                                                                                                                                                                                                                                    else:
                                                                                                                                                                                                                                                        while True:
                                                                                                                                                                                                                                                            pass
                                                                                                                                                                                                                                                    raise Exception()
                                                                                                                                                                                                                                            return n * 2
                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                        return g()
                                                                                                                                                                                                                                • a_t48 14 hours ago

                                                                                                                                                                                                                                  You can do this in C++, too, but the syntax is a little uglier.

                                                                                                                                                                                                                                  • kllrnohj 4 hours ago

                                                                                                                                                                                                                                    Not that bad?

                                                                                                                                                                                                                                        int main() {
                                                                                                                                                                                                                                            int a = -1;
                                                                                                                                                                                                                                            [&] {
                                                                                                                                                                                                                                                a = 42;
                                                                                                                                                                                                                                                printf("I'm an uncallable inline block");
                                                                                                                                                                                                                                            }();
                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                            printf(" ");
                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                            [&] {
                                                                                                                                                                                                                                                printf("of code\n");
                                                                                                                                                                                                                                            }();
                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                            [&] {
                                                                                                                                                                                                                                                printf("Passing state: %d\n", a);
                                                                                                                                                                                                                                            }();
                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                            return 0;
                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                    • int_19h 6 minutes ago

                                                                                                                                                                                                                                      At this point, why wouldn't you just use a nested block?

                                                                                                                                                                                                                                      • a_t48 4 hours ago

                                                                                                                                                                                                                                        It’s not horrible, a little bit verbose though.

                                                                                                                                                                                                                                    • raverbashing 16 hours ago

                                                                                                                                                                                                                                      It might be a benefit in some cases, but I do feel that f1/f2/f3 are the prime candidates for actual unit testing

                                                                                                                                                                                                                                      • bitwize 16 hours ago

                                                                                                                                                                                                                                        It's possible to nest subprograms within subprograms in Ada. I take advantage of this ability to break a large operation into one or more smaller simpler "core" operations, and then in the main body of the procedure write some setup code followed by calls to the core operation(s).

                                                                                                                                                                                                                                        • LoganDark 13 hours ago

                                                                                                                                                                                                                                          Remember to `nonlocal xs, db, logger` inside those inner functions. I'm not sure if this is needed for variables that are only read, but I wouldn't ever leave it out.

                                                                                                                                                                                                                                          • pansa2 10 hours ago

                                                                                                                                                                                                                                            > I'm not sure if this is needed for variables that are only read

                                                                                                                                                                                                                                            It’s not needed. In fact, you should leave it out for read-only variables. That’s standard practice - if you use `nonlocal` people reading the code will expect to see writes to the variables.

                                                                                                                                                                                                                                        • dehrmann 16 hours ago

                                                                                                                                                                                                                                          Always read older stuff from Carmack remembering the context. He made a name for himself getting 3D games to run on slow hardware. The standard advice of write for clarity first, make sure algorithms have reasonable runtimes, and look at profiler data if it's slow is all you need 99% of the time.

                                                                                                                                                                                                                                          • marginalia_nu 12 hours ago

                                                                                                                                                                                                                                            I find the inlined style can actually improve clarity.

                                                                                                                                                                                                                                            A lot of code written toward the "uncle bob" style where you maximize the number of functions has fantastic local clarity, you can see exactly what the code you are looking at is doing; but atrocious global clarity, where it's nearly impossible to figure out what the system does on a larger scale.

                                                                                                                                                                                                                                            Inlining can help with that, local clarity deteriorates a bit, but global clarity typically improves by reducing the number of indirections. The code does indeed also tend to get faster, as it's much easier to identify and remove redundant code when you have it all in front of you. ... but this also improves the clarity of the code!

                                                                                                                                                                                                                                            You can of course go too far, in either direction, but my sense is that we're often leaning much too far toward short isolated functions now than is optimal.

                                                                                                                                                                                                                                            • silvestrov 6 hours ago

                                                                                                                                                                                                                                              > atrocious global clarity

                                                                                                                                                                                                                                              much like microservices.

                                                                                                                                                                                                                                            • Cthulhu_ 16 hours ago

                                                                                                                                                                                                                                              And before that, 2D games (side-scrolling platformers were not a thing on PC hardware until Carmack did it, iirc). I think his main thing is balancing clarity - what happens when and in what order - with maintainability.

                                                                                                                                                                                                                                              Compare this with enterprise software, which is orders of magnitude more complex than video games in terms of business logic (the complexity in video games is in performance optimization), but whose developers tend to add many layers of abstraction and indirection, so the core business process is obfuscated, or there's a billion non-functional side activities also being applied (logging, analytics, etc), again obfuscating the core functionality.

                                                                                                                                                                                                                                              It's fun to go back to more elementary programming things, in e.g. Advent of Code challenges or indeed, game development.

                                                                                                                                                                                                                                              • nadam 15 hours ago

                                                                                                                                                                                                                                                "compare this with enterprise software, which is orders of magnitude more complex than video games in terms of business logic" Maybe this was true 20 years ago, but I do not think this is true today. Game code of some games is almost as complex as enterprise software or even more complex in some cases (think of grand strategy games like Civilization or Paradox games). The difference is that it still needs to be performant, so the evolutionary force just kills programmers and companies creating unperformant abstractions. In my opinion game programming is just harder than enterprise programming if we speak about complex games. (I have done both). The only thing which is easier in game programming is that it is a bit easier to see clearly in terms of 'business requirements', and also it is more meritocratic (you can start a game company anywhere on the globe, no need to be at business centers.) And of course game programming is more fun, so programmers do the harder job even for less money.

                                                                                                                                                                                                                                                For people who think game programming is less complex than enterprise software, I suggest the CharacterMovementComponent class in unreal engine which is the logic of movement of characters (people) in a networked game environment... With multiple thousand lines of code in just the header is not uncommon in unreal. And this is not complex because of optimization mostly. This is very complex and messy logic. Of course we can argue that networking and physics could be done in a simple naive way, which would be unacceptable in terms of latency and throughput, so all in all complexity is because of optimization after all. But it is not the 'fun' elegant kind of optimization, it is close to messy enterprise software in some sense in my opinion.

                                                                                                                                                                                                                                                • ykonstant 13 hours ago

                                                                                                                                                                                                                                                  I have heard modern game development compared to OS development in terms of complexity and I think that comparison is quite apt; especially when the game involves intricate graphics and complicated networking involving multiple time scales as you say.

                                                                                                                                                                                                                                                • aidenn0 43 minutes ago

                                                                                                                                                                                                                                                  > And before that, 2D games (side-scrolling platformers were not a thing on PC hardware until Carmack did it, iirc). I think his main thing is balancing clarity - what happens when and in what order - with maintainability.

                                                                                                                                                                                                                                                  Smooth side-scrollers did exist on the PC before Keen (An early one would be the PC port of Defender). Moon Patrol even had jumping in the early '80s.

                                                                                                                                                                                                                                                  Furthermore other contemporaries of Carmack were making full-fledged side-scrolling platformers in ways different from how Keen did it (there were many platformers released in 1990). They all involved various limitations on level design (as did what Keen used), but I don't believe any of them allowed both X and Y scrolling like the Keen games did.

                                                                                                                                                                                                                                                  • high_na_euv 15 hours ago

                                                                                                                                                                                                                                                    >Compare this with enterprise software, which is orders of magnitude more complex than video games in terms of business logic

                                                                                                                                                                                                                                                    I dont buy it in games like gta, cyberpunk or witcher 3

                                                                                                                                                                                                                                                    • a_t48 14 hours ago

                                                                                                                                                                                                                                                      In both design space and programming complexity, you're right.

                                                                                                                                                                                                                                                  • physicles 16 hours ago

                                                                                                                                                                                                                                                    I agree with this in general, but his essay on functional programming in C++ (linked at the top of the page) is phenomenal and is fantastic general advice when working in any non-functional language.

                                                                                                                                                                                                                                                • donatj 10 hours ago

                                                                                                                                                                                                                                                  I have a coworker that LOVES to make these one or two line single use functions that absolutely drives me nuts.

                                                                                                                                                                                                                                                  Just from a sheer readability perspective being able to read a routine from top to bottom and understand what everything is doing is invaluable.

                                                                                                                                                                                                                                                  I have thought about it many times, I wish there was an IDE where you could expand function calls inline.

                                                                                                                                                                                                                                                  • wodenokoto 9 hours ago

                                                                                                                                                                                                                                                    It’s called “self documenting code” and the way you self document code it is by taking all your comments and make them into functions, named after your would-be comment.

                                                                                                                                                                                                                                                    I’m not a fan either.

                                                                                                                                                                                                                                                    • MetaWhirledPeas 8 hours ago

                                                                                                                                                                                                                                                      Everything must be done to taste. I think code can be made "self-documenting" without going overboard and doing silly things.

                                                                                                                                                                                                                                                    • zelphirkalt 4 hours ago

                                                                                                                                                                                                                                                      This can be done in a good way and in bad ways. With most code you will be calling builtin procedures/functions. You also don't look under the hood for those usually. But for the code of your coworker it seems to irritate you. This could mean many things. Just to name a few: (1) The names are not giving a good idea what those functions do. (2) The level of abstraction is not the same inside the calling function, so that you feel the need to check the implementation detail of those small functions. (3) You don't trust the implementation of those smaller functions. (4) The separated out functions could be not worth separating out and being given names, because what the code in them does is clear enough without them being separated out. (n) or some other reason.

                                                                                                                                                                                                                                                      The issue does not have to be that those things are split out into separate small functions. The issue might be something else.

                                                                                                                                                                                                                                                      • Tyr42 8 hours ago

                                                                                                                                                                                                                                                        Sometimes it's easier to define some vocabulary and then use it. Like defining push and pop on a stack vs stack[++ix] = blah and blah = stack[ix--].

                                                                                                                                                                                                                                                        And avoids needing to think about it being prefix or postfix after you don't that one time.

                                                                                                                                                                                                                                                        But at other times it's insufferable, when the abstraction is leaky and unintuitive.

                                                                                                                                                                                                                                                      • kazinator 2 hours ago

                                                                                                                                                                                                                                                        In my opinion, there is value in functions that have only one caller: it's called functional decomposition. The right granularity of functional decomposition can make the logic easier to understand.

                                                                                                                                                                                                                                                        To prevent unintended uses of a helper function in C, you can make it static. Then at least nothing from outside of that translation unit can call it.

                                                                                                                                                                                                                                                        • Cthulhu_ 16 hours ago

                                                                                                                                                                                                                                                          I feel like this style is also encouraged in Go and / or the clean/onion architecture / DDD, to a point, where the core business logic can and should be a string of "do this, then do that, then do that" code. In my own experience I've only had a few opportunities to do so (most of my work is front-end which is a different thing entirely), the one was application initialisation (Create the logger, then connect to the database, then if needed initialize / migrate it, then if needed load test data. Then create the core domain services that uses the database connection. Then create the HTTP handlers that interface with the domain services. Then start the HTTP server. Then listen for an end process command and shut down gracefully), the other was pure business logic (read the database, transform, write to file, but "database" and "file" were abstract concepts that could be swapped out easily). You don't really get that in front-end programming though, it's all event driven etc.

                                                                                                                                                                                                                                                          • xxs 15 hours ago

                                                                                                                                                                                                                                                            > the one was application initialisation

                                                                                                                                                                                                                                                            ...and then you want to parallelize as much as possible to allow for fast boot times which helps the development process immensely.

                                                                                                                                                                                                                                                            One of the things I've learned is that optimizing for developer quality of life is one of the best approaches when it comes to correctness and performance. Then, the developers would be able to run multiple iterations of the real thing.

                                                                                                                                                                                                                                                          • otikik 15 hours ago

                                                                                                                                                                                                                                                            > I have gotten much more bullish about pure functional programming, even in C/C++ where reasonable: (link)

                                                                                                                                                                                                                                                            The link is no longer valid, I believe this is the article in question:

                                                                                                                                                                                                                                                            https://www.gamedeveloper.com/programming/in-depth-functiona...

                                                                                                                                                                                                                                                            • ninetyninenine 15 hours ago

                                                                                                                                                                                                                                                              Probably the more important link. He's basically saying his old email is outdated and he does pure FP now.

                                                                                                                                                                                                                                                              • meheleventyone 15 hours ago

                                                                                                                                                                                                                                                                This is over a decade old at this stage, it would be interesting to know how his thoughts have evolved since.

                                                                                                                                                                                                                                                            • lencastre 4 hours ago

                                                                                                                                                                                                                                                              I’m not even pretending I understood Carmack’s email/mailing list post but if more intelligent/experienced programmers than me care to help me out, what exactly is meant by this he wrote in 2007:

                                                                                                                                                                                                                                                              _If a function is called from multiple places, see if it is possible to arrange for the work to be done in a single place, perhaps with flags, and inline that._

                                                                                                                                                                                                                                                              Thanks,

                                                                                                                                                                                                                                                              • tcoville 4 hours ago

                                                                                                                                                                                                                                                                This is a heavily simplified version of what I'm suspecting he's trying to portray, key this wouldn't be useful for utility functions like string manipulation but more business logic being used across similar functions:

                                                                                                                                                                                                                                                                  def processOrder():
                                                                                                                                                                                                                                                                      # Some common processing logic
                                                                                                                                                                                                                                                                      print("Processing the order...")
                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                  def placeOnlineOrder():
                                                                                                                                                                                                                                                                      processOrder()
                                                                                                                                                                                                                                                                      print("Sending confirmation email...")
                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                  def placeInStoreOrder():
                                                                                                                                                                                                                                                                      processOrder()
                                                                                                                                                                                                                                                                      print("Printing receipt...")
                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                  # Calls from different locations
                                                                                                                                                                                                                                                                  placeOnlineOrder()
                                                                                                                                                                                                                                                                  placeInStoreOrder()
                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                Could become:

                                                                                                                                                                                                                                                                  def processOrder(order_type):
                                                                                                                                                                                                                                                                      # Common processing logic
                                                                                                                                                                                                                                                                      print("Processing the order...")
                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                      if order_type == "online":
                                                                                                                                                                                                                                                                          print("Sending confirmation email...")
                                                                                                                                                                                                                                                                      elif order_type == "in_store":
                                                                                                                                                                                                                                                                          print("Printing receipt...")
                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                  # Unified calls with different flags
                                                                                                                                                                                                                                                                  processOrder("online")
                                                                                                                                                                                                                                                                  processOrder("in_store")
                                                                                                                                                                                                                                                                • fluoridation 3 hours ago

                                                                                                                                                                                                                                                                  That... looks decidedly worse. Now you have fewer functions that need to be concerned with multiple unrelated things for no reason.

                                                                                                                                                                                                                                                              • physicsguy 14 hours ago

                                                                                                                                                                                                                                                                I think when developing something from scratch, it's actually not a terrible strategy to do this and pick out boundaries when they become clearer. Creating interfaces that make sense is an art, not a science.

                                                                                                                                                                                                                                                                • endlessmike89 7 hours ago

                                                                                                                                                                                                                                                                  Link to the Wayback Machine cache/mirror, in case you're also experiencing a "Bad Gateway/Connection refused" error

                                                                                                                                                                                                                                                                  https://web.archive.org/web/20241009062005/http://number-non...

                                                                                                                                                                                                                                                                  • wruza 15 hours ago

                                                                                                                                                                                                                                                                    I wish languages had the following:

                                                                                                                                                                                                                                                                      let x = block {
                                                                                                                                                                                                                                                                         …
                                                                                                                                                                                                                                                                         return 5
                                                                                                                                                                                                                                                                      } // x == 5
                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                    And the way to mark copypaste, e.g.

                                                                                                                                                                                                                                                                      common foo {
                                                                                                                                                                                                                                                                        asdf(qwerty(i+j));
                                                                                                                                                                                                                                                                        printf(“%p”, write));
                                                                                                                                                                                                                                                                        bar();
                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                      …(repeats verbatim 20 times)…
                                                                                                                                                                                                                                                                      …
                                                                                                                                                                                                                                                                      common foo {
                                                                                                                                                                                                                                                                        asdf(qwerty(i+k));
                                                                                                                                                                                                                                                                        printf(“%d”, (int)write); // cast to int
                                                                                                                                                                                                                                                                        bar();
                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                      …
                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                    And then you could `mycc diff-common foo` and see:

                                                                                                                                                                                                                                                                      <file>:<line>: common
                                                                                                                                                                                                                                                                      <file>:<line>: common
                                                                                                                                                                                                                                                                      …
                                                                                                                                                                                                                                                                      <file>:<line>:
                                                                                                                                                                                                                                                                        @@…@@
                                                                                                                                                                                                                                                                        -asdf(qwerty(i+j));
                                                                                                                                                                                                                                                                        +asdf(qwerty(i+k));
                                                                                                                                                                                                                                                                        @@…@@
                                                                                                                                                                                                                                                                        -printf(“%p”, write));
                                                                                                                                                                                                                                                                        +printf(“%d”, (int)write); // cast to int
                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                    With this you can track named common blocks (allows using surrounding context like i,j,k). Without them being functions and subject for functional entanglement $subj discusses. Most common code gets found out and divergences get bold. IDE support for immediate highlighting, snippeting and auto-common-ing similar code would be very nice.

                                                                                                                                                                                                                                                                    Multi-patching common parts with easily reviewing the results would also be great. Because the bugs from calling a common function arise from the fact that you modify it and it suddenly works differently for some context. Well, you can comment a common block as fragile and then ignore it while patching:

                                                                                                                                                                                                                                                                      common foo {
                                                                                                                                                                                                                                                                        // @const: modified and fragile!
                                                                                                                                                                                                                                                                        …
                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                    You still see differences but it doesn’t add in a multi-patch dialog.

                                                                                                                                                                                                                                                                    Not expecting it to appear anywhere though, features like that are never considered. Maybe someone interested can feature it in circles? (without my name associated)

                                                                                                                                                                                                                                                                    • badmintonbaseba 14 hours ago

                                                                                                                                                                                                                                                                      In C++ it's an idiom to use immediately invoked lambdas:

                                                                                                                                                                                                                                                                        auto x = []{
                                                                                                                                                                                                                                                                          /*...*/
                                                                                                                                                                                                                                                                          return 5;
                                                                                                                                                                                                                                                                        }();
                                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                      There is/was an attempt to introduce a more of a first-class language construct for such immediate "block expressions":

                                                                                                                                                                                                                                                                      https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p28...

                                                                                                                                                                                                                                                                      I'm not convinced that automatic checking of copy-paste errors of such blocks make much sense though. At least I think the false positive rate would be way too high.

                                                                                                                                                                                                                                                                      • sltkr 13 hours ago

                                                                                                                                                                                                                                                                        GCC has supported statement expressions for ages: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html

                                                                                                                                                                                                                                                                        They're also used extensively in the Linux kernel, mostly to implement macros: https://github.com/search?q=repo%3Atorvalds%2Flinux%20%22(%7...

                                                                                                                                                                                                                                                                        • wruza 14 hours ago

                                                                                                                                                                                                                                                                          IIFE exist, but are cumbersome to type/read in most languages. C++ is probably the winner by syntax and semantics here.

                                                                                                                                                                                                                                                                          false positive rate would be way too high

                                                                                                                                                                                                                                                                          The key idea is not to have identical blocks, but to have a way to overview changes in similar code, similar by origin and design. It’s a snippet diff tool, not a typo autocorrector. There’s no false positives cause if “common foo” has zero diff in all cases, it probably should be “foo(…)”.

                                                                                                                                                                                                                                                                          • mkoubaa 12 hours ago

                                                                                                                                                                                                                                                                            Now if only c++ could guarantee copy elision from lambda returns...

                                                                                                                                                                                                                                                                            • badmintonbaseba 12 hours ago

                                                                                                                                                                                                                                                                              There is a lot of guaranteed copy elision since C++17, what exactly do you mean?

                                                                                                                                                                                                                                                                              • pjmlp 12 hours ago

                                                                                                                                                                                                                                                                                It depends if using C++17 and later versions.

                                                                                                                                                                                                                                                                            • atq2119 2 hours ago

                                                                                                                                                                                                                                                                              Regarding compound statements returning values: There are a number of languages which have that, including Rust. Ironically, it made me wish for a reversed form of the construct, i.e. something like

                                                                                                                                                                                                                                                                                  { ...; expr } --> x;
                                                                                                                                                                                                                                                                                  // x is a new variable initialized to expr
                                                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                                              I feel like this would help readability when the compound statement is very large.
                                                                                                                                                                                                                                                                              • lexicality 14 hours ago

                                                                                                                                                                                                                                                                                Can you help me understand why this would be beneficial, other than avoiding using the word "function"?

                                                                                                                                                                                                                                                                                • wruza 14 hours ago

                                                                                                                                                                                                                                                                                  I guess you’re asking about the block part — it’s a minor syntactic convenience and not the main point of the comment. It avoids the word function/lambda or def-block and related syntactic inconvenience like parentheses around and at the end and interference with ASI (when applicable).

                                                                                                                                                                                                                                                                                  • kibwen 8 hours ago

                                                                                                                                                                                                                                                                                    You're looking for blocks-as-expressions, e.g. the following is valid Rust:

                                                                                                                                                                                                                                                                                        let x = {
                                                                                                                                                                                                                                                                                            whatever;
                                                                                                                                                                                                                                                                                            5
                                                                                                                                                                                                                                                                                        }; // assigns 5 to x
                                                                                                                                                                                                                                                                              • easeout 7 hours ago

                                                                                                                                                                                                                                                                                Come to think of it, execute-and-inhibit style as described here is exactly what's going on when in continuous deployment you run your same pipeline many times a day with small changes, and gate new development behind feature flags. We're familiar with the confidence derived from frequently repeating the whole job.

                                                                                                                                                                                                                                                                                • fabiensanglard 6 hours ago

                                                                                                                                                                                                                                                                                  How does a program work when its disallow "backward branches". Same thing with "subroutine calls" how do you structure a program without them?

                                                                                                                                                                                                                                                                                  • Jtsummers an hour ago

                                                                                                                                                                                                                                                                                    It allows one backward branch. Think of it like hand-rolling your OS scheduler for processes/threads. You also have to track your "program counter" yourself. As a silly example:

                                                                                                                                                                                                                                                                                      typedef enum state {EVEN, ODD} state_t;
                                                                                                                                                                                                                                                                                      state_t task1 = EVEN;
                                                                                                                                                                                                                                                                                      state_t task2 = EVEN;
                                                                                                                                                                                                                                                                                      while (1) {
                                                                                                                                                                                                                                                                                        switch(task1) {
                                                                                                                                                                                                                                                                                        case EVEN:
                                                                                                                                                                                                                                                                                            // do even things
                                                                                                                                                                                                                                                                                            task1 = ODD;
                                                                                                                                                                                                                                                                                            break;
                                                                                                                                                                                                                                                                                        case ODD:
                                                                                                                                                                                                                                                                                            // do odd things
                                                                                                                                                                                                                                                                                            task1 = EVEN;
                                                                                                                                                                                                                                                                                            break;
                                                                                                                                                                                                                                                                                        default:
                                                                                                                                                                                                                                                                                            fprintf(stderr, "WTF?\n");
                                                                                                                                                                                                                                                                                            exit(1);
                                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                                        switch(task2) {
                                                                                                                                                                                                                                                                                        case EVEN:
                                                                                                                                                                                                                                                                                            // do even things
                                                                                                                                                                                                                                                                                            task2 = ODD;
                                                                                                                                                                                                                                                                                            break;
                                                                                                                                                                                                                                                                                        case ODD:
                                                                                                                                                                                                                                                                                            // do odd things
                                                                                                                                                                                                                                                                                            task2 = EVEN;
                                                                                                                                                                                                                                                                                            break;
                                                                                                                                                                                                                                                                                        default:
                                                                                                                                                                                                                                                                                            fprintf(stderr, "WTF?\n");
                                                                                                                                                                                                                                                                                            exit(1);
                                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                    For every "process" you've unrolled like this, you have to place it into its own switch/case or call out to a function which has similar logic (when subroutines aren't disallowed). If the process is short enough you let it execute all the way through, bigger processes would need to be broken apart like above to avoid consuming an entire cycle's time (especially important in real-time systems).
                                                                                                                                                                                                                                                                                    • bdowling 5 hours ago

                                                                                                                                                                                                                                                                                      You can do a lot with a program that looks like:

                                                                                                                                                                                                                                                                                        while(1) {
                                                                                                                                                                                                                                                                                          if (condition1)
                                                                                                                                                                                                                                                                                            ...
                                                                                                                                                                                                                                                                                          if (condition2)
                                                                                                                                                                                                                                                                                            ...
                                                                                                                                                                                                                                                                                          // etc
                                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                      Subroutine calls can be eliminated by inlining everything, using macros to make the code more manageable. Loops can be simulated using macros that expand to multiple copies of the code, one for each step.

                                                                                                                                                                                                                                                                                      One advantage is that the program will never get into an unbounded loop because the program counter will always advance towards the end of the main loop.

                                                                                                                                                                                                                                                                                      • kragen 5 hours ago

                                                                                                                                                                                                                                                                                        Well, you have one backward branch at the end of the program, and you inline your subroutines. I'm pretty sure you've written shaders for ancient GPUs that had similar limitations? And anything you can do in hardware you can do without subroutine calls, and in hardware the loop starts again on every clock cycle.

                                                                                                                                                                                                                                                                                      • nuancebydefault 14 hours ago

                                                                                                                                                                                                                                                                                        > The function that is least likely to cause a problem is one that doesn’t exist, which is the benefit of inlining it.

                                                                                                                                                                                                                                                                                        I think that summarizes the case pro inlining.

                                                                                                                                                                                                                                                                                        • atulvi 2 hours ago

                                                                                                                                                                                                                                                                                          who read this in John Carmack's voice?

                                                                                                                                                                                                                                                                                          • adamrezich 7 hours ago

                                                                                                                                                                                                                                                                                            I find that when initially exploring a problem space, it's useful to consider functions as “verbs” to help me think through the solution, and that feels useful in helping me figure out a solution to my problem—I've isolated some_operation() into its own function, and it's easy to see at a glance whether or not some_operation() does the specific thing its name claims to do (and if so, how well).

                                                                                                                                                                                                                                                                                            But then after things have solidified somewhat, it's good practice to go back through your code and determine whether those “verbs” ended up being used more than once. Quite often, something that I thought would be repeated enough to justify being its own function, is actually only invoked in one specific place—so I go back and inline these functions as needed.

                                                                                                                                                                                                                                                                                            The less my code looks like a byzantine tangle of function invocations, and the more my code reads like a straightforward list of statements to execute in order, the better it makes me feel, because I know that I'm not unnecessarily hiding complexity, and I can get a better, more concrete feel for what my program's execution looks like.

                                                                                                                                                                                                                                                                                            • rossant 17 hours ago

                                                                                                                                                                                                                                                                                              (2014)

                                                                                                                                                                                                                                                                                            • randomtoast 15 hours ago

                                                                                                                                                                                                                                                                                              My browser says "The connection to number-none.com is not secure". Guess it is only a matter of time until HTTPS becomes mandatory.

                                                                                                                                                                                                                                                                                              • Ono-Sendai 15 hours ago

                                                                                                                                                                                                                                                                                                There is actually a major problem with long functions - they take a long time to compile, due to superlinear complexity in computation time as a function of function length. In other words breaking up a large function into smaller function can greatly reduce compile times.

                                                                                                                                                                                                                                                                                                • badmintonbaseba 15 hours ago

                                                                                                                                                                                                                                                                                                  That honestly feels like a minor problem, and not something to optimize for. Also an aggressively inlining compiler will experience exactly the same problem. AFAIK at least clang always inlines a static (as in internal linkage) function if it's used only once in the translation unit, no matter how large it is.

                                                                                                                                                                                                                                                                                                  • Ono-Sendai 14 hours ago

                                                                                                                                                                                                                                                                                                    Visual studio doesn't do that inlining. And it is a significant problem, I have had to refactor my code into multiple functions because of it.

                                                                                                                                                                                                                                                                                                    • badmintonbaseba 12 hours ago

                                                                                                                                                                                                                                                                                                      It might be a significant problem, but not in the code, but the compiler. Fair enough, you are working around a compiler issue.

                                                                                                                                                                                                                                                                                                      • Ono-Sendai 8 hours ago

                                                                                                                                                                                                                                                                                                        If you consider any superlinear complexity a 'compiler issue' I guess.

                                                                                                                                                                                                                                                                                                        • badmintonbaseba 8 hours ago

                                                                                                                                                                                                                                                                                                          It absolutely is, if it makes compile times unreasonable for reasonable code. Compilers have to make trade-offs like this all the time, they can't use overly excessive optimizations.

                                                                                                                                                                                                                                                                                                          • Ono-Sendai 8 hours ago

                                                                                                                                                                                                                                                                                                            I dunno. O(n^2) is for sure a bug. But O(nlogn) I think is reasonable.

                                                                                                                                                                                                                                                                                                            • badmintonbaseba 7 hours ago

                                                                                                                                                                                                                                                                                                              O(nlogn) is probably reasonable. Why break up a long function then if you are experiencing O(nlogn) scaling of compile time on function size?

                                                                                                                                                                                                                                                                                                              • Ono-Sendai 7 hours ago

                                                                                                                                                                                                                                                                                                                Because it can still result in compile times I find excessive. For example breaking up a function that takes 5 seconds to compile into a bunch of functions that take 1 to 2 seconds in total.

                                                                                                                                                                                                                                                                                                  • tightbookkeeper 12 hours ago

                                                                                                                                                                                                                                                                                                    If you are willing to make code worse to micro optimize compile times (not even sure this is true) then you should not use any modern language with complex type checking (rust, swift, C#, etc).

                                                                                                                                                                                                                                                                                                    • Ono-Sendai 8 hours ago

                                                                                                                                                                                                                                                                                                      Writing a medium to large program in C++, you really need to fight long compile times or they can get out of hand. That affects the way you write code quite a lot, or it should at least. I've heard Rust and Swift also suffer from long compile times.

                                                                                                                                                                                                                                                                                                      • tightbookkeeper 6 hours ago

                                                                                                                                                                                                                                                                                                        Agreed.

                                                                                                                                                                                                                                                                                                        But For C++ template combinatorics are going to dominate any slow down due to function length.

                                                                                                                                                                                                                                                                                                  • gdiamos 17 hours ago

                                                                                                                                                                                                                                                                                                    How much of this is specific to control loops that execute at 60hz?

                                                                                                                                                                                                                                                                                                    • lloeki 16 hours ago

                                                                                                                                                                                                                                                                                                      None.

                                                                                                                                                                                                                                                                                                      > The real enemy addressed by inlining is unexpected dependency and mutation of state, which functional programming solves more directly and completely. However, if you are going to make a lot of state changes, having them all happen inline does have advantages; you should be made constantly aware of the full horror of what you are doing. When it gets to be too much to take, figure out how to factor blocks out into pure functions (and don.t let them slide back into impurity!).

                                                                                                                                                                                                                                                                                                      Some years ago at job foo I wrote a Ruby library that was doing some stuff. Time was of the essence, I was a one-man team, and the trickiness of it required a clear understanding of the details, so I wrote a single ~1000 LOC file comprising of the entirety of the namespace module of that library, with but a couple or three functions.

                                                                                                                                                                                                                                                                                                      Then a new hire joined my one-man team. I said: apologies for this unholy mess, it's overdue for a refactoring, with a bunch of proper classes with small methods and split in a few files accordingly. They said: not at all, the code was exceptionally clear; I could sit and understand every bit of it down to the grittier critical details in under an hour, and having seen it written this way it is obvious to me that these details of interactions would not have been abstracted away, but obscured away.

                                                                                                                                                                                                                                                                                                      • otikik 14 hours ago

                                                                                                                                                                                                                                                                                                        I have worked with many developers and I have seen them follow two distinct paths when encountering complex code.

                                                                                                                                                                                                                                                                                                        There's one camp that wants to use abstractions and names, and there's another (in my experience, smaller) camp which prefers to have as few abstractions as possible, and "every gritty detail visible".

                                                                                                                                                                                                                                                                                                        I think both strategies have advantages and disadvantages. The group that likes abstractions can "ignore parts of the code" quickly, which potentially makes them "search" faster. If there's a bug that needs fixing, or a new feature that needs to be added, they will reach the part of the code that will need modifications faster.

                                                                                                                                                                                                                                                                                                        The detail-oriented people can take a bit longer to identify the code that needs modification, but they also tend to be able to make those modifications faster. They also tend to be be great "spelunkers". They seem to have a "bigger cache", so to speak. But it is not infinite. They will eventually not be able to hold all the complexity in their heads, just like the first group. It will just take a bit longer.

                                                                                                                                                                                                                                                                                                        I am firmly on the first group and that is how I write my code. I have been fortunate enough to encounter enough people from the other group to know not to diss their code immediately, and to appreciate it for its merits. When working in a team with both kinds of personalities one has to make compromises ("please remove all of these 1-line functions, Jonathan will hate them", and "could you split this 3k lines function into 2 or 3 smaller ones, for easier review?").

                                                                                                                                                                                                                                                                                                        • whstl an hour ago

                                                                                                                                                                                                                                                                                                          Some might consider me part of the "second group", but I'm perfectly fine with abstractions and I create them all the time.

                                                                                                                                                                                                                                                                                                          I do however have a problem with indirections that don't really abstract anything and only exist for aesthetical reasons.

                                                                                                                                                                                                                                                                                                          Not every function/method is an "abstraction". Having too many one-line methods is as bad as pretending that functions with 2k/3k lines are appropriate in all cases.

                                                                                                                                                                                                                                                                                                        • Cthulhu_ 15 hours ago

                                                                                                                                                                                                                                                                                                          Thing is, a lot of developers see long code and think "this is a Bad Thing" because of dogma, but in practice, a lot of developers never actually wrote anything nontrivial like that.

                                                                                                                                                                                                                                                                                                          • yoz 16 hours ago

                                                                                                                                                                                                                                                                                                            Your final ten words of the comment are a perfectly concise explanation of the problem; thank you! And it drives home something I often forget about why code units should do Only One Thing.

                                                                                                                                                                                                                                                                                                          • cryptonym 16 hours ago

                                                                                                                                                                                                                                                                                                            > Minimize control flow complexity and “area under ifs”, favoring consistent execution paths and times over “optimally” avoiding unnecessary work.

                                                                                                                                                                                                                                                                                                            If your control loop must always run under 16ms, you better make sure the worst case is 16ms rather than trying to optimise best or mid case. Avoid ifs that skips processing, that's good for demo but doesn't help reaching prod quality goals. Sometimes it doesn't bring the benefits you think, sometimes it hides poorly optimised paths, sometimes it creates subtle bugs. Of course always use your own discernment...

                                                                                                                                                                                                                                                                                                            That would be very different in a typical cloud app where the goal is to keep CPU, memory and network usage as low as possible, not much caring about having a constant response time on each REST endpoint.

                                                                                                                                                                                                                                                                                                            • Netch 16 hours ago

                                                                                                                                                                                                                                                                                                              All the code which is not in hot path may conform to any rules, and typically is designed according to something like SOLID, to make understanding and maintenance as simple as possible (and suitable to any average coder).

                                                                                                                                                                                                                                                                                                              All the code which performance, memory cost, etc. is critical, should be adjusted to fit into required confine even if it will violate all other tenets. This often results in combination of opposite approaches - anything that does well.

                                                                                                                                                                                                                                                                                                              Finally, one just profiles the code and fixes all most spending paths. This is what now any average programmer can do. What it canʼt do - and what Carmack has been doing for decades - is to predict such places and fixes them proactively at architectural level; and to find tricky solutions that average joe-the-programmer hasnʼt heard ever.

                                                                                                                                                                                                                                                                                                              • cjfd 14 hours ago

                                                                                                                                                                                                                                                                                                                Mostly, all of it. People who are not writing that kind of loop probably should not do any of this. Optimize for code clarity, which may involve either inlining or extracting depending on the situation.

                                                                                                                                                                                                                                                                                                              • exodust 10 hours ago

                                                                                                                                                                                                                                                                                                                For some reason this quote by Carmack stands out for me:

                                                                                                                                                                                                                                                                                                                > "it is often better to go ahead and do an operation, then choose to inhibit or ignore some or all of the results, than try to conditionally perform the operation."

                                                                                                                                                                                                                                                                                                                I'm not the audience for this topic, I do javascript from a designer-dev perspective. But I get in the weeds sometimes, maxing out my abilities and bogged down by conditional logic. I like his quote it feels liberating... "just send it all for processing and cherry-pick the results". Lightbulb moment.

                                                                                                                                                                                                                                                                                                                • wiz21c 10 hours ago

                                                                                                                                                                                                                                                                                                                  my CPU does that all of the time, it is speculative execution :-)

                                                                                                                                                                                                                                                                                                                • mellosouls 11 hours ago

                                                                                                                                                                                                                                                                                                                  (2014)

                                                                                                                                                                                                                                                                                                                  Ten years ago - a long time in coding.

                                                                                                                                                                                                                                                                                                                  • rm445 11 hours ago

                                                                                                                                                                                                                                                                                                                    It's at least twenty new web frameworks, but maybe not so long in low-level stuff. You can probably rely on C99 being available now more than you could in 2014.

                                                                                                                                                                                                                                                                                                                  • jjallen 14 hours ago

                                                                                                                                                                                                                                                                                                                    One benefit that I can think of for inlined code is the ability to "step" through each time step/tick/whatever and debug the state at each step of the way.

                                                                                                                                                                                                                                                                                                                    And one drawback I can think of is that when there are more than something like ten variables finding a particular variable's value in an IDE debugger gets pretty difficult. It would be at this point that I would use "watches", at least in the case of Jetbrains's IDEs.

                                                                                                                                                                                                                                                                                                                    But then yeah you can also just log each step in a custom way verifying the key values are correct which is what I am doing as we speak.

                                                                                                                                                                                                                                                                                                                    • IshKebab 14 hours ago

                                                                                                                                                                                                                                                                                                                      I think the major problem with this is scope. Now a variable declared at the top of your function is in scope for the entire function.

                                                                                                                                                                                                                                                                                                                      Limiting scope is one of the best tools we have to prevent bugs. It's one reason why we don't just use globals for everything.

                                                                                                                                                                                                                                                                                                                      • wheybags 12 hours ago

                                                                                                                                                                                                                                                                                                                        You can artificially create scope. I often write code like:

                                                                                                                                                                                                                                                                                                                            Foo f = null;
                                                                                                                                                                                                                                                                                                                            {
                                                                                                                                                                                                                                                                                                                              ... stuff with variables
                                                                                                                                                                                                                                                                                                                              f = barbaz;
                                                                                                                                                                                                                                                                                                                            }
                                                                                                                                                                                                                                                                                                                        • SJC_Hacker 9 hours ago

                                                                                                                                                                                                                                                                                                                          Is the point that any var declared in between the braces automatically goes out of scope, to minimize potential duplication of var names and unintended behavior ?

                                                                                                                                                                                                                                                                                                                          The worst I've seen is old school C programmers who insisted on reusing loop variables in other loops. Even worse, those loop variables were declared inside the loop declaration, which old C standards allowed to visible outside of it.

                                                                                                                                                                                                                                                                                                                          So they would have stuff like this

                                                                                                                                                                                                                                                                                                                            for(int i=0; i<10; i++) { ... }
                                                                                                                                                                                                                                                                                                                            for (;i<20;i++) { ... } 
                                                                                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                                                          Later versions of C++ disallowed this, which led to some interesting compile failures, which led to insistence of the old stubborn programmers that new compilers simply not be used
                                                                                                                                                                                                                                                                                                                          • IshKebab 8 hours ago

                                                                                                                                                                                                                                                                                                                            Now you have to make `f` nullable and you run the risk of not initialising it and getting a null pointer.

                                                                                                                                                                                                                                                                                                                            You can't do it in C, but in functional style languages you can do this:

                                                                                                                                                                                                                                                                                                                              let f = {
                                                                                                                                                                                                                                                                                                                                let bar = ...;
                                                                                                                                                                                                                                                                                                                                let baz = ...;
                                                                                                                                                                                                                                                                                                                                let barbaz = ...;
                                                                                                                                                                                                                                                                                                                                barbaz
                                                                                                                                                                                                                                                                                                                              };
                                                                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                                                            Which is a lot nicer. But if you ask me it's just a function by another name except it still doesn't limit scope quite as precisely as a function.
                                                                                                                                                                                                                                                                                                                      • rickreynoldssf 11 hours ago

                                                                                                                                                                                                                                                                                                                        The clean code people are losing their collective minds reading that. lol

                                                                                                                                                                                                                                                                                                                        • randomdata 8 hours ago

                                                                                                                                                                                                                                                                                                                          Why's that? Uncle Bob seems pretty clear that most of your code should be free of side effects, and that necessary state mutation should be isolated to one place. Carmack is saying the same thing.

                                                                                                                                                                                                                                                                                                                        • shortrounddev2 10 hours ago

                                                                                                                                                                                                                                                                                                                          Can someone explain what inlined means here? It was my assumption that the compiler will automatically inline functions and you don't need to do it explicitly. Unless it means something else in this context

                                                                                                                                                                                                                                                                                                                          • AlotOfReading 10 hours ago

                                                                                                                                                                                                                                                                                                                            It means not using explicit functions, just writing the same code as little inline blocks inside the main function because it allows you to see all the things that would be hidden if all the code wasn't immediately visible.

                                                                                                                                                                                                                                                                                                                            To the other point though, the quality of compiler inlining heuristics is a bit of a white lie. The compiler doesn't make optimal choices, but very few people care enough to notice the difference. V8 used a strategy of considering the entire source code function length (including comments) in inlining decisions for many years, despite the obvious drawbacks.

                                                                                                                                                                                                                                                                                                                            • shortrounddev2 9 hours ago

                                                                                                                                                                                                                                                                                                                              Well there's also compiler directives other than `inline`, like msvc's `__inline` and `__forceinline` (which probably also have an equivalent in gcc or clang), so personally I don't think you need to make the tradeoff between readability and reusability while avoiding function calls. Not to mention C++ constevals and C-style macros, though consteval didn't exist in 2007

                                                                                                                                                                                                                                                                                                                              • AlotOfReading 8 hours ago

                                                                                                                                                                                                                                                                                                                                __forceinline is purely a suggestion to the compiler, not a requirement. Carmack's point isn't about optimizing the costs of function calls though. It's about the benefits to code quality by having everything locally visible to the developer.

                                                                                                                                                                                                                                                                                                                                • shortrounddev2 8 hours ago

                                                                                                                                                                                                                                                                                                                                  It's an interesting view because I find neatly compartmentalized functions easier to read and less error prone, though he does point out that copying chunks of code such as vector operations can lead to bugs when you forget to change some variable. I guess it depends on the function. Something like

                                                                                                                                                                                                                                                                                                                                      Vector c = dotProduct(a, b);
                                                                                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                                                                                  is readable enough and doesn't warrant inlining, I think. There's nothing about `dotProduct` that I would expect to have any side effects, especially if its prototype looks like:

                                                                                                                                                                                                                                                                                                                                      Vector dotProduct(Vector const& a, Vector const& b) const;
                                                                                                                                                                                                                                                                                                                                  • AlotOfReading 8 hours ago

                                                                                                                                                                                                                                                                                                                                    That's a pure function, which he says should be the goal. It's impure functions that he's talking about.

                                                                                                                                                                                                                                                                                                                          • sylware 11 hours ago

                                                                                                                                                                                                                                                                                                                            I have been super picky about what JC says since he moved the ID engine from plain and simple C99 to c++.

                                                                                                                                                                                                                                                                                                                            • oglop 7 hours ago

                                                                                                                                                                                                                                                                                                                              Oh good, a FP post. I love watching people argue over nothing.

                                                                                                                                                                                                                                                                                                                              Here’s the actual rule, do what works and ships. Don’t posture. Don’t lament. Don’t idealize. Just solve the fucking problem with the tool and method that fits and move on.

                                                                                                                                                                                                                                                                                                                              And do not try to use this comment threat to understand FP. Too many cooks, and most of the are condescending douchebags. Go look at Wikipedia or talk with an AI about it. Don’t ask this place, it’s all just lectures and nitpicks.

                                                                                                                                                                                                                                                                                                                              • eapriv 7 hours ago

                                                                                                                                                                                                                                                                                                                                “Comment threat” is a nice one.

                                                                                                                                                                                                                                                                                                                                • adamrezich 7 hours ago

                                                                                                                                                                                                                                                                                                                                  This isn't actually an FP post.