1. 12
  1.  

  2. 22

    So I think we can agree that there is one design rule, or ‘pattern’ by analogy, that always holds:

    Make it simpler.

    “Make it simpler” ignores all the many, many practical factors that push code to be complex. What if the “simple” code is so slow people hate using your software? What if the “simple” code would be in a different language? What if there’s a difference between local and global simplicity? What if the simple code is much harder to test and deploy? What about the Law of Requisite Variety?

    In architecture, where the problems that buildings and city planning need to solve don’t change, a set of patterns can be expected to be distilled after some time and to remain more or less comprehensive. But the nature of code is very different. Once something is solved in code, it doesn’t has to be solved again. The analogy is completely inapplicable.

    Do you know this? Have you ever done architecture or city planning? I’ve been talking to a lot of cross-discipline programmers as part of a longform project, and they all agree we completely misunderstand this.


    I’m burnt out on “simplicity” essays. What even is simplicity? We all use it but don’t have a consensus, or even good definitions. Which is simpler: shared memory concurrency, or message passing concurrency? Shared memory is much, much simpler, requiring fewer primitives, fewer moving parts, fewer axioms, fewer abstractions. It also leads to much more globally complex and error-prone code.

    1. 8

      Simple code also requires a simple problem, and a simple world to be modeled by the code. Text is simple if you ignore any language other than English, somewhat simple if you ignore any language outside of Western Europe as long as you add complexity for ß upcasing to SS as opposed to any single letter, still pretty simple if you ignore any language which does not use the Latin alphabet as long as you add another hack to account for dotted and dotless I in Turkish…

      Once you move on to most of the world, solving the general problem of text is hard. It requires complicated code because you’re trying to capture an organic system, and the only reason Latin script languages are as simple as they are is because you’re getting a pre-chewed version hammered down by printing presses and typewriters, and shorn of things like scribal abbreviations and most ligatures and even “missing” a few letters like yogh and wynn, not to mention the long s, which coexisted with the short s for a good, long time.

      1. 1

        Yeah, in some code reviews I did, I was surprised to find disagreement with fellow programmers when I tried to use argument of “simple” - in that we saw simplicity in different ways (esp. in architectural choices).

        1. 1

          Do you know this? Have you ever done architecture or city planning?

          I haven’t, so this is just an assumption, an attempt to understand why design patterns may have been appealing seeing that they don’t add the promised (or expected) value in practice.

          What even is simplicity? We all use it but don’t have a consensus, or even good definitions. Which is simpler: shared memory concurrency, or message passing concurrency? Shared memory is much, much simpler, requiring fewer primitives, fewer moving parts, fewer axioms, fewer abstractions. It also leads to much more globally complex and error-prone code.

          I agree, we don’t have good definitions and that sucks, because not having a set of rules to follow always feels bad. For me simpler at least means easier to understand. It is very hard to do hard measurements of whether something is easy to understand. However, it is not impossible to say whether one approach is easier to understand than another, in other words, if a refactor was valuable or not.

          I am afraid I don’t know of a better practical tactic to decide on the adequacy of a structure than just changing code and seeing if I made it easier to understand. Given that functional requirements are met, of course, and given all circumstances, conditions and consequences. I agree that this is not stated explicitly in the article.

          I think the example of choosing a concurrency model is a great one. Do we prefer global complexity and the potential for pitfalls over the complexity of a larger number of moving parts? I believe that acknowledging that there is no general answer to this question is better than believing that I should be able to solve this with a pre-defined pattern if only I would have studied pre-defined patterns enough.

          1. 1

            What even is simplicity? We all use it but don’t have a consensus, or even good definitions. Which is simpler: shared memory concurrency, or message passing concurrency? Shared memory is much, much simpler, requiring fewer primitives, fewer moving parts, fewer axioms, fewer abstractions. It also leads to much more globally complex and error-prone code.

            I can only recommend Rich Hickey’s Simple Made Easy, which helps answer why just having fewer parts doesn’t make something simple.

            1. 3

              A few months back I asked people on Twitter to give me two samples of code, in the same language and doing the same thing, but one had to be “simple but not easy” and the other “easy but not simple”. Most of the responses ended up contradicting each other. I don’t think we have a sense of what “simple” means.

              1. 1

                I think the problem with such a challenge is that what is easy will differ from person to person — I think we should be able to agree on whether a thing is simple vs. complex, but if you ask for easy or not easy (which both sets of responses are also trying to answer) it will come to depend very much on who’s answering.

          2. 5

            Very true. The problem is when you have to deal with complex problems, which is our stock and trade. Then you have to choose how to make the problem simpler, which will necessarily require a tradeoff somewhere. The art of choosing this tradeoff is often called “design”.

            1. 1

              “The biggest challenge is how to structure code so that its complexity stays manageable.”

              I disagree that this is the biggest challenge. Instead I contend that the biggest challenge is how to abstract or model a large, complex problem or system into working code.

              Maintainability is just one measure of quality of that effort.