Basta’s Notes

Share this post

Our pathological inability to do "done"

basta.substack.com

Our pathological inability to do "done"

Getting projects over the line and not accruing project debt

Matt Basta
Jan 23
4
2
Share this post

Our pathological inability to do "done"

basta.substack.com

Over the summer, I discovered that the treadmills in my gym had a solitaire app. I grew up playing solitaire on my dad's Windows 95 computer because I wasn't allowed to play video games without supervision (I had an extremely sheltered childhood), and apparently solitaire didn't count as a video game. I love a good round of solitaire, and this was the best way to distract me from the fact that I was doing cardio.

I got the Microsoft solitaire app for my phone and started playing more often. I was enjoying it as a distraction from my no-good very-bad summer. I get on kicks like this—like the time I invested myself heavily into Sudoku—and became interested in optimum strats.

The thing about solitaire (specifically Klondike), at least as far as I can tell, is that there's not really a way to force a win. Often you'll find yourself with only a few cards left to play, but you have a jack of diamonds instead of a jack of hearts, blocking you from winning. There are so many variables that make solitaire tricky to win, and as far as I can tell, any heuristic will always have games that it fails to win.

One of the tricks to playing a better game of solitaire that has been very difficult for me to adopt has been to wait. I'm the UK, they call Klondike "Patience", and perhaps for good reason. Sometimes you'll find yourself with a move that can be made, but the move results in you losing the game. Let me give you an example:

A game of klondike solitaire showing two aces in the foundations. A nine of clubs is alone in the first pile of the tableau and an eight of diamonds is alone in the second pile of the tableau. An unimportant two of diamonds is visible atop uncovered cards in the third pile of the tableau.
The eight and nine here are what are interesting

I'm going to hand-wave away the rest of the cards and what moves could be played; let's just assume that the eight and the nine is the only thing that obviously looks like a move without drawing from the stock or any of the four foundations

1
.

The obvious move is to place the eight on the nine. Of course, why wouldn't you? If there was a king available in the tableau, this would make sense: you want to uncover as many cards as you can. But let's say there's not a king available. Making the spot the eight is on available only serves to free up a space for a king you might later encounter.

Placing the eight on the nine has a hidden cost: if you encounter the other red eight (the eight of hearts), you have nowhere to put it. If you leave the nine of clubs and eight of diamonds as they are, you keep a valuable spot available. And there's no cost to keeping the eight off the nine in the short term: if you realize you need that spot for a king, you're always free to put your eight on your nine
2
. In programming terms, it’s better to play this move “lazily.”

Psychologically, it's difficult for me (and I imagine, many other people) to *not* play the obvious move. We optimize for the moves we can see, not the moves we haven't seen yet. We know we’ll get the gratification of making a move immediately instead of maybe getting gratification in the future from making a potentially more beneficial move, even though there’s no cost to waiting.


So what the hell does this have to do with engineering? It has a lot to do with how we plan—and in turn—create incentives. Or, how we prevent ourselves from completing work that needs doing.

Imagine you start an engineering migration. You want to, say, move off of your Coffeescript and Backbone front-end stack and switch over to React and Redux or whatever. You start plugging along and make incredible progress. But eventually, you hit the 80/20 problem. Some of the code is unowned. Maybe some of the code is deeply complicated or integrates with a library that's hard to get working on React. Maybe the React component library is missing a component that your old CS code used to have. Maybe someone promises to do the work you asked of them, but then they disappear, never to be heard from again.

At this point, the responsible solution is to burn down the migration with good old engineering practices. Get resources, make a plan for all the loose threads, work through everything until the job is done.

But that's almost never how it goes, is it?

The last few hard parts are hard, and there's other work that's coming up that's pretty important. Everyone at the top of the food chain forgot about the importance of your migration, and they’re worried about other things. Is it really useful to get these last few pieces done? Surely this crusty code will be rewritten soon anyway. We’ve basically finished the project, anyway: we already hit our metrics for the OKRs. How bad could it possibly be to put that eight on the nine?

The org sees an easy win. Low hanging fruit. And so we ignore the wins that we intellectually know are important (getting the migration done) and instead go for the wins that we know we can get this week/sprint/month/quarter.

The story ends predictably. The unowned code didn't become owned. The code that was surely going to get rewritten didn't get rewritten. The person who promised to do the work actually quit and built a yurt on their alpaca farm in Oregon.

Nobody wants to be the “we need to stick to the plan” guy. And really, nobody should have to: leadership in an organization should put their foot down and say, “If converting Coffeescript and Backbone to React and Redux was so important, then why isn’t it important enough to finish?” And truthfully, nobody in leadership should need to put their foot down because nobody should be pushing back against finishing the work you decided to start in the first place.

The real world is complicated, though. Nobody realized that this was going to be a seven month project instead of a three month project like it was specced as. Resources weren’t allocated. The plan for the half was mangled and work that we planned to do after the migration wasn’t started. There are lots of reasons why there’s pushback against crossing the finish line, and leadership has to balance the needs of folks who need things done with the folks who do the things.

The real trouble here is that “done” wasn’t really ever defined. Not once in my career has the definition of finishing a project I’ve been on “it’s done.” The definition of the project is metrics (“five customers have adopted the thing”) and high-fivey sounding success criteria (“React and Redux is the default way of doing things going forward”). Or at least, maybe it starts as something resembling done (“100% of instances of Coffeescript and Backbone are removed”) but then quarterly planning rolls around and the goal mysteriously finds itself not on the next quarter’s plan. The goalposts move; if we can’t firmly define what “done” looks like for a project and then hold to it, it’s indicative of one of a few things:

  1. It became a lower priority, or other things became a higher priority

    1. Anyone can just say that, though. If we said, “well, the last ten miles of this 100 mile highway don’t really provide that much benefit, it’s only 10% of the length!” we wouldn’t have any 100 mile highways.

  2. The people making decisions lost sight of the practical benefits of being actually done

  3. The people doing the work got tired of doing the work and found ways to roll up project requirements into some notion of “maintenance mode”

    1. Often this is because it was such a slog and they weren’t offered any support.

  4. The remaining work is probably going to take a while, and nobody with any accountability thinks that they can salvage the remaining time for an impressive-sounding bullet on their self-evaluation come performance season

The temptation to play the bad solitaire move and do the thing that’s right in front of us makes it harder to get the original project done. Why? Well, you’ve distilled the project down to its hardest or most toilsome parts, you’ve killed all of the momentum around it, and you’ve cleared everyone’s short-term memories of the state of the work. When the next poor soul asks, “Why do we still have Coffeescript and Backbone? Can we delete this?” they’re starting from square one to get things moving again. But yet, we all do it (especially at large companies). This is where the most loathsome bugbears of our codebases come from.

And not to rant, but they accumulate! This is never a one-time thing. If this is a problem for you and your organization, it’s happening time and time again. If it only happened once, it’s not a problem, it’s a business decision. If this resonates with you, it’s because it keeps happening (which, if you’re at a medium to large-sized company, I can almost guarantee). As these almost-finished projects build up, their maintenance cost—or the maintenance cost of the unfinished bits—starts to grow superlinearly. Things that were annoyances become ship-stoppers. Or if left long enough, code yellow events.

This is project debt. You’ve got projects that have piled up, unfinished, like an engineering manager’s Github. The interest that you pay on those projects compounds, and you pay it with engineers’ time. The less project debt you have, the faster you move, and the less time you waste on things that should have been finished months or years ago.

The solution, ultimately, is digging in our heels and doing the work that we committed to. Maybe that means getting some help or giving an impassioned dad speech about “finishing the things we started.” Or, when someone else is giving the dad speech, remember that plans are all made up and what we get done matters more than the number of things that we start.

1

A quick primer on terminology: the four piles in the upper left are “foundations”. The pile you draw from is the “stock”. You flip cards from the stock into a “waste” pile. The space underneath is the “tableau”. Each pile within the tableau is a “pile”.

2

One of the complexities of Klondike is that there’s no way of knowing whether moving the eight to make room for a king is the right move: if may be that you find yourself in a dead end because of that. The best you can do is to try to optimize your strategy to avoid committing yourself to a position that may force the game in one direction or another until you’re certain that you’re making (what you believe to be) a good move.

2
Share this post

Our pathological inability to do "done"

basta.substack.com
2 Comments
Brandon Ramirez
Jan 23

This message speaks to me. I have worked on projects like this and also play Solitaire.

Expand full comment
Reply
Jacob Scott
Writes Word Word Club
Jan 23

One approach that is difficult and doesn’t apply globally — but is way underused imo — is to structure migrations such that every incremental step makes things better. Such a migration has basically no WIP and can be paused/stopped at any time without adding project debt.

Expand full comment
Reply
TopNewCommunity

No posts

Ready for more?

© 2023 Matt Basta
Privacy ∙ Terms ∙ Collection notice
Start WritingGet the app
Substack is the home for great writing