1. 44
  1.  

  2. 42

    Reminds me of a quote:

    Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.

    • Brian W. Kernighan
    1. 13

      :) Came here to post that.

      The blog is good but I’m not convinced by his argument. It seems too worried about what other people think. I agree that we have to be considerate in how we code but forgoing, say, closures because people aren’t familiar with them or because we’re concerned about how we look will just hold back the industry. Higher level constructs that allow us to simplify and clarify our expression are a win in most cases. People can get used to them. It’s learning.

      1. 8

        I think he may not disagree with you as much as it sounds like. I don’t think that sentence says “don’t use closures,” just that they’re not for impressing colleagues. (It was: “You might impress your peers with your fancy use of closures… but this no longer works so well on people who have known for a decades what closure are.”)

        Like, at work we need closures routinely for callbacks–event handlers, functions passed to map/filter/sort, etc. But they’re just the concise/idiomatic/etc. way to get the job done; no one would look at the code and say “wow, clever use of a closure.” If someone does, it might even signal we should refactor it!

        1. 5

          It seems too worried about what other people think.

          I agree with your considerations on learning.
          Still, just like we all agree that good code must be readable, we should agree that it should be simple too. If nothing else, for security reasons.

        2. 2

          On the other hand, sometimes concepts at the limit of my understanding (like advanced formal verification techniques) allow me to write better code than if I had stayed well within my mental comfort zone.

        3. 15

          Reminds me of this quote about Niklaus Wirth, the creator of Pascal from this article by Michael Franz.

          And true to his quest for simplicity, Wirth continuously kept improving his compilers according to this metric, even if this meant throwing away a perfectly workable, albeit more complex solution. I still vividly remember the day that Wirth decided to replace the elegant data structure used in the compiler’s symbol table handler by a mundane linear list. In the original compiler, the objects in the symbol table had been sorted in a tree data structure (in identifier lexical order) for fast access, with a separate linear list representing their declaration order. One day Wirth decided that there really weren’t enough objects in a typical scope to make the sorted tree cost-effective. All of us Ph.D. students were horrified: it had taken time to implement the sorted tree, the solution was elegant, and it worked well – so why would one want to throw it away and replace it by something simpler, and even worse, something as prosaic as a linear list? But of course, Wirth was right, and the simplified compiler was both smaller and faster than its predecessor.

          1. 6

            Faulkner said: “In writing, you must kill your darlings.” Definitely applies to software too.

            1. 2

              What do you mean by that in this case?

              1. 3

                I think he’s referring to excising little bits of “clever” code when their purpose could legitimately be handled with simpler code. Some complexity is warranted, other isn’t

          2. 10

            I don’t quite see how object-oriented programming or functional programming has to stand in opposition to simple programming. Surely, abusing or forcing idioms from these padigramms, leads to unreadable and confusing code, but a good padigramm will (or should) enable you to simplify solving a more complex problem, by providing an infrastructure to describe it. Ultimately you would have to end up with assembler (or worse), if you follow his idea to the end.

            A certain degree of abstraction and “complicated” words is always necessary, if only for purley pragmatic reasons - otherwise you’ll end up describing the same existing concepts over and over again.

            Edit: Spelling mistakes and formatting fixed.

            1. 15

              I didn’t read it as being about OOP or FP specifically, but the attitude of needing to always looking for excuses to try out the latest techniques you’ve learned regardless of whether they are appropriate for the setting. I call this the Labyrinth effect, after http://p.hagelb.org/labyrinth.jpg

              I have worked with folks who have done the whole “I just learned about monads, so I’m going to write a bunch of monadic code at work even though there is no call for it and it just makes the code impossible to follow by anyone but me” thing, and it ended up causing all that code to get thrown out eventually, but only after a long struggle to get it working reliably.

              Sometimes I wonder if the main advantage of coding side projects is to be a release valve for cleverness.

              1. 5

                On the flip sode, you’re not going to recognize that a particular idiom is well suited to a task without burning yourself a couple times to find its limits. To continue with monads, something like railway-oriented programming might be just the abstraction to make your production code easier to reason about and maintain. Not that you should necessarily be twirling your mustache as your coworkers struggle to untie themselves from a monad railway, either…

                Side projects can be pretty good proving grounds for techniques, but you need to be bad at new abstractions /somewhere/ to figure out how to leverage those into good business value. It’s unfortunate that this distinction between work projects/side projects pushes us either into cartoon villianry or unpaid professional development.

                Have other crustaceans found an effective way to reconcile these two?

                1. 4

                  Absolutely; it’s not that you should always try your hardest to avoid making this mistake, but that you should recognize that it is a mistake and learn from it when it happens.

                  1. 2

                    If you’re familiar in the domain, you’re much more likely to have success taking a leap in the code, and vice versa. If it’s the second time you’ve written a renderer, maybe try out that new technique you read about, if it’s the first time maybe stick to what you know.

                  2. 1

                    the attitude of needing to always looking for excuses to try out the latest techniques you’ve learned regardless of whether they are appropriate for the setting.

                    Well that was kind of what I was talking about - when you force one kind of problem into a framework for solving different kinds of problems, you’ll end up in a mess (think of all the design patterns that were though of in connection to Java). But, as mention in the other thread, being frightened of tools, won’t make it easier for everyone.

                2. 9

                  You might impress your peers with your fancy use of closures… but this no longer works so well on people who have known for a decades what closure are.

                  It reminds me of the age old argument that simplistic is what I know and what I don’t know must be complicated, because I am getting by without knowing it. And maybe it is exactly closures which would make the code much easier.

                  It’s like lenses. I think they look complicated, so they must be useless. But maybe they would simplify all the boilerplate code I am writing again and again (always with fresh new subtle bugs that I have to debug).

                  I do see the point of not writing code for “cool code’s sake”. I wrote a CPS based logging solution which I thought was very fun to write and cute but in review it was pointed out that it is completely pointless and what I wanted to to could be achieved much simpler in a more understandable way.

                  1. 9

                    Write code. Not too much. Mostly procedural. (Apologies to Michael Pollan)

                    1. 1

                      Is this a Michael Pollan quote?

                      1. 7

                        There’s a Michael Pollan quote summarizing his nutrition philosophy, which the above quote’s form is adapted from: “Eat food. Not too much. Mostly plants.”

                        1. 1

                          Ahhhh! Everything makes sense now.

                    2. 9

                      While we’re here, a link to a classic: http://thedailywtf.com/articles/the-inner-json-effect

                      As a particular example of meta programming gone awry, one can get pretty far down the “smart” road before realizing its a disaster. And then “it’s only complicated because you don’t understand” kicks in and new comers figure it’s their fault.

                      1. 6

                        As someone currently writing free monad code for something that could probably just be dependency injection, this hit a bit too close to home.

                        1. 4

                          In terms of selecting an appropriate vocabulary for discussion or writing, it is imperative to understand your audience. “Bigger” words or technical language often exist to communicate ones ideas more fully and exist as useful abstractions in the right context. The author’s concerns are valid, but it is also worth not limiting yourself to the most simple forms of programming or communication as an absolute rule.

                          Higher level languages and techniques are inherently more complex, but the benefit of this abstraction is being able to focus on solving a problem by communicating your intent in terms that more closely match your understanding of the problem.

                          The term “simple” can also mean a lot of different things. C and Ruby are both simple in some ways and complex in others.

                          1. 3

                            Higher level languages and techniques are inherently more complex

                            No. The implementation of high-level languages on machines designed to run Fortran and C might be involved, but their semantics of high-level languages can be quite simple, actually. Not that simple low-level languages cannot possibly exist, of course.

                            1. 2

                              Completely agree. My perspective is very biased towards current implementations of high level languages on top of machines designed to be operated at a lower level.

                            2. 2

                              The term “simple” can also mean a lot of different things. C and Ruby are both simple in some ways and complex in others.

                              C is simple, while Ruby is easy… is what I wanted to write immediately after reading last paragraph - which might just prove your point ;)

                              1. 1

                                If this is a reference to the well known Rich Hickey talk “Simple Made Easy”, which nails down the definition of simple to something more objective and less squishy than its normal usage, then neither C nor Ruby would be simple in that sense.

                                Are either of them easy? Well, how familiar are you with them? At one point during college o had written so much C for different systems classes that it was easy. Now, no way. In this sense, simple is an intimrinstic property of the design, ease is a property of the subject answering (in this case, me).

                                1. 1

                                  Rich Hickey does not give a technically precise definition of “simple”. I have in the past proposed two measures of simplicity:

                                  • The size of a formal semantics for the language in question.
                                  • The size of the proofs of correctness for benchmark programs written in the language in question.

                                  But most other people were at best indifferent to my suggestion. At worst they were made very angry.

                                  EDIT: Fixed typo.

                                  1. 1

                                    I can see both your point and some other peoples’ objections.

                                    For the record, I didn’t say technically precise. His definition is more philosophically or conceptually well founded, if you want me to get specific. It won’t give a snippet of code or a program a “complexity score”, but it is useful for reasoning how relatively complex/simple two concepts, approaches, or solutions are.

                                    And that’s a big improvement over “I feel like closures are complex”, “oh yeah well I feel like they’re simple!”.

                                    Falling short of full technical precision does not delegitimize the his definition, or the conversations that can be had using that definition.

                                    1. 1

                                      I can see both your point and some other peoples’ objections.

                                      I can see their objections too: “How dare you! A formal semantics would expose the complexity of what I’m trying to sell as simple! You damn little…!”

                                      His definition is more philosophically or conceptually well founded, if you want me to get specific.

                                      Philosophy is useful when it guides technical work, but it’s the latter that ultimately we need to get done. If “simplicity” is to become a software quality criterion, we need to abstract, quantify and measure it.

                                      Falling short of full technical precision does not delegitimize the his definition, or the conversations that can be had using that definition.

                                      Without a formal definition, his idea of “simplicity” is just a proxy for his aesthetics.

                            3. 4

                              It’s very interesting that many of these comments are people claiming that what they do is actually simple - you just have to be smart / educated enough to understand it. I’m a believer that if the people reading your code can’t understand it then it’s you who’ve failed, not them.

                              1. 7

                                Doesn’t that depend a lot on who’s reading your code? I’ve been working with a few different Java codebases and sets of collaborators lately, and people seem to differ in whether they find some of the newer Java 8 features like streams to increase or decrease readability. In a lot of cases they take what used to be normally implemented with verbose boilerplate code, and add a language-level feature doing things in a somewhat more functional style.

                                To some people this introduces too much fancy functional stuff that makes it hard to read. To others it makes the code easier to read by reducing the amount of verbosity and boilerplate you have to wrangle with. I’m a bit more in the 2nd camp: classic Java is so verbose that it’s hard for me to read. You basically have to be good at skimming for common patterns/idioms and able to “read” it in terms of these higher-level constructs, because reading it line-by-line you get bogged down immediately. I personally find it clearer and more readable if those constructs are just reified at the language level and I don’t have to keep a bag of common Java boilerplate in my head while reading code (e.g. write .filter() instead of a nested loop that I have to recognize as doing filtering), but others obviously disagree.

                                So sure, I agree you should write code that people can read, but to do that, you first need to know who’s going to be reading it! There’s no code that’s universally more readable…

                                1. 5

                                  Taking this perspective further, perhaps simplicity isn’t an innate property of code, but something that is relative to the community around it.

                                  1. 2

                                    Have you watched the talk Simple Made Easy? I’d be interested in hearing you thoughts with respect to those ideas.

                                    1. 3

                                      It has been on my list for a while, and prompted by your comment I finally did so, or at least I read the transcript :) Great talk - My favorite part was how he differentiates simple from easy. This leaves room for people’s ability to learn stuff. Often when people ask for simplicity it seems like they’re expressing a desire not to have to learn something. Which I think is a way of shortchanging oneself as well as one’s code.

                                      The central conceit of this talk is that complexity is the intertwining of parts. The more that parts of a program are intertwined the more complex it is. Perhaps there are other definitions we could give complexity, and they would probably have their own pluses and minuses. But this is the the one Hickey uses.

                                      Personally I think this is great part about this talk. Rather than using the word simplicity like everyone knows what it means, he comes up with an underlying principle of simplicity and explores the consequences of this principle.

                                      Some examples he gives of the intertwining that implies complexity: value and time, function and state, inheritance and types, what is going to be done and who is going to do it.

                                      Now in terms of whether complexity is relative to the community of people around a codebase, I think that position is still defensible, even accepting his central metaphor.

                                      For starters, How could we objectively determine the degree of intertwining in a program? Surely we must first identify what’s being intertwined. Regarding abstraction as a tool to fight complexity he says:

                                      I can’t totally explain how this is done. It’s really the job of designing, but one approach you can take is just to do who, what, when, where, why, and how. If you just go through those things and sort of look at everything you’re deciding to do and say, “What is the who aspect of this? What is the what aspect of it?” This can help you take stuff apart.

                                      In other words, during the design phase we can take stuff apart so that it’s not intertwined when we write our program. It’s interesting that he says he can’t explain how this is done, even though he gives some prompts to jump-start this process.

                                      Thinking about this right now, I have a hard time imagining that there aren’t endless ways to analyze a program into parts. And depending on these analyses, we are going to have different parts, different braids, different degrees of intertwining, and thus different evaluations of complexity.

                                      Surely there must be some significance to choosing some way of cutting up the program versus others? And surely some of these cut ups are going to yield more useful estimates of complexity than others, relative to how easy it is to change the code?

                                      Going further, it may be that intertwining time and value (for example) is more significant of a problem in some programs than others. And the significance of this problem, this complexity, must be relative to the technical team changing the code, as well as relative to the users of the program and the expectations they have of it.

                                      Another way to think about this:

                                      Let’s assume for the sake of argument that simplicity is intrinsic to a program. Hickey states that the value of simplicity is that it helps us to more reliably change programs. But the reliability of a program, its ability to do what it’s what’s expected to, depends, obviously, in part on the expectations about it. And these expectations are extrinsic to the program. For example, some programs are easier to change reliably merely by virtue of there being fewer expectations around them.

                                      Regarding whether simplicity is intrinsic to a program, he says:

                                      if something is interleaved or not, that’s sort of an objective thing

                                      Well I guess my position is that the concept of simplicity is objective, but only “sort of” objective, per the quote. Given certain expectations, and a certain way of cutting up the code to analyze complexity, the complexity we estimate is objective. But these expectations depend on the community, on what’s important to the community, and these analyses depend on how we decide to cut up the program so that we can identify complexity. Even granting his central conceit, which I like, I still think it must be right to say that complexity isn’t really intrinsic to the program itself, but is relative to the community around it.

                                2. 3

                                  Just these day, I’m wondering if I should port the OBNC compiler for Oberon to Jehanne.

                                  More than 20 years passed after my last Pascal program, and I did not know it has evolved so much.
                                  On one hand, Oberon aim for simplicity matches pretty well the design of Jehanne. On the other hand, Jehanne kernel and core is written in C and while I’m looking for an higher level and safer language to work with, I’m unsure about its practicality.

                                  I wonder if I could write a package manager or a build system (I currently use Go+JSON to cross build the os) in Oberon-07… The kind of simplicity of this language seems different from the others, I’m not even able to describe it.

                                  1. 1

                                    If you like his Oberon stuff, check out Pascal-S he did before it. I never did any error handling in prototypes since it complicated it. Look how simple he makes it in Pascal/S, though. Id have just started with that if I knew about it. Ive forgotten compiler implementation since so there might be equally-simple but better ways by now.

                                  2. 3

                                    Simplistic programming would be everywhere, if all problems or systems to be modeled were simple. In some ways I consider bleating on about programming languages and technical features as a sign of immaturity. How to model and represent complex problems and systems into code using a design approach or paradigm, reliably, consistently and predictably, is the real issue.

                                    1. 1

                                      And a matter of experience and hard won intuition, not to be pared down to simple “always, never” notions.

                                      But people seem to not get as passionate about those kinds of discussions…

                                      Do you think we all want to believe that it is easy, and the we just happen to know the right mixture of “always, never” rules?

                                      I’ll note that Lobsters is far better/more mature about this than anything else I’ve read.

                                    2. 3

                                      Simplicity comes in different forms. You may find sequential mutation on a set of variables simple, but another person may find mutation-free substitution of variables simple. These techniques are also applicable in different problem domains.

                                      1. 3

                                        There’s some kind of “dialectic” to be uncovered here—sorry for the jargon.

                                        In my experience, and I think it’s quite common, intellectual curiosity is a motor for learning, but also shamefully related to varieties of “I’m very smart”-ism.

                                        If I were feeling particularly contrarian I would even argue that one-upmanship in this domain can be productive and fun.

                                        Haskell is now entering the mainstream of coding, and I kind of think that wouldn’t have happened without a lot of people having fun with being clever. Now, maybe Haskell is a huge mistake… I don’t think so, though.

                                        1. 3

                                          Welcome to Lobsters! Good to see you again!

                                          1. 1

                                            Thank you!