Computers Are Hard: building software with David Heinemeier Hansson

Wojtek Borowicz
Computers Are Hard
Published in
14 min readSep 27, 2020

If you were to summarize the entire endeavor of software development, you’d say: ‘The project ran late and it got canceled’.

Illustration showing a person designing an app interface on a whiteboard.
Building software with David Heinemeier Hansson. Illustration by Gabi Krakowska.

We’ve reached the end of Computers Are Hard. After several conversations about how individual components of software come to be — from printer drivers to password hashing — I wanted to wrap up with a look at the philosophy of building software products.

It’s perhaps a little embarrassing, but even after a couple of years in the industry, I never understood why tech companies are so obsessed with speed. And that obsession is baked into the very language of software, where work cycles are called sprints and measure of progress is called velocity. But is it really so fundamental to ship software fast? I don’t know. I don’t build software myself but I troubleshoot it every day and boy, do I sometimes wish engineers worked a little slower.

I brought my questions about the methodology of building software to someone who’s had his share of heated debates about the topic. David Heinemeier Hansson created Ruby on Rails, is a co-founder and CTO of Basecamp, and an author of business books such as Rework. He also has a reputation for speaking against the industry trends without mincing words, whether these trends are technical, like the rising popularity of microservices, or institutional, like venture capital becoming the default way of growing a business in tech. We talked about how software is built today, what does it mean for the people doing the building, and how it could be built instead.

Enjoy the final chapter of Computers Are Hard.

Wojtek Borowicz: Software methodology is an industry of its own. There is Scrum, and Agile, and coaches, and books, and all of that. But you and your team at Basecamp don’t follow these practices. Why?

DHH: First of all, our approach to software development is heavily inspired by the Agile Manifesto and the Agile values. It is not so much inspired by the Agile practices as they exist today.

A lot of Agile software methodologies focus on areas of product development that are not where the hard bits lie. They are so much about the procedural structures. Software, in most cases, is inherently unpredictable, unknowable, and unshaped. It’s almost like a gas. It can fit into all sorts of different openings from the same basic idea. The notion of trying to estimate how long a feature is going to take doesn’t work because you don’t know what you’re building and because humans are terrible at estimating anything. The history of software development is one of late or cancelled projects. If you were to summarize the entire endeavor of software development, you’d say: ‘The project ran late and it got canceled’. Planning work doesn’t work, so to speak.

What we do at Basecamp we chose to label Shape Up, simply because that is where we find the hard work to be. We’re trying to just accept the core constraint that it is impossible to accurately specify what software should do up front. You can only discover what software should do within constraints. But it’s not like we follow the idea that it’s done when it’s done, either. That’s an absolute abdication of product management thinking. What we say instead is: don’t do estimates, do budgets. The core of Shape Up is about budgets. Not how long is something going to take but what is something worth. Because something could take a week or four months. What is it worth?

Something could be worth the whole cycle of work — that’s usually the limit — and for us this is six weeks. That’s what we call a big batch. Or it could be worth less than that. Maybe only a week, maybe it’s two weeks. That’s a small batch. That takes the fuzzy project statement of ‘let’s add Feature A’, puts it under a constraint, and delegates figuring out implementation to the people doing the work. That’s the key insight here. If you have a big problem definition and a fixed boundary and you give creative, intelligent people the freedom to come up with a solution within those terms, they will do wonderful work they are very proud of.

AGILE MANIFESTO

In 2001 a group of 17 men (and, yes, zero women) published a document that set the course for the next 20 years of software development. Their Agile Manifesto codified the principles of agile software development. The ideas were basic (for example: ‘Working software is the primary measure of progress’ and ‘Simplicity is essential’) but they spawned an entire universe of Agile coaches, consultants, and authors.

So the problem with those methodologies is they put too much focus on estimating, which is inherently impossible with software?

I’d go even further and say that estimation is bullshit. It’s so imprecise as to be useless, even when you’re dealing with fixed inputs. And you’re not. No one is ever able to accurately describe what a piece of software should do before they see the piece of software. This idea that we can preemptively describe what something should do before we start working on it is bunk. Agile was sort of onto this idea that you need running software to get feedback but the modern implementations of Agile are not embracing the lesson they themselves taught.

But technology is almost religiously obsessed with speed. How does that work if you want to focus on speed but can’t trust the estimations?

We’re talking about progress and speed. Those are actually two different things. You can try to do things faster and faster and realize you’re actually not going any further. The interest for us in Shape Up is to go far. It’s to end up with projects that deliver meaningful, large batches of work that customers and implementers are proud of and happy with. And that is not improved by trying to shrink feedback loops to be impossibly small. There’s this idea that constant feedback is a good thing. Yes, within some reason! I don’t want to be constantly evaluated on what I do. For example, we don’t do sprints. Rejigging the work every two weeks as Scrum and other methodologies dictate is a completely oppressive and churning way of work that just exhausts everyone and doesn’t actually deliver anything. Most people can’t deliver real, big features in two weeks.

The magic really is in shifting your mindset from estimates to budgets. Don’t think about how long something takes. Think about how long are you willing to give something. This flips the entire idea. It lets the requirements float. The project definition that is vague is actually more realistic. Highly specific project definitions usually go astray very quickly. Vague enough definitions allow for creativity and selectivity for the people doing the work. And when you allow for those two things, you empower these people with the agency to do the best work they think they can do, not just follow the spec.

The whole Agile rebellion was about rejecting big, upfront design. But I think Agile didn’t take that conclusion far enough. It thought: ‘we don’t want big upfront design. We just want little upfront design’. That’s not really that much better. A lot of software methodology is myopically focused on the technical requirements of implementation. But the hard work in software is figuring out what it should do, not how to make it work. There is a mythology of the 10x programmer. But that’s not the programmer who is heroic in their implementation of the problem. The 10x programmer is the programmer who restates the problem.

The problem re-statement really should be at the forefront of software methodology.

When teams are tied to the two-week cycles, estimations, and working to spec, do the quality of code and decision-making suffer?

Certainly. But even more than that. It results in — and I mean it in a little bit of a glib way but not that glib — human suffering. People who work under such regimes are simply eaten up and spat out. To be constantly reevaluating everything you’re doing every two weeks because that’s when the new sprint cycle starts — it’s going really fast, nowhere.

This is why we don’t do daily standups. This constant churn, just spinning around in a circle on a very tight leash. I think it’s actually dehumanizing. Again, Agile said: ‘Hey, you know what? That two-year software project you’re trying to plan upfront? That’s a completely bunk idea. It’s way too far out’. And yeah, absolutely! But then, Agile methodology as it’s practiced lately, overcorrected and went way too short. It says two weeks with the daily standups is this magic cycle. No. People need some slack. Some autonomy. Some space.

Through extensive experimentation we’ve found that about six weeks gives us enough room to breathe and to think. And as long as you’ve set boundaries, you end up making progress even if the day to day doesn’t necessarily look like it. You may see more activity on a team that is very tightly leashed in terms of the feedback loops. They’re frantically churning but don’t end up making more progress. Sometimes, the teams that go the fastest are the teams that look very calm. They are not constantly on some methodology treadmill or on some procedural clock that goes ding! ding! ding! every five seconds.

Four smiley people are pointing at a computer during a work meeting.
You can tell this is a stock photo because no one is that enthusiastic during a standup meeting. Photo by Jud Mackrill.

Do you believe your Shape Up approach could work at a much larger organization? One that has 500 or 5,000 instead of 50 engineers?

First of all, you shouldn’t be thinking about software methodologies in terms of being able to scale from five people to five thousand. Trying to plan work for 5,000 people as one unit is a fool’s errand and no one actually does that anyway. The more interesting approach is: what is the appropriate team limit in general, for any company? Large company of 500 is, let’s say, a hundred teams of five. That’s how you make the comparison.

It’s not to say that six weeks is some sort of a magic number that can work for everyone. I think it can actually work in a surprisingly large number of instances, far more often than the two weeks approach. It’s far more generous. And far more realistic that you can ship whole things. But if you cannot ship complete features in six weeks, your feedback loop is still too short. If I can’t make the whole thing happen, from inception, to implementation, to shipping, within the time frame I had for my cycle, my cycle is too short.

If you work in native app development, which has a balls-up reputation, maybe six weeks is not enough time. Maybe you need a little more. Or maybe not. It depends on whether you can ship the things you want to ship. But the industry at large has already gelled around thinking ‘oh, two weeks is a great time frame’. What? How did we get to be so sure? Even for a 5,000 people company, six weeks is a more realistic starting point.

You have also spoken in less than favorable terms about other trends that have emerged in software development, like microservices and serverless or Test-Driven Development. Are there any trends in software engineering that you actually find appealing?

That’s a tough question. It’s much easier to pick out all the shit that I don’t like.

Obviously, I’m a little biased because I am pushing those things. I’m implementing Ruby on Rails and Shape Up and I’m sharing the things that I think are how you should do software development. That doesn’t mean there aren’t other ways of doing it. There are all sorts of technology stacks and approaches to web development that give me a smile. I’m very happy to see the advances we’ve made in JavaScript development. The level-ups that have happened in front-end have been great at the atomic level. I think where we’ve gone astray is the sort of molecule level. The frameworks and the approaches people take to front-end are not great but I think the underpinning technical improvements are with transpilers, polyfilling, and core progress on JavaScript over the versions.

A lot of what we’re doing with the web is working with fundamentals that are 25 years old. The core innovations that happened are more about recognizing where to put the emphasis, what’s important, what to push for. For example, the web industry approach that front-end development should go through JSON, where the server side is just responsible for generating an API and then the API returns a JSON, is a bad detour. We should go back to embracing HTML. Have HTML at the center of what we do, send HTML over the wire so we’re returning fully formed web documents on the first load, and then do subsequent hydration or updates through HTML as well.

I wanted to go back to microservices. One of the engineers I spoke to earlier talked about them as a response to the monolithic software becoming too complex to maintain. What’s your beef with microservices?

Let’s start with the premise. Monolithic software becoming so complicated… what is this ‘becoming’? Is this just happening to us? Are we completely innocent bystanders? The complexity is just rolling over us and there’s nothing we can do? That is a bullshit assumption that we need to refute. Complete and utter nonsense. You don’t have to let complexity roll over you. You choose to. And once you’ve chosen to be flooded with complexity, then a natural response is to try shove that complexity into more different boxes because you just can’t handle it all at once, right? Wrong! Just deal with the fucking thing in the first place. Why are things so complicated? Do they have to be so complicated? Could they not be so complicated? In my opinion, the answers are: yeah, they don’t have to be so complicated. Yeah, we can do something about it. No, it doesn’t mean we have to just submit.

I want to tackle the root cause here. Web development in large terms should be simpler than it ever was. We’ve made tremendous steps forward in compressing conceptual overhead from a large number of domains that used to be very complicated and people needed to think very carefully about. That people somehow still end up with monolithic applications overwhelming them with complexity is a beast of their own making. Rather than try to think what can we do, the response is: ‘where can we put the complexity?’ How about we center the discussion around why we have the complexity in the first place?

Developers talk about accidental and inherent complexity. Accidental complexity is in the implementation, inherent complexity is the complexity of the domain in which we’re working. Inherent complexity in most web applications is the same it ever was. Where we regressed is in introducing a tremendous amount of accidental complexity. If you are unable to contain the complexity of a monolithic application, what on earth makes you think you are competent to distribute that complexity across a fleet of services that now have to interact and deal with network unreliability, retries, two-phase commits, and all these other complexities that simply do not exist when you deal with method calls, parameters, and the basics of running a singular process. There’s very little worse harm you can do to an application on the complexity level than distribute it. You can take even the smallest problem and as soon as you distribute it, it grows an order of magnitude in complexity.

Other industries and even politicians look up to tech as a source of innovation. Meanwhile, I hear developers more and more often say that the entire field is fundamentally broken…

No, no, no. This comes from a misconception that most software development is engineering. I don’t believe it is. When you look at software development through the eyes of an engineer, yeah, things look broken. And then the engineer would go like: ‘Well, your specs are really loose. Your tolerances are undefined’. All these things, all the engineering assessment, blah, blah, blah. And it’s a fundamental misunderstanding of what software development is. Software development is not engineering in the same sense as building a bridge. They’re not just a different branch of the same root discipline.

This whole self-loathing a lot of software engineers engage in is entirely unproductive and is never going to be resolved. The idea that software development is a young industry and if we just give it another 30 years of ISO compliance or whatever rigor, we’re going to arrive at a romanticized notion of engineering they have in aerospace, or elevators, or bridges… no, we’re not. This is a fundamentally different domain that requires a fundamentally different approach.

We already have many of the answers. We’re simply afraid of embracing them. For example, in traditional engineering estimates are a huge part. Things run on estimates and on critical path diagrams because that’s simply the way you build a skyscraper. You don’t get to reconfigure how the pylons go after you pour the concrete. Software development is nothing like that. Software in many ways is far closer to the creative process of writing, game making, movies. Experiences where you design the unknown and you don’t know whether it’s good or not until you see it.

Look at movie making. We’ve been making movies for a hundred years. Haven’t we figured out the creative process yet? No! We haven’t. You can take a great director, a great cast, and still make a totally shitty movie. Versus in building, largely speaking, if you take a great architect, a great engineering firm, and a great general contractor, you’re gonna arrive at a building that works. You may make minor mistakes but the basic structure is going to be sound, unless someone makes a completely negligent error. In movie making, in music, in software things fail all the time. Even when good people who know the techniques of how to build things get together and work on something, they still end up failing.

One more thing engineers feel strongly about is their choice of programming language. Is there such a thing as a good and a bad language?

Yes, for a person. A programming language can be better or worse for an individual. I think they can also be better or worse on an objective level, but that discussion is almost uninteresting. The interesting discussion for me is one of personal truth.

For example, one of the long-running debates about what makes a programming language good or not is whether you should use static or dynamic typing or strongly or weakly typed languages. In Ruby you don’t have a statically typed language, and there’s a certain class of refactorings or mistakes that approach does not do well with. On the other hand you have something like Java, just to take a standard example of the strongest-typed language, that works in a different way. For different people, with different brains, different languages either speak to them or they don’t. It’s similar when you look at learning styles. Some people learn visually, some people learn audibly, and these styles are absolutely right or wrong for the individual. If you are a visual learner, trying to learn in an audible or a tactile way just doesn’t work for you. For me, Ruby is a far superior programming language to any other I ever tried because it fits my brain like a glove.

We should look and embrace the personal truths that arise from different brains but we shouldn’t shy away from people with different brain types arguing about what is better and what is worse. There is tremendous value in the clash of opinions. Even if you have one person, like me, who says Ruby is the greatest language ever and another person says Java is the greatest language ever. These are things we’re supposed to embrace. It’s like atoms hitting each other. Then we get light, we get energy, we get excitement. And that’s good! Engineers are so fucking conflict-shy that they can’t take two people disagreeing without backing off and going ‘Trade offs! Trade offs! It depends!’. It’s like crying uncle, which I think is a completely counterproductive way to learning, to inspiration, to anything.

When I debate software development and my choices and opinions, I do it with the full force of conviction about what’s right for me. And the spectators can decide who they’re more like. They can try for themselves. They can see whether the arguments I put forward about my romantic affair with Ruby resonate with them. And if they don’t? Who gives a hoot!

TYPE CHECKING

Data is classified into types and each programming language has its own rules for what you can do with which type. Type checking means enforcing those rules, so that the program knows, for example, whether the value 30 you assigned to a variable should be treated as a number or a string of two characters. Languages are statically or dynamically typed depending on when the type check occurs and strongly or weakly typed depending on how it’s done.

--

--