Two recurring phrases in my work are 1) It is like this because we built it to be like this. 2) The code works for you, you don’t work for the code.
Two sides of one page, phrased on the front as negative critique, and on the verso as positive encouragement.
Before we dig in, I remind you of the relative unimportance of geekery to me just now. This is just respite. Please work for change and support the others who are doing so.
Stay safe, stay strong, stay angry, stay kind. Black Lives Matter.
Humans make programs by manipulating code over time. The code’s state, right now, is the direct result of the action of its humans. The code’s state, tomorrow, will be the direct result of the action of its humans.
It’s about agency, plasticity, & temporality.
Agency: the software itself is an artifact.
It has no intelligence, no will, and no feelings. It’s a thing. By contrast, the humans making that artifact have intelligence, will, and feelings, in spades. The humans are the agents here, they’re in charge, not the software.
Agency means that every aspect of our software, its look, its feel, its packaging, its performance, its structure, its text, its size, its changeability, its testability, every aspect of our software, was put into it by us, and can be altered by us.
Plasticity: for any software problem we encounter, there are uncountably many arrangements of code that will solve it.
Software is extraordinarily malleable, pliable, responsive, moldable, alterable, moreso than almost any other medium.
Turing established via theory that there are an infinite number of Turing machines that produce identical results. In practice, plain ol’ street geeks establish it almost every time they inspect someone else’s code.
Don’t look at me like you don’t know what I mean. 🙂
Temporality: virtually all modern programs are built across significant stretches of time.
They have a past embedded in them, but it’s not the past of the code repo, it’s the past of all the human decisions that shaped them.
We got our artifact to its current state by taking many actions, in steps. Whatever we want that state to become, it almost certainly will become only by us taking many actions, in steps. Colloquially? "We didn’t dig the hole all at once, we won’t get out of it all at once."
Why even bring all this up? As a coach, for two decades, I’ve worked with lots of teams on lots of projects in lots of domains. Much of that work has, of course, involved working with code. Much of that code has been, not to put too fine a point on it, simply awful to work with.
Notice, I’m talking here about usability of the code, where the users are programmers. The code runs, more or less, and does the thing, more or less, that’s not the point. The point is that the code makes working with the code a remarkably unpleasant experience.
And that very low level of usability in the code? That’s not the bad part.
The bad part, the single most striking feature of my experience as a coach, isn’t that the code sucks, and thus makes the programming suck: It’s that the programmers think it has to suck.
You can call it fatalism, or learned helplessness. You can blame it on lousy training, absent mentoring, 19th century management theory, successful marketing by confidence tricksters, the fundamental nature of capitalism.
The fact is, a remarkable number of programmers actively dislike working with their own artifact. And they made that artifact. They are making it. And they don’t like doing it. And they don’t believe they can do anything about it.
And the theme, it runs through 90% of my content: they can do something about it. The medium of software has the fewest constraints of any craft I’ve ever seen or heard of. As a programmer, I can make a program be whatever I want it to be.
Let’s concretize. I could cite dozens of examples, and I can pull them not just from code, but from process as well. (As you know, I often use cases from "the code" to draw out cases from "the coding" and "the process" and "the team" and "the org".) Here’s a few, then.
1) Environments that require developers to code, test, and debug using machines and data outside of their own control are commonplace in the trade.
They are nearly all extremely unpleasant to work in.
This is one specific flavor of the nearly universal trade practice of having programmers make a shipping app using only the shipping app in their making. You see it in microservice architectures a lot, but it’s done many times in old-school monolith environments, too.
And why do these rigs require us to run our code on another machine, sometimes on several machines, in data centers that are God knows where, with no decent control facility, no permissions, no developer-friendly interface, and so on? Because we built them so they’d require that.
Could we have built them another way? We could have. That’s plasticity. Should we have built them another way? We should have. That’s agency. How can we make the artifact more to our liking? Step-by-step, that’s temporality.
2) A variation of this occurs when teams first start attempting to use TDD in their existing codebase.
They get fired up, maybe someone went to a lecture, or showed them a kata, or whatever. "Chaaaaarge!!!" For two weeks. Then it’s all Brave Sir Robin: "Retreeeeeat!!"
Why does it happen? It’s TDD’s Steering Premise in reverse. That premise says that tests and testability are first-class citizens in design. But the code these folks are rushing into was not built that way. It is untestable because it was not built to be tested.
It is not necessarily the case that "not built to be tested" means not testable. But it is the case so very often that we devised the Steering Premise precisely to highlight it. If you don’t make it testable, odds are good that it won’t be.
(NB "Untestable" is vernacular. It’s pretty much all testable no matter what, but the game isn’t to prove an arcane theorem in computer science, it is to ship more value faster. "Untestable in affordable practice" is "untestable" to us, because we work for a living.)
The trick here isn’t learning how to test untestable code. It’s learning how to take untestable code and change it in ways that make it testable. But learned helplessness works its magic, and we declare that TDD doesn’t work, and continue to suffer the pain.
3) At a larger scale, a little more subtly, we have the collossally expensive and usually failed Great Rewrite efforts that flicker for years across the face of enterprise organizations.
The first version of a system is still making money, but we foresee that it will not continue to do so unless we keep enhancing it. The problem is that, due to how we built it, changing it gets ever more difficult as we go along.
Eventually, it feels like it costs us more to change the old version than to just make a new one from scratch. We can pick different technologies, a different team and code structure, and so on. The Great Rewrite is on!
When we do a Great Rewrite, we’re reacting to the unchangeability of the old version, to the great difficulty of working with the old code. It is generally a combination of poor factoring, a lack of tests, and dependencies from abstraction onto detail.
We built the unchangeability, putting into place, because we weren’t accepting the reality that our ability to predict the future of a software application across stretches measured in years is almost nil. It is that way because we built it to be that way.
In a Great Rewrite, we change almost everything about the old one, and the reason they fail so often is hidden in that "almost": the thing we don’t change is the level of changeability. Sadly, that’s the thing that got us into that old-version mess in the first place.
Many programmers, way too many, believe that life as a professional programmer sucks. And they believe it has to suck.
In fact, that belief is less true in software development than in any other artifact-making craft I know or have heard of.
We have agency, plasticity, and temporality. We can make it not suck, and we should, and we’ll do it in steps.
1. It’s that way because we built it to be that way.
2. The code works for you, you don’t work for the code.
Two mantras, one a critique and one an encouragement.
Do you love the GeePaw Podcast?
If so, consider a monthly donation to help keep the content flowing. You can also subscribe for free to get weekly posts sent straight to your inbox. And to get more involved in the conversation, jump into the Camerata and start talking to other like-minded Change-Harvesters today.