1. 27
  1.  

  2. 10

    I’ve started valuing simplicity a lot more lately, I think unless there is a very good reason the simpler the code, the better. But, I believe that correctness should go first. If a program is not correct it doesn’t matter if it’s simple, it will just not do whatever it’s meant to do, so it is a bad program. I understand where you are coming from, if it’s complex the chances of it also being not correct are higher. But I still think correctness should be placed above.

    That said I agree that we should strive for simplicity a lot more than we do. Just recently, at the new job I’ve started at, I’ve been rewriting a whole part of the codebase which has mostly come down to simplifying a lot what was there. I’ve found that a very good way of simplifying is thinking about the data flow inside your application. Instead of thinking in some “elegant” abstract organization of code think what data comes in, how the data is transformed and then how it leaves. Once you’ve found how the data is transformed, the program structure will become a lot simpler in my experience. So that’s mostly what I’ve been doing.

    1. 12

      I think it’s easier to make simple code correct than to make correct code simple (without starting over and having to make it correct again). If our objective is to reach the same end point, then we should take the easier path.

      1. 1

        I think I might disagree. How do I know my code is correct? However I know, that is the key. Even in the worst case of starting over— something someone who isn’t me may do in the future— correctness remains.

        Writing complicated code in the exploration of a design space can be the easier path.

        1. 1

          If we’re talking about a section of code that will be more or less written once and considered “finished”, then I would agree that correctness should come first. This situation certainly exists, but many of us have probably never encountered it. In the case of “living” software (for which the requirements are constantly evolving, for better or for worse), simplicity makes ongoing correctness more attainable. To be clear, though, I don’t think this is a “pick two” kind of a deal, you need all three, but the order in which you satisfy them matters.

      2. 3

        but it’s not just a choice “oh shall I make this simple or go with a complex solution?”, it’s a constant battle. it takes a mix of art, design, genius, care. We are told not to do rewrites but rewrites are one of the best ways to achieves simplicity.

        Instead of thinking in some “elegant” abstract organization of code think what data comes in, how the data is transformed and then how it leaves. Once you’ve found how the data is transformed, the program structure will become a lot simpler in my experience. So that’s mostly what I’ve been doing.

        I think this is one of the best guidelines we could give for “how to achieve simplicity” in programming.

        1. 2

          but it’s not just a choice “oh shall I make this simple or go with a complex solution?”, it’s a constant battle. it takes a mix of art, design, genius, care.

          Yes, simplicity in a complex (or even in a simple!) domain is very hard to achieve, and I think we should all strive for that as much as we can. But I still think that correctness has to come first for the simplicity to be meaningful.

          We are told not to do rewrites but rewrites are one of the best ways to achieves simplicity.

          Couldn’t agree more, I recently read an article (that I can’t find now) about how repetition of code is less costly than the wrong abstraction. I think that is very true, and for that to work we need to be constantly refactoring and rewriting our code in order to simplify in the right ways instead of settling in a wrong abstraction early in the process. Bloat and complexity are diseases for a program.

      3. 16

        Complicated software breaks. Simple software is more easily understood and far less prone to breaking: there are less moving pieces, less lines of code to keep in your head, and fewer edge cases.

        Sometimes code is complicated because the problem is complicated.

        And sometimes the simple solution is wrong, even for something as basic as averaging two numbers.

        1. 3

          But there’s a difference here: a code is simple when it doesn’t introduce (too much of) its own accidental complexity. The innate complexity of the field is out of the equation, can’t do anything about it. But the code must strive to express its intent as simple as possible. It’s not a contradictory goal.

          1. 3

            No, it’s not the problem that’s complicated, it’s the underlying platform on which they chose to develop. You wouldn’t have that bug in Common LISP, Ruby, or any environment with big nums.

              1. 1

                Funny as he is, there were systems- and performance-oriented variants of LISP that either stayed low-level or produced C output that was low-level. They were really fast with many advantages of LISP-like languages. PreScheme was an early one. My current favorite, since they beat me to my idea of C-like Scheme, was ZL where authors did C/C++ in Scheme. With Scheme macros, one might pick the numeric representation that worked best from safest to fastest. Even change it over time if you had to start with speed on weak hardware but hardware got better.

                These days, we even have type-safe and verified schemes for metaprogramming that might be mixed with such designs. So, you get clean-looking code that generates the messy, high-performance stuff maybe in verified or at least type-checked way. People are already doing similar things for SIMD. And you’re still using x86! And if you want, you can also use a VIA x86 CPU so you can say hardware itself was specified and verified in LISP-based ACL2. ;)

                1. 1

                  What if you try to help the process along by immersing both objects in water?

              2. 3

                I’m not sure this really rebuts the claim. Is complicated code that solves complicated problems immune from breaking?

                Also, I don’t think he recommended stopping at simple and ignoring correct.

                1. 3

                  Is complicated code that solves complicated problems immune from breaking?

                  It’s more that some problem cannot be solved with simple code, because you can’t capture the whole complexity of the problem without writing a lot of code to capture it.

                  Consider tax code. Accurately following tax law is going to be messy because tax law itself is messy.

                  1. 1

                    Definitely not. If you have simple but wrong, it’s no good by definition. You can “not have” fast, but essentially, you still need “fast enough”. If you can accomplish the task, and you can do so simply and correctly, but can’t work it quick enough for real-life workloads, then in those cases you might as well call it broken.

                    1. 1

                      I am put in mind of the quote:

                      You can make simple software with obviously no defects, or complicated software with no obvious defects.

                      I don’t even think “correct” software is required–for many lucrative things, you have a human being interpreting the results, and oftentimes incorrect or wrong output can still be of commercial value.

                      That’s why we have customer support folks. :)

                      1. 2

                        This is a misquote of C.A.R. Hoare:

                        I conclude there are two ways of constructing software design: one way is to make it so simple there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies.

                        Nothing wrong with a misquote btw, as it means you internalized the statement rather than regurgitating the words :).

                    2. 1

                      If I understand correctly (hah), his point is that if you aim for simplicity, it’s easier to ensure correctness.

                    3. 5

                      The conflation of “correct” with the absence of bugs makes this argument hard to critique.

                      1. No code is bug-free.
                      2. Software with bugs is not correct.
                      3. Simple code is easier to make correct.
                      4. Simple code is easier to make fast.
                      5. Therefore: prioritise simple code, to make future work easier.

                      If I’m understanding this argument correctly, then I see several fallacies. The biggest problem is the assertion that no code can ever be correct. Then why even try for simplicity? What is the heuristic for knowing if my simple code is correct… enough?

                      I think there might be something in another assertion: correct code is easier to make simple. Most first cuts at a problem are complicated. But it’s hard as hell to get to simple without knowing what logic can stay and what logic is extraneous.

                      You need to take problems apart, identify smaller problems within them and ruthlessly remove scope until you find the basic problem you can apply a basic solution to.

                      This proposal defers the conflict. Once I have a “basic” problem and solution… then what?

                      This argument ignores the jump from not-working code to working code. This argument assumes “simple” code is easy to understand and easy to change.

                      Get off my lawn. 😉

                      1. 3

                        The conflation of “correct” with the absence of bugs makes this argument hard to critique.

                        Yeah. For me, coming from a theoretical CS background, correctness is meaningless without a formal specification. If you don’t have a spec, how can you even know what correctness is? Code that you debug into existence alongside a kind of “I know it when I see it spec” can be perfectly functional and useful, but not “correct” in any meaningful sense.

                        (Sometimes of course, you debug something into existence & then realise that there’s an underlying structure to the problem that you hadn’t seen before. Bonus! Now you can spec it out and catch future errors, if you care enough to do that.)

                      2. 5

                        Simple > correct resonates with me greatly. My day job is writing developer tools (specifically, build analysis) for big enterprises, and it’s surprising how often a simpler (but less correct) approach works much better than a correct approach. Our customers are often very okay with having approximately correct answers as long as (1) they understand how we’re approximating and (2) the tool’s algorithm is simple enough for them to work around it if they need special behaviour.

                        One example of this is Bower build analysis. The Bower configuration spec is extremely complex, and our first attempt at analysing the dependency resolution logic in Bower was bug-prone and often failed in ways that were difficult to debug. It turns out that the simpler approach (just read bower_components) worked well enough for customers about 95% of the time, and was significantly more reliable.

                        You could make an argument that if a simpler implementation is correct enough, it’s actually still correct (we’ve simply shifted the goalposts), but I think that “simple > correct” does a good job of capturing the spirit of the lesson.

                        1. 2

                          Yes, the intuition around “correct” brings to mind a waterfall-style process where we start by defining (formally or informally) what it is we want to do, and this is carved in stone forever more. Anything which doesn’t fulfill that definition is “incorrect”.

                          Yet I’ve often found that something turns out to be a little hairier than I’d like, but if we redefine what it is we’re trying to do then a small, elegant, reliable implementation may fall out; even though this is “incorrect” according to the original definition.

                          As you say, this is moving goalposts. We could backsplain that our simple implementation is correct for the “actual” goal of ‘delivering customer value’ or somesuch, but that quickly becomes too hand-wavey to be useful. I think it’s better in these situations to think in an agile-like feedback loop, where we’re figuring out what we want and how to do it at the same time.

                          and our first attempt at analysing the dependency resolution logic in Bower was bug-prone and often failed in ways that were difficult to debug

                          As an aside, for things like this I try to keep in mind that predictability is a useful feature. It’s often preferable to have something that predictably fails in, say, 10% of cases, than it is to have a “smart” system which only fails 2% of the time but is difficult to tell when that will be. For the former we can draw a line around that 90% and say everything within it is solved; for the latter we’ll always have uncertainty.

                        2. 3
                          1. 2

                            I’m pretty much totally in support of this thesis. Simplicity is imperative.

                            Interesting cultural note: the Hacker News crowd pretty much disagreed (https://news.ycombinator.com/item?id=17489934). Most of the commenters there thought that correctness should be first.

                            1. 5

                              My own revelation was understanding that correctness is not a state, but a process: you get to the correct code iteratively through fixing bugs. Simplicity, thus, should come first as it helps (or even required) getting there.

                            2. 2

                              This idea remimds me of the Worst Is Besser article I just finished reading a few days ago, in which the author opposes this view. I found it quite interesting, and maybe others, who, like I do, tend to agree with the article posted here, might too.