Understandable Code

Above all else, our software must be correct. Since correctness can only be measured at a single point in time, we need our software to be changeable in order to maintain correctness through time. In order to change one part of the system without breaking anything else we need to be able to understand our software and predict the impact our changes will have. The next obvious question is: how do we write software that is easy to understand?

The first problem is that you can’t tell how easy it is to understand your software because you always understand the software you’re currently writing. It’s not possible for you to imagine how difficult it would be for someone who has never seen your code before to understand it. Even a big tangly mess seems perfectly reasonable while you’re working on it because you understand it at that moment. If you’ve ever been whining about “who wrote this garbage piece of code” only to open git blame and discover it was you, six months ago, you’ve experienced how it’s possible to write code that even you will have a hard time understanding later on.

The feedback cycle is just too long if we have to wait until we forget about something and have to relearn it. By that point it will be too late and by the time we understand it again, we won’t be able to judge whether our changes or rewrites are easier to understand. We need immediate feedback, which we can get by writing isolated unit tests:

The benefit, the real benefit, of isolated tests for one function at a time is that those tests put tremendous pressure on our designs. Those tests are the ones that make it the most clear where our design problems are. Remember that the whole point of test-driven development is not to do testing.

One of the goals of test-driven development is to learn about the quality of our design. We use the theory that if our design has problems then the tests will be hard to write—the tests will be hard to understand. It will be difficult to write these small isolated tests to check one thing at a time. So we rely on these tiny isolated tests to give us feedback about our design and to identify the problems.

J.B. Rainsberger, Integrated Tests Are A Scam

It turns out that everything that makes code difficult to understand also makes code difficult to write isolated tests for. That’s very useful because the habit of test-driven development can provide the immediate feedback we need to tell us how easy our code is to understand.

Writing tests can be used as a proxy for learning about code because writing tests is, in a way, teaching the computer about how your code works. If the tests are easy to write, then it was easy for the computer to learn to understand your code. If the tests were difficult or complicated to write then the computer had a difficult time understanding how your code works. If the computer has a difficult time understanding your code, then other people are going to have a hard time as well. And other people includes you-six-months-from-now.

Most of the people I work with eventually come to the realization that when you know how to design software that is free of interleaving that you don’t feel the need to write unit tests to make sure that it works. And that’s true. This is why I rank Testable as the fourth priority. Once you know how, it’s easy to write very high quality code without writing any tests for it. The reason I still write tests for my code is because of the short feedback cycles. JBrains is right—without the constant pressure you get from writing isolated tests for all your code it’s too easy to get just a little bit lazy. And maybe these three things are a little tangly and messy, but it doesn’t really seem that bad so I let myself get away with it. Test-Driven Design is a discipline. You have to do it all the time because you’ll still always think the code you just wrote is easy to understand no matter how good you get.

Scott Adams says that even after decades of writing Dilbert comics, he still can’t predict with a high certainty how much people will enjoy a new comic strip. He’s been surprised that some of his all time most popular strips have been ideas that he almost didn’t even bother drawing because he didn’t think they were funny. And plenty of his favorite strips were gigantic flops.

When you’re too close to something you’ll never be able to evaluate it from the perspective of an outsider. You have to approach your work with the discipline that you’re going to iterate with fast feedback cycles. The only way to get that fast feedback on your software is to write the isolated tests.

No One is Smart Enough

There’s a third aspect of being easy that I don’t think we think enough about, which is being near to our capabilities. And due to a combination of hubris and insecurity we never really talk about whether something is outside of our capabilities. It ends up that it’s not so embarrassing after all because we don’t have tremendously divergent abilities in that area.

Rich Hickey, Simple Made Easy [00:07:10]
Continue reading “No One is Smart Enough”


From Stanford CS Professsor John Ousterhout:

Today’s thought for the weekend is: the most important component of evolution is death. So I want to address that first at a biological level and then let’s pop up and talk about it at a societal level, Silicon Valley, and computer software. So, first, from an underlying biological standpoint, it’s sort of fundamental that for some reason it’s really hard for an existing organism to change in fundamental ways. How many of you have been able to grow a third leg? Most people can’t even change their mind let alone change something fundamental about themselves.

People try. You make your hair look a different color, but it’s really the same color underneath. In fact you have this whole thing called your autoimmune system whose goal is basically to prevent change. You’ve got these white blood cells running around looking for anything that looks different or the slightest bit unfamiliar and as soon as they find it they kill it. So it’s very hard for any organism to change itself, but when we make new organisms it’s actually fairly easy to make them different from the old ones. So for example gene mutations seem to happen pretty commonly. They can be good or bad, but they do change the system. Or, with sexual reproduction, it’s even easier because you take genes from two parents and you mix and match them and who knows you’re going to end up with as a result.

So the bottom line is it’s a lot easier to build a new organism than it is to change an existing one. And, in order for that to work, you have to get rid of all the existing ones. So death is really fundamental. If it wasn’t for death there’d be no way to bring in new organisms and create change.

I would argue this same concept applies at a societal level. In fact, if you look at social structures, any structure that’s been around a really long time, it’s almost certainly out of date. Because, they just can’t change. Human organizations, companies, political systems, religions, they all have tremendous difficulty changing.

So, let me talk about companies in particular. We’re hearing these days about various companies in trouble. Is Yahoo going to make it? And Kodak filing for Chapter 11. People seem to think: those guys must have been bozos. They blew it. How could you fumble the internet when you’re Yahoo?

My opinion is this is just the circle of life. That’s all. That fundamentally companies can’t change. You come in with a particular technology, something you do very well, but if the underlying technology changes, companies can’t adapt. So they actually need to die.

I view this as a good thing. This is the way we clear out the old and make room for the new. And in Silicon Valley everyone kind of accepts that. The death of a company is not a big deal. In fact, what’s interesting is that the death of the company isn’t necessarily bad for the people at all. They just go on to the next company.

And I was talking to a venture capitalist once and she told me she actually liked funding entrepreneurs who had been in failed startups because they were really hungry yet still had experience. People in successful startups weren’t as hungry and didn’t succeed as often when they got funded. So death is actually a good thing in Silicon Valley.

Now let’s talk about computer software. This is kind of ironic because software is a very malleable medium, very easy to change. Much easier to change than most things. And I actually consider that to be a problem because, in fact, people don’t have to throw it away and start again.

Software lives on and on and on. You know we’re still working on versions of Windows 20 years old right now. And as a result the software gets messier and kludgier and harder and harder to change. And yet people keep struggling. They won’t throw it away and start again.

And so what’s funny is that this incredibly malleable medium gets to a point where we can’t make fundamental changes in it because people aren’t willing to throw it away and start again. I sometimes think the world would be a better place if somehow there could be a time limit on software where it expired. You had to throw it away and start again.

So this is actually one of the things I like about California and Silicon Valley. It’s that we have a culture where people like change and aren’t afraid of that. And we’re not afraid of the death of an idea or a company because it means that something even new and better is coming along next.

So that’s my thought for the weekend…

John Ousterhout, Favorite Sayings

From Wunderlist CTO, Chad Fowler:

One of the things about a tiny method or a tiny micro-service is that it allows you to consider replacing it. You can change it, you can understand it really quickly, but even if you can’t—if it’s small enough, and it’s named well enough, and it’s decoupled enough, you can just throw it away and restart it if you have to.

You have a system and you need to keep it in a consistent state, an understood state, and it runs for a long time. I remember having systems that were up for two years and being really proud of that. Now I realize, though, that’s terrifying. If a system is up for two years it’s very difficult to know how to recreate it and what the state might be.

What we figured out is that you should just throw away the systems. That’s the best way to do it. Don’t change the systems. Because then you get rid of all of the issues of these weird unchangeable scary things. Throw them away and start over.

So we established this immutable infrastructure system where if you wanted to upgrade anything on a given server, then you would throw away the entire thing and just rebuild a new one and replace it. The beautiful thing there is you know exactly what’s there, you know you can recreate it, you’re doing it all the time. The thing is changing, which proves its ability to change and you have a system that is able to change.

The more radical thing was, how do you change your software? Well, maybe you should just plan to throw it away. This is where I start talking about the code fetish thing. At Microsoft, we have code that’s twenty years old and it’s reusable, it’s shared all over the place. People both love it and feel tied to it. If you’ve worked at a big company you probably know this thing where you have these reusable libraries and you’re kinda stuck to what they can do and they follow you around.

We started saying let’s just plan to throw our software away. So when we write it, there’s a little less stress. We can also try out new things because there’s less stress about trying out new things. And not only can we throw away the pieces of code, but we should.

If you need to be able to change your software and you do it all the time and you plan to do it then it’s easy to throw stuff away.

If I need to be able to change an entire app system, if I declare that you cannot change anything inside a component, you can only replace it, then it makes the entire thing easier to understand and easier to actually change.

We rewrote all these pieces of code, we didn’t rewrite the system. Within a year we rewrote 80% of the code and there was no project where someone said I’m now going to work on rewriting a piece of code, there was nothing in a backlog, it was just maintenance work. While I do this feature change, I’m just going to replace the whole thing. Why do I do that? Because it’s healthy to be able to change code so I’m going to prove I can.

Chad Fowler, Impermanence: The Ironic Key To Systems That Survive.

Nonlinearity, Convexity and Concavity

Many of the problems we encounter when trying to design systems that are maintainable long term are complications of combinatorial explosions. Combinatorial problems are a subset of the problems associated with Nonlinearity, which are the subject of Nassim Taleb’s outstanding book, Antifragile.

If I throw at someone’s head a ten-pound stone, it will cause more than twice the harm of a five-pound stone, more than five times the harm of a two-pound stone, etc.

Your car is fragile. If you drive it into the wall at 50 miles per hour, it will cause more damage than if you drive it into the same wall ten times at 5 mph.

Drinking seven bottles of wine in one sitting, then purified water with lemon twist for the remaining six days is more harmful than drinking one bottle of wine a day for seven days (spread out in two glasses per meal). Every additional glass of wine harms you more than the preceding one.

Letting a porcelain cup drop on the floor from a height of one foot is worse than twelve times the damage from a drop of a height of one inch.

Jumping from a height of thirty feet brings more than ten times the harm of jumping from a height of three feet—actually, thirty feet seems to be the cutoff point for death from free fall.

Fragility is generally non-linear, that was the intuition from the porcelain cup.

Nassim Taleb, Antifragile [268-269]

With Linear effects, if you double the input you get exactly double the output. Non-linear effects you get either a lot less or a lot more than twice the output.

Taleb describes non-linear behavior as either convex or concave.

Convexity: twice the input gets you way more than twice the output

Concavity: twice the input gets you way less than twice the output.

Exponentials and factorials are convex, they go up faster and faster. The output of 100! is not ten times greater than the output of 10!, its an absolutely insane 10^151times greater.

Square-root and logarithms are concave, they go up slower and slower.

Using Taleb’s jumping analogy: does height have linear or non-linear effects on the harm from jumping? If the harm is the same, height has a linear effect. If the harm from the high fall is greater, height has a convex effect. If the harm from many short falls is greater, height has a concave effect.

Does jumping one time from a height of thirty feet will do the same damage as jumping thirty times from a height of one foot? The answer is intuitively, no. The first one kills you and the second one does no damage at all—convexity at its finest.

But how can that be since mathematically you will have experienced the same cumulative dosage of “feet fallen”? Thirty “feet fallen” delivered in a single fall delivers the effects combined, where as the same thirty “feet fallen” spread across thirty falls sees those effects isolated.

The most important thing to understand about convexity is that it means that each additional input causes a greater increase in the output. Hockey stick curves are deceiving like that because the beginning of the curve is so flat that it’s easy to mistake the behavior for linear. It seems like adding more of something harmful isn’t doing much damage. Once you hit the knee of the curve, the growth in harm shoots up so fast that you go from “no problem” to “dead in the water” faster than you realize what’s going on.

This makes convexity effects very tricky because it means you can get away with doing the wrong thing for a very long time and by the time the harm increases enough for you to notice it’s usually too late to do anything about it. Even after 400 pages, Taleb’s only proposed remedy for the problems of convexity are to avoid getting on the wrong side of it, and try to use it to your advantage if you can.

Designing A Training Curriculum

If you get good enough at doing something valuable, at some point they’re going to reward you by not letting you do that thing anymore. They’ll ask you to spend your time teaching other people to be as good as you are. You will mourn the loss of what you enjoyed doing, but eventually you’ll start to see that a group of people you trained can get way more done than you ever could.

Continue reading “Designing A Training Curriculum”