The Things That Pass For Simple I Can't Understand 35
(with apologies to Steely Dan for the nearly-lyrical title)
I’ve noticed that “Do the simplest thing that might possibly work” gets universal agreement in principle, and great divergence in practice.
Is a set of variables related by name prefix simpler than a named class containing the same variables? In the “fewest number of the fewest things” sense, I see a group of variables as a lot more to manage than a class. I can’t understand why some people pass the same group of loose variables to a number of methods. Isn’t a seven-argument method “complex”?
Is a custom exception “fancy” or “trivial”? I’ve had that discussion recently. Some feel one way, some the other. Is it more complex to throw a standard exception type and then try to figure out what it means elsewhere?
Is event-handling simpler than polling? I’ve heard this one too. I don’t know how other people see it. I think events have more complex plumbing, but that polling has a greater run-time complexity because of failure modes (periodicity issues, race conditions, etc). I think that it is simpler if you have fewer problems to look out for.
If you need a fixed set of java instances, is an enum simple or fancy? Here you have a newer language feature, but it exists to simplify the management of a fixed set of instances, so it leaves you less to deal with, no?
Is “more primitive” simpler? How about if you use arrays and integer indices into a string rather than a list of strings? Is that simpler or more complex? Is the Bowling Game “simple” because it uses a primitive array and a separate array counter instead of custom objects?
In C++, is it simpler to have a struct that contains two data members or to use the pair<> template? Is it “simpler” that you have to refer to them as “first” an “second”? Or is that just more obscure? I think that “obvious” is more simple.
I am not sure how other people judge simple. I think “simple to use”, “less bookkeepping”, “harder to mess up”, “less setup to call”, “fewer parameters” are simpler. Other people seem to have contrary thoughts.
Clearly simple involves more than “least thought” or “most primitive data components”. It should have something to do with the lack of effort in using something and the ease with which things can be used correctly. Well, clearly to me. And it should have to do with the least difficulty in reading the resulting code, and the least difficulty in changing it.
Unrelated to this, I find some tools that others find simple to be baffling, and some tools that are complex to others seem pretty easy to use to me. I suppose that’s a kind of “personal taste” but is there a more reliable rule to base “simple” on than personal choice? There should be if the rule is for us all to do the simplest thing that works, and to maintain the simplest design we can afford to build.
But I have unique viewpoints sometimes, and maybe simple to you isn’t simple to me. Personally, some of the things that are simple to you may be all but inscrutable to me. I guess that happens.
Another point is this: simplicity comes with experience. Beginner developers are almost always, by default, procedural programmers. They just don’t get the whole OOP thing that well. At this level, a collection of variables and some functions that act on those variables is indeed, “the simplest thing that can possibly work”. Over time, a bit like osmosis, they gradually shift over to another way of thinking.
Rather than simplicity being just a matter of personal taste, I think its a measure of both the depth of understanding and experience of the problem and the tools you use. The more you look at something, the more you understand it. It becomes simpler.
Simplicity also needs context. Simple to use or simple to build or simple to fix or simple to extend. These are often competing forces. Similar to the forces of time, quality, scope and cost, a change in one can have a chaotic effect on the others. Its a strategic decision that needs to be made early on, dare I say “up front” around here ;-) ?
Of course, common sense helps alot too.
To me “the simplest thing that can possibly work†is the thing that requires least effort for the team to undertand. Understanding is key to a successful team.
It is very important, to establish at what level the team is at, early in the project, and set the “simple-quota” accordingly. As Phil Bennet says above, some teams are comprised of people with little or no OO understanding and such a team, should not use complex patterns and OO principles, as it would not be simple for each member to understand.
On the other hand a team of OO specialists, each with 15 years of OO experience, “simple” would be implied by heavy use of patterns.
In his books “Domain Driven Design”, Eric Evans, talks of Ubiquitous Language as the common (among members of the team) understanding of the domain. A similar thing should be in place with regards, how we do things. So “simple” has the same meaning for any mambe of the team.
I’d like to put it more simply ;-)
Understanding trumps simplicity.
The main reason we want simple design is that simplicity makes it easier to understand. Any technical definition of “simple” is less relevant.
I both agree and disagree.
I think that there is incredible value in “understandable” but I think it’s a different thing.
I don’ t think that the reason we want simple is just so we can understand. I hink it has much more to do with fluidity of further development. Understanding it also has a lot to do with fluidity, but I am loath to claim that “simple” means “easy to understand”. I think it means something more, and says something about avoiding over-design and other bad practices.
I think that there needs to be a concrete, nearly mechanical definition of simple that has to do with the fewest interactions (methods) with the fewest elements (variable, object, struct, etc) and with requiring the least care from developers who use the code.
I think maybe there is something akin to “exposed failure modes” as the inverse of “simplicity”. If Ican pass parameters out-of-order, accidentally pass one integer where I mean another, or mess up the handling of an array by setting an integer value incorrectly, make an object unusable by calling its methods out-of-established-order, or the like, then I’m not looking at something simple but something treacherous in its exposure of complexity. A simple thing isn’t simple just because it’s easy on the inside, it has to be simple to use.
A friend said that complexity in the tests is just complexity looking for a better place to live in the model. I paraphrase, perhaps wrongly, so I’ll not mention names.
A simple system would have very few moving parts, and very few exposed failure modes, very few degrees of freedom of movement, and the use of it would be more-or-less obvious to a biased observer.
Also, the more work a user has to do to painstakingly set up an operation and painstakingly interpret the results, the less simple the operation is, regardless of patterns, primitive variables, and experience with advanced code.
I don’t have the research to back this up yet, but I feel in the very marrow of my bones that there is a concrete way to understand “simple” that should lead us to a much better sense of “the simplest thing that might possibly work.”
OTOH, I’ve been known to over-think simple things, so I could be totally wrong or totally irrelevant.
Eliyahu M. Goldratt, father of Theory of Constraints and an author worth looking up, defined simple as how many “levers” does something have to change the way it works. He had an example of a system with a cloud of boxes and lines that was complex to behold, but simple to control through one or two levers at the top. Another graph of components looked more like a straight line, but had a “lever” at each box.
This relates directly to the values of eliminating duplication through abstracting common behavior. The code may look more complex with more interfaces and abstract classes, but controling it over time is simpler.
He attributed this view to his physics background, and went on to say “There are no complex systems in reality”. You’ll have to listen to “Beyond the Goal” audio book to learn why. ;-)
Oops, found the slide that came with the audio book … the simple looking system that was actually complex was four unlinked nodes. You have to go to each one to control them.
The trouble with the “most understandable” criterion is that it really means “least common denominator.”
If you don’t use OOP because some members on the team aren’t familiar with it, then they’ll never have a chance to learn.
As I understand the story, the phrase originated when Kent was pairing with someone at the C3 project, and the someone got hung up on what to do next. Kent Beck seems to be real big on trying something to find out what happens rather than analyzing.
Consequently, I’ve taken it as a heuristic on how to get unstuck. It’s a bit more specific than “do something, don’t just sit there!” The goal is to keep moving in a generally productive direction. I wouldn’t read more into it than that.
John Roth
I tend to think of “simple” like you and others here have mentioned: smaller design footprint, fewer things to keep up with, less code to write (and maintain), easier to assimilate and understand, etc. Of course, as many have pointed out, simple is often hard to accomplish.
More on this topic at Ridiculous Simplicity.
Brian Sondergaard
http://blog.softwarearchitecture.com
Simplicity is complicated.
Simple is a personal choice
It’s interesting that one of the things that most developers try to strive is simplicity, especially when the essence of simplicity is such a personal sentiment…
http://www.cwk-technologies.co.uk/blog/2007/06/15/Simple+Is+A+Personal+Choice.aspx
There’s rather a great deal of difference between being a simple thing to imagine (cheesiest) and being simple. I respect the value of doing something so that you splash come code on the page, but then once it works it needs to have simple design.
If I scribbled up the first thing that crossed my mind and didn’t ever go through the effort of making it simple (expressive, clear, obvious, uncomplicated, etc) then my code base would go south in a big way.
So I’m okay with doing brute-force kinds of things like “return true” when TDD-ing, but then there is some standard of “simple” that interests me as a target for my refactoring.
YAGNI and OAOO and Tell-Dont-Ask are techniques for approaching “simple”, and I love that. I just wonder if there is some way to say what simple is, rather than just “what comes from YAGNI, OAOO, TDA, and tasteful use of explanatory functions and variables”. What is it really?
I think it has something to do with least-collateral-effort-to-use, and fewest-inobvious-failure-modes, fewest-parts, simplest-parts, structure-shy-interactions, etc.
So I totally respect what John says, and I still want something entirely other.
Doesn’t “simple” depend on shared experience? What’s simple to me could be unendingly complex to someone else (for example, a non-programmer).
One way this manifests itself is through abstractions. Abstractions often make things much simpler, but if you don’t understand the abstractions, you wind up totally lost. Design Patterns are a great example of this (Oh, it’s just a Flyweight!...What’s a flyweight?)
Another way would be the shared understanding of members on a team. While the team can often (but not always) come to an agreement about solution A being simpler than solution B, if you leave the friendly confines of the team, you often (but not always) start playing by a different set of rules.
@Mark Wilden. You are right about “most understandable” being the “least common denominator.†But this will only be a problem if you have too much spread in skill within your team, and I would argue that, that is a bad thing.
If you HAVE TO have a few team members with skills way lower than the rest, you need a time budget, to bring these up to speed, and this will ofcourse affect the progress.
@Tim Ottinger. I think understanding is key to all the things you mention. I see no reason to keep things simple if it’s not from an “understanding” point of view. It’s not hard to make a complex system work just as well as a simple system, unless the complexity defies our understanding of it. In essence, the final system, “doesn’t care” whether it is simple or complex. Only when we need to look at it again and need to understand it’s inner workings, do we see the difference.
I think that simple is a relative word, and can only be made concrete in it’s meaning among people with a common base.
BTW: I like your philosophical approach to these thing :-).
I don’t deny at all the value of “understandable”. I appreciate that. I think that’s “simple v. hard-to-understand” and am in favor of it.
I am currently interested (for different, related reasons) in “simple v. complex” though. I concede that the DTSTTMPW was really meaning “easiest to envsion right now”.
I also disagree with patterns being “simple”. Flyweight and visitor and several of the others strike me as being non-simple solutions, but reusable in their context nonetheless. I think that visitor in particular, though quite useful, is complicated in number of interactions and complexity of interactions.
Yes, yes, a complex system can work too. I am not saying that complexity can’t work. My goal is not to eliminate anything complex, but to work toward a definition of “reasonably complex” v. “unreasonably complex”. As with big O notation, the purpose is more to observe the difference between the required complexity and the actual complexity of the solution.
I recently read (I don’t recall where) that the skill of the developer is:
This is a good axiom, a nice observation. But it spurred me to think about things not just seeming simpler (a darned good goal all by itself) but being simpler in fact. I am now trying to find a way to measure simplicity.
I of course know about McCabe’s cyclomatic complexity (and I believe it’s right, also). But in an object system there should be something similar.
I think that “seeming” simple is relative, but that there should be an “actual simplicity” that is measurable and concrete.
@Tim. Hehe.. you might have seen that equation on my email signature or perhaps you read my little blog entry (http://blog.10footego.net/blog/_archives/2007/1/26/2684503.html) about my perception of skill :). But then again, maybe you saw it somewhere else. I do feel a little honoured though :).
I understand what you’re aiming for and I can see why my comments above, doesn’t quite cut it.
You should read Chuck Moore. He is a marginal guy. And his margin is a simplicity. He build his own processors because he does not like bloated hardware. For doing this he built his own OS, CAD, character set and keyboard.
OS is 2kb. CAD is 500 lines in his own language for sure. Charset is 86 characters, ~5.2 bits each. Keyboard has 27 keys.
Jef Fox has an article about Moore and his way: http://www.ultratechnology.com/forth.htm
Also his quotes: http://www.ultratechnology.com/moore4th.htm
> Isn’t a seven-argument method “complex�
Maybe. Sometimes simple-to-use means complex internally. For instance, one could read your arguments to mean that all parameters should be put in a struct first. That, however, may be more complex due to the overhead of having to create a struct if one doesn’t exist. If the seven arguments come from seven different objects, then the seven-argument-method is indeed the simplest one to use – anything else will involve the overhead of creating a struct from those seven values.
PJ: I agree. Having more collateral effort pushed into a client doesn’t decrease the complexity, it actually spreads it. If the function receives seven parameters and then manages them itself, it is in one place only. If, on the other hand, every caller must collect all the parameters into a structure or class before calling, then all of the callers have this collateral effort (caller-side complexity) to deal with. This is a fact dealing with cardinality: one function v. multiple callers. It is also clearly an issue of removing duplication.
But likewise there is the question of why we have seven parameters on a function. There are very few reasons for such a thing. One is primitive obsession, where (for instance) one may be passing coordinates for two objects as sets of loose integer variables instead of having a coordinates class. Another reason is that the function is doing several things and uses some of the variables to choose which of the others it should look at. It is also possible that the function is a constructor, and has a lot of dependencies (perhaps too many). At any rate, having seven parameters makes me suspect “not simple”. Likewise, having too many member variables or too many methods says “not simple”.
I remember Bjarne Stroustrup saying that in a well-designed system, the average number of parameters per function approaches one. I think that was in Champaign/Urbana, back in maybe the mid-to-late 90s. I think he’s right.
Given a choice, though, I do agree with you that complexity belongs in the smallest number of places, and in this case it should be pushed into the function, not pushed out as duplicated work on all the callers.
Jan: it might have been you. It’s a good quote regardless. I like it alongside this one on project risk:At work, I end up maintaining a lot of code that other people have written…in the end, to me, simple code is something that is easily understood. And which usually is code that is written in the simplest way possible. But the interesting thing is that the simplest code is usually written by the more experienced programmers…less experienced programmers tend to be inconsistent and simplistic (as opposed to programming simple-y) in their programming!
I thought DTSTTMPW had to do with the make-your-test-work part of TDD. Here simplicity has nothing to do with values of good design. Instead, simple means as few keystrokes as possible or the shortest implementation time. Duplicate as much as you like. Magic numbers are king in the world of simplicity.
Then you put on you refactoring hat. Simple might be a good value to use here too, but it has a completely different meaning. And if that meaning is so hard to define, then is it really a good word to use? Perhaps it should be refactored into something clearer? :)
I share Tim’s experiences regarding problems with the definition of simple. In the name of simplicity, a collegue recently wanted to duplicate a class (copy/paste 500 LOC) instead of using strategy pattern. I agree with my collegue that his solution is indeed simpler in many ways (simple to write, simple to use, simple to understand), and would therefore use other design values (simple to maintain, simple to change/modify) to motivate the strategy solution.
I think that the DTSTTMPW “simple” would have been better off named “easy” or “cheesy” or “dumbest”. I have respect for the word “simple” that isn’t deserved there.
I am really interested in “simple design” most of all. I admit that I should not have mentioned DTSTTMPW in this posting. I repent.
But I want a better definition of simple, and could be happy using a different word that means “simple” in this context. “Clean” starts to approach the right meaning, but is not precise enough.
Recommendations?
I doubt there is one word to express all you want, especially one that for some people does not at the same time mean the completely opposite (such as “simple”, and also “clean”, I’m afraid).
Perhaps it would be easier to find a few values to describe your vision. “Simple” could be one of them, but perhaps not the one with highest priority due to its disambiguous tendency.
I would use “maintainable” since maintain the code is what you do most of the time, and “modifyable” as the design requirements tend to change.
“Understandable” is a good candidate (and perhaps it’s part of “maintainable”) but not a top priority. It often takes a few moments to understand OO code due to the abstractions, while functionally decomposed code is more straight forward. Yet I prefer OO code.
“Easy to use” and “hard to misuse” are two more good values.
Or define the values by giving counter-examples. “Easy to write” is clearly one of those: code will be read many more times than it is written.
One suggestion is “Structural Simplicity” (thanks Mike). I think you are onto something here.
Okay, let’s look at the simplicity improvement if I create a class instead of having a set of loose variables. We can discuss an address class. Since I’m an American (and most of us are barely aware of how things work elsewhere), I’ll make it a US postal address. The address class has a street address, city, state, and zip code. The zip code has a funny relationship with city and state, in that only certain combinations are valid. The city has to be withing the state, and the zip code has to refer to that address combination (zip to city is many-to-many).
If I pass around the address as loose variables, the answer is more primitive but not simpler. Likewise if I place these all in a structure but pass it around in a writeable form, it is no simpler.
One problem is that there are a number of operations I can perform on strings that don’t make a lot of sense on addresses. Why do I want to substring them? “Worth” is a wholly different place than “Fort Worth”. There are a lot of operations I have available to me on a number of variables, and I have to know which ones make sense. This is a problem of too much variety. Each user of the structure will have to know which operations make sense on each member.
A second problem is invalid combinations. If I change the state, I will have to change the street address and likely the city. I will certainly have to change the zip code. There are many ways I can fail to have a valid combination. This is a problem of having too many exposed failure modes. Every client of this structure or set of variables will have to know how to validate them against each other.
The third problem is that I may be working more than one address at once, perhaps comparing them. I will have to maintain a clear separation in my head between the fields of the two, and which values are in which place. This is somewhat alleviated by the structure. The risk of accidental shuffling is increased by the number of variables I have at hand.
The fourth problem is that any act of copying to another structure, to a set of loose variables, to a file, to a document for network transfer, to a database or to any other source will have the same set of operations. By not managing an entity as a whole, I have duplication of structure and this a multiplication of maintenance points.
It is worse, by the way, if I were to make the zip code an integer. Does the zip + 4 overflow? Is the zip a five-digit or 9-digit zip (I need to know for formatting, and because some receivers may want only 5 digits). What does it mean to left shift, bitwise-or, add, multiply, or square a zip code? I have even more noise to sort through with an integer.
Loose variables may seem “simple” (having only one moving part each) but having loose variables for a single entity multiplies complexities. I can clearly see that many times a more primitive answer is not a simpler answer, and not even an easier answer. But still there is primitive obsession in much modern code.
If I put my loose variables into a class, I can restrict the set of operations one may choose to those that are meaningful for the class. I can add methods to the class to extend the meaning in a clear way. I can build operations at the class/instance level and refactor them together to avoid duplication. I can add decorators to give short-term features to the objects. I can make the object responsible for the validity of its own state. These are things that you can do with a class (“not the simplest thing” some say) that you cannot do easily with more primitive forms.
These kinds of structural simplicity relieve the burden on the programmer (and each of the functions written) to have to know the valid states and operations of the cluster of data elements. Some say this only hides the complexity, but I would say that it literally reduces the complexity of the methods using the class (which are many) and adds complexity only to the class definition (of which there is one).
That sounds like reducing overall complexity. That sounds more structurally simple, and also results in things being easier to use and harder to misuse.
I said above:I think that this example helps to make the case. Fewer moving parts (one v. five) for users, less freedom of movement, fewer decisions to be made, and far fewer options to sort through in order to decide what to do next.
That’s simpler. Or structurally simpler. Or “more minimal” maybe.
And, finally, for my C++ friends, look at how I can completely demolish the real structural simplicity of my class by returning each of the variables as a non-const reference.
More on simplicity and complexity: http://betterexplained.com/articles/combining-simplicity-and-complexity/
This article is very usefull for me! I can see that you are putting a lots of efforts into your blog. I will keep watching in your blog, thanks.
Some say this only hides the complexity, but I would say that it literally reduces the complexity of the methods using the class (which are many) and adds complexity only to the class definition (of which there is one).
I think maybe there is something akin to “exposed failure modes” as the inverse of “simplicity”. If Ican pass parameters out-of-order, accidentally pass one integer where I mean another, or mess up the handling of an array by setting an integer value incorrectly, make an object unusable by calling its methods out-of-established-order, or the like, then I’m not looking at something simple but something treacherous in its exposure of complexity.
good article
Okey oynamak hiç bu kadar zevkli olmadi. Online ve 3 boyutlu okey oyunu oyna ve turnuvalara sende katil.
for people that adore music, i suggest the monster powerbeats sport headphones to you, Just Beats Studio Purple has three color, red, whit and black. It is significant efficacy on this monster diddybeats in-ear headphones.
Women seeking probably the most excellent models inside the fashionable shoes or boots assortment uncover 2011 louboutin sandals available regarding exactly how pricey this kind with astounding designs are generally.
understand the abstractions, you wind up totally lost. Design Patterns are high quality headphones new design headphones
I have only one regret for landing on this article site. That regret is I can’t write really good content as well as this author.
Good informative content is written to target the thinking person. This content is brain-challenging yet straightforward. The viewpoints are written with a lot of thought and prep work behind them.
Well. Though I am not a good application developer. And I need do more hard work to improve myself. When I come to here. I know that I have come to the right place to learn something I need. Thanks for your good advice. And I will do the practice as possible as I can. Thanks.