Watch the presentation here:
https://www.infoq.com/presentations/Simple-Made-Easy
(the slideshow plays alongside with the video.)
My notes on the presentation:
"Simplicity is prerequisite for reliability" - Edsger W. Dijkstra
Simple vs. Complex, Easy vs. Hard
Simple: one role; one task; one concept; one dimension. (But not one instance; one operation.) Lack of interleaving, but not lack of cardinality. Objective notion.
Easy: near, at hand; near to our understanding / skill set; familiar; near our capabilities. Relative notion.
Regarding "at hand" and "familiar":
I think that collectively we are infatuated with these two notions of easy.
We are just so self-involved in these two aspects, it's hurting us tremendously.
All we care about is, can I get it instantly and start running it in five seconds?
It could be this giant hairball that you got, but all you care for is can you get it?
In addition, we are fixated on "oh, I can't, I can't read that."
I can't read German. Does this mean that German is unreadable? No! I don't know German!
So, you know, this sort of approach is definitely not helpful.
In particular, if you want everything to be familiar you will never learn anything new
'cause it can't be significantly different from what you already know, and not drift
away from the familiarity.
Regarding "near to our capabilities":
due to a combination of hubris and insecurity we never really talk about whether or not something is outside of our capabilities.
Regarding easy being "relative":
Playing the violin and reading German are really hard for me; they are easy for other people; certain other people.
So, unlike simple, where we can go and look for interleavings, look for braiding, easy is always going to be easy for whom; or hard for whom; it is a relative term. The fact that we throw these things around, sort of casually saying, oh I like to use that technology cause it is simple, and when I am saying simple I mean easy, and when I am saying easy I mean because I already know something that looks very much alike that, is how this whole thing degrades, and we can never have an objective discussion about the qualities that matter to us in our software.
Constructs and artifacts
We program with constructs. We have programming languages, we use particular libraries, and those things in and of themselves, when we look at them, when we look at the code we write, have certain characteristics in and of themselves. But we are in a business of artifacts, right? We don't ship source code, and the user doesn't look at our source code and say "oh, this is so pleasant". They run our software. And they run it for a long period of time. [...] all that stuff, the running of it, the performance of it, the ability to change it, all is an attribute of the artifact. Not the original construct. But again, here we still focus so much on our experience of the use of the construct. --Oh look, I only had to type 16 characters. Wow, that's great, no semicolons. Or things like that. This whole notion of programmer convenience. Again, we are infatuated by it, not to our benefit.
So, we are going to contrast this with the impacts of long term use. What does this mean to use this long term? And, what's there? What's there is all the meat, right? Does the software do what it is supposed to do? Is it of high quality? Can we rely on it doing what it's supposed to do? Can we fix problems when they arise, and if we are given a new requirement, can we change it? These things have nothing to do with the construct as we typed it in, or very little to do with it, and have a lot to do with the attributes of the artifact.
We have to start assessing our constructs based around the artifacts, not around the look and feel of the experience of typing it in, or the cultural aspects of it.
We can only hope to make reliable those things we can understand.
We can only consider a few things at a time.
Intertwined things must be considered together.
Complexity undermines understanding.
What's true of every bug found in the field?
It passed the type checker.
What else did it do?
It passed all the tests.
Your ability to reason about your program is critical to debugging.
Development speed:
Now, of course everyone is going to start moaning, but I have all this speed, I am agile, I'm fast, this easy stuff is making my life good because I have a lot of speed.
So, what kind of runner can run as fast as they possibly can from the very start of the race?
(Audience: Sprinters.)
Right. Only someone who runs really short races!
But of course we are programmers, and we are smarter than runners, apparently, because we know how to fix that problem, right? We just fire the starting pistol every hundred yards; and call it a new sprint. Right?
(Applause)
I don't know why they haven't figured that out.
It's my contention, based on experience, that if you ignore complexity you will slow down, you will invariably slow down over the long haul. Of course if you are doing something that is really short term, you don't need any of this, you can write it, you know, in ones and zeros.
Emphasizing ease gives early speed.
Ignoring complexity will slow you down over the long haul.
On throwaway or trivial projects, nothing much matters.
If you focus on ease, and ignore simplicity (so I am not saying you can't try to do both, that'd be great) but if you focus on ease you will be able to go as fast as possible from the beginning of the race, but no matter what technology you use, or sprints or firing pistols or whatever, the complexity will eventually kill you; it will kill you in a way that will make every sprint accomplish less, most sprints being about completely redoing things that you have already done, and the net event is that you are not moving forward in any significant way.
Now, if you start by focusing on simplicity why can't you go as fast as possible right from the beginning? Cause some tools that are simple are actually as easy to use as some tools that are not, why can't you go as fast then? You have to think --you have to actually apply some simplicity work to the problem before you start, and that's going to give you this ramp-up.
So, one of the problems I think we have is this conundrum that some things that are easy actually are complex.
But we don't care about that, right? Again, the user does not look at our software and they don't actually care very much about how good a time we had when we were writing it. What they care about is what the program does, and if it works well it will be related to whether or not the output of those constructs were simple --in other words, what complexity did they yield.
When there is complexity there, we are going to call that _incidental complexity_. It was not part of the user asked us to do, we chose the tool, it had some incidental complexity.
The mental capability part
The fact is we can learn more things, we actually can't get much smarter. We are not going to move our brain closer to the complexity, we have to make things near by simplifying them. The truth here is that there are not these super bright people who can do these amazing things and everyone else is stuck. Because the juggling analogy is pretty close. The average juggler can do three balls. The most amazing juggler in the world can do like nine balls or twelve, or something like that. They can't do twenty, or a hundred. We are all very limited. Compared to the complexity we can create, we are all statistically at the same point in our ability to understand it, which is not very good.
(section about parens rather uninteresting to anyone not using them.)
You never see in these discussions was there a tradeoff? Is there any downside? Is there anything bad that comes along with this? Never, nothing; we are looking all for benefits. So I think that as programmers now I think we are looking all for benefits and we are not looking at the byproducts.
Complect vs. Compose
- Composing simple components is the key to writing robust software.
State is Never Simple
- Complects value and time
- It is easy, in the at-hand and familiar senses
- Interweaves everything that touches it, directly or indirectly
- Not mitigated by modules, encapsulation
Construct | Complects |
State | Everything that touches it |
Objects | State, identity, value |
Methods | Function and state, namespaces |
Syntax | Meaning, order |
Inheritance | Types |
Switch/matching | Multiple who/what pairs |
var(iable)s | Value, time |
Imperative loops, fold | what/how |
Actors | what/who |
ORM | OMG |
Conditionals | Why, rest of program |
The Simplicity Toolkit
Construct | Get it via... |
Values | final, persistent collections |
Functions | a.k.a. stateless methods |
Namespaces | language support |
Data | Maps, arrays, sets, XML, JSON etc |
Polymorphism a la carte | Protocols, type classes |
Managed refs | Clojure/Haskell refs |
Set functions | Libraries |
Queues | Libraries |
Declarative data manipulation | SQL/LINQ/Datalog |
Rules | Libraries, Prolog |
Consistency | Transactions, values |
Complexity that you have no control over: Environmental Complexity (Inherent)
Not part of the problem, part of the implementation. (You cannot go back to the customer and say the thing you wanted is not good because I have GC problems.)
"Programming, when stripped of all its circumstantial irrelevancies, boils down to no more and no less than very effective thinking so as to avoid unmastered complexity, to very vigorous separation of your many different concerns". --Edsger W. Dijkstra
Strictly separating what from how is the key to making how somebody else's problem.
"Simplicity is the ultimate sophistication". --Leonardo da Vinci
Here is a somewhat shorter and somewhat different version of this presentation:
No comments:
Post a Comment