Wednesday, December 27, 2006
  Economizing can be penny-wise and pound foolish

How I learned to stop worrying and love the separation of accidental and actual complexity in programs.”

Reinier Zwitserloot said something interesting:
What’s worth more? The macro facilities or the provably linear-time chasability of code identifiers and the like? All I know is: I have never read a satisfactory answer to the dilemma, merely a boatload of silent assumptions that the expressive power must be better… somehow.
I’m a simple man, and what matters to me is the amount of time it takes me to design code, to write code, to debug code, to understand the code that others have written, and to write code that others will understand.

The one factor that has risen above all others for these tasks is the signal to noise ratio in code. This idea has been far, far better explained as the difference between “accidental” and “inherent” (or “actual”) difficulty in programming.

Pick up the source code for a program and three highlighter markers, one green, one yellow, and one red. Go through the source. If you don’t understand what something does, mark it in red. If you understand it, but it has everything to do with the accidental difficulty of the implementation and nothing to do with the inherent difficulty of the problem, mark it in yellow. And finally, if something seems to express the problem and its solution fairly directly in an manner you understand, mark it in green.

What could be simpler? We want more green, less yellow, and absolutely no red in our programs. We can’t escape the yellow marker: if we like (I only say if we like) Ruby on Rails, we like the fact that our code has lots of green, because it’s written in a DSL aimed at CRUD applications on the web. But if we look inside Rails, we see lots of stuff that is yellow or—horribly for us—red. The yellow under the covers makes the green in our code possible.

Forever Undecided: A puzzle Guide to Godel

(Ad: Raymond Smullyan introduces Godel's incompleteness theorems and some interesting related results though puzzles about knights, knaves, and their ability to become self-aware.)
(I dare say that this was once the philosophy behind enterprisey stuff like SOAP and J2EE. Somebody, somewhere felt that these frameworks could help programmers write stuff that focused on the task at hand and left the accidental difficulties, the “gory details” as they were, to the framework.)


This is the principle behind structured programming and every programming language where we can build our own abstractions.

It’s not that we eliminate the yellow. Rather, we shift it away from the green so that there's one nice place where everything is green: we say that this is our solution, this is our idea. And there's this other mass of yellow that makes the green work, we say this is our infrastructure, this is the hardest working code in show business.

The opposite of this is where the green and yellow is all mixed up. This is why Design Patterns are a sign of weakness, not of strength. With a pattern, you have yellow code intermingled with your green code. You may be so used to it you think you don’t see the yellow, but nevertheless your Visitor or your Singleton or your Adapter is there taking up space and diluting your ideas.

At a finer level, this is why iteration—especially when we maintain our own index variables—is weaker code than mapping and folding. Yellow code like ++i; is sprayed all over our problem logic.

When the green is all in once place, we can look at it and verify it and think about it with our Inherent Difficulty hat on. And by segregating all of the yellow code into its own place, we can look at it and verify it and think about it with our Accidental Difficulty or Implementation hat on.

Further to that, my experience is that our green code sometimes changes in really fundamental ways. Requirements change, our understanding changes, and we must make drastic changes to the fundamental logic behind our programs. When the green is in one place, you change it and—cross your fingers—you’re done.

The yellow code is as obstinate and coupled and messy as ever, but you don’t touch it. When your code is intermingles, the slightest change to your program logic breaks all of your infrastructure and patterns in unpredictable ways. Coupling in yellow code is bad, but coupling yellow code to green code is hideous.

That's the appeal of Domain-Specific Languages and Metaprogramming. When done right, there's no less implementation or accidentally difficult code, but it’s sharply separated from the code that solves the inherent difficulties of our problem.

So, does size matter?

I’m very comfortable with the idea that some languages enable more green and less yellow through sheer economy of expression. But I’m also comfortable with the idea that other languages may offer more green and less yellow or more separation of green and yellow through different mechanisms.

If two programs do much the same thing and one is smaller than the other, I’m not going to jump right up and say that the former is necessarily better or that the former’s language/framework/libraries are necessarily better.

At the moment, I want to look at them both and start colouring. If the longer program has more green code, or has it all in one place, or if the shorter program is marred by red… I’m inclined to say the short one isn’t the best we can do.

So… if expressiveness is measured by economy of expression, I don’t think it is always better. And should we pursue economy at the expense of separating the solution to inherent and accidental difficulties, I think we make our code less worse, not better.

Comments on “Economizing can be penny-wise and pound foolish:
The trick with so-called "red" code is that code that you don't understand isn't necessarily bad code. Anyone who comes into a new shop and begins introducing new patterns and (maybe better) abstractions can often get marked up by the old hands with a sea of red.

Just because you don't understand something doesn't mean you can't understand it, or that it is bad. Originally I would have marked 90% of my current Ocaml code red. Now that I understand functional programming more, I find that it's all quite green, with smidgens of yellow here and there to baby the type inferencer.

Yes, thanks! I do want to write something about red code. This has come up over and over again: see Irony--I think we are in violent agreement.

But you know, if there are two ways to write a thing, and one way introduces some short red code and another introduces page after page of yellow code, it is tempting to say "go with the yellow pages: we can understand every line."

But behold, the yellow pages are unmaintainable because there is so much of the stuff and it is all smeared together with so many interdependencies that you can't make any changes.

So sometimes we have to change so we understand the red code.
Having just been inside some new (to me) bits of Rails, Reinier's comments regarding provably linear-time chasability and yours regarding the yellow under the covers resonate quite strongly.

Good thoughts all. Keep them coming.
Very interesting paradigm. I'm sure the green / yellow / red code analogy will be used extensively once I force my colleagues to read this ;). Kudos for that.
martoo asked: In what way is use of the strategy pattern a case of accidental implementation complexity?
Just checking mail and somehow I end up here...

Reg: Congrats. This is the firstt time anyone has ever seriously tempted me into reconsidering my opinion that machine analysis always beats expressiveness. (read: java beats python/js/ruby).

I'd like to make two points now, and blog more later (on vacation, typing on a TV):

- leaky abstractions. If in java something is either buggy or not flexible enough I can very easily boil down to raw unmarred java, which is 100% grokkable. green code is clean but may not work.

- I've written plenty of green-enabling logistics in java. Your average java library is a piece of horse manure, though. A popular language tends to build up cruft.

I will also be mailing this around, though.

The seventh circle of hell must have already placed their order for a new Zamboni: of late, I have ben working my way through The Little MLer and delighting in Ocaml's mix of strong typing and higher-order functional programming.

Anyways, I have also written a lot of abstraction mechanisms in Java. Although some tools make this kind of thing easier than others, I can think of no technical reason why these ideas would not apply to Java.
Yes, ocaml (and my personal fave, Haskell) are delicious, but don't quite seem to work as seamlessly as I want them to in real life projects.

The more I think about it, the more I feel GYR analysis of code (Green/Yellow/Red) is the natural enemy of leaky abstractions.

Seas of green are enabled only by vast tracts of abstractions.

And thus that Zamboni might have to be cancelled: FIRST ensure that all abstractions are easy to break down, THEN enable green code. I feel Java is, as far as main stream languages go, far more on track compared to Ruby, Python, JS, and LISP.

It's just a lesson java programmers need to start taking to heart. Soon.
I like the red-yellow-green concept. It puts into words something which I've long felt was important, but have never been able to define.

Do you think it matters a lot whether the "magic" behind the scenes (in Rails etc) is yellow or red? I ask because I'm currently working on a framework which, to borrow your term, enables seas of green. However, I'm concerned that the framework itself will look too "red".
I think you should clarify that your red/yellow/green classification (which I very much like) only applies to languages you know modestly well.

Some languages are very green once you know a bit about them, but they are a sea of red before that.

Also, while the syntactic sugar for functional programming is a productivity god-send, the real power is, as you said, the ability to write domain-specific (sub-)languages in your language. It's why the LISPers rave about their macros. But Ruby does a decent job there, too.

And I think your green/yellow separation really boils down to that: can I write effective DSLs (the yellow) with a given language. If I can, my problem-solving code is very green.

I think it is related to expressiveness, but not merely in a code size sense. Ultimately, it's the power of abstraction that the language offers you. Or how much of the code in a project could be generalized and consolidated ("pushed into a library") if not for language constraints?

A possible hierarchy: bad programmers seem to write code as giant if-else/switch-case statements; mediocre programmers abstract and write subroutines; good programmers further abstract and create generic algorithms and objects; great programmers do a little more abstraction and create novel languages to concisely express their intention?
Reinier Zwitserloot:

We seem to have some overlapping ideas, but oddly I consider Java to be one of (if not the most) limited languages in current use.

Functions as first-class objects would help immensely, but I could build far more expressive and productive environments with mere C code.

I don't see how Java is anything more than an overly verbose VB, but I would certainly like to hear arguments to the contrary.
Or in metaphor:

Why is most of the world writing code by pressing sticks in wet clay when we have, at least, ink and papyrus, if not the damn printing press already.
I love this idea. The part that I really like about the idea is that it's flexible.

- I can use it to get a grip on the code that I just inherited from some one else.
- I can use it to understand what parts of my code need more comments or need to be rewritten as the rest of my team does not understand them.
- It shows what can be learned, and what is not worth struggling to learn.

This is something that can be done by an entire team just as well as it can be something that I just do. Thanks so much for this post.

I know, I'm supposed to write green colored code...it's just that writing yellow code is so much more seductive!

<< Home
Reg Braithwaite

Recent Writing
Homoiconic Technical Writing / raganwald.posterous.com

What I‘ve Learned From Failure / Kestrels, Quirky Birds, and Hopeless Egocentricity

rewrite_rails / andand / unfold.rb / string_to_proc.rb / dsl_and_let.rb / comprehension.rb / lazy_lists.rb

IS-STRICTLY-EQUIVALENT-TO-A / Spaghetti-Western Coding / Golf is a good program spoiled / Programming conventions as signals / Not all functions should be object methods

The Not So Big Software Design / Writing programs for people to read / Why Why Functional Programming Matters Matters / But Y would I want to do a thing like this?

The single most important thing you must do to improve your programming career / The Naïve Approach to Hiring People / No Disrespect / Take control of your interview / Three tips for getting a job through a recruiter / My favourite interview question

Exception Handling in Software Development / What if powerful languages and idioms only work for small teams? / Bricks / Which theory fits the evidence? / Still failing, still learning / What I’ve learned from failure

The unary ampersand in Ruby / (1..100).inject(&:+) / The challenge of teaching yourself a programming language / The significance of the meta-circular interpreter / Block-Structured Javascript / Haskell, Ruby and Infinity / Closures and Higher-Order Functions

Why Apple is more expensive than Amazon / Why we are the biggest obstacles to our own growth / Is software the documentation of business process mistakes? / We have lost control of the apparatus / What I’ve Learned From Sales I, II, III

The Narcissism of Small Code Differences / Billy Martin’s Technique for Managing his Manager / Three stories about The Tao / Programming Language Stories / Why You Need a Degree to Work For BigCo

06/04 / 07/04 / 08/04 / 09/04 / 10/04 / 11/04 / 12/04 / 01/05 / 02/05 / 03/05 / 04/05 / 06/05 / 07/05 / 08/05 / 09/05 / 10/05 / 11/05 / 01/06 / 02/06 / 03/06 / 04/06 / 05/06 / 06/06 / 07/06 / 08/06 / 09/06 / 10/06 / 11/06 / 12/06 / 01/07 / 02/07 / 03/07 / 04/07 / 05/07 / 06/07 / 07/07 / 08/07 / 09/07 / 10/07 / 11/07 / 12/07 / 01/08 / 02/08 / 03/08 / 04/08 / 05/08 / 06/08 / 07/08 /