Jan 16, 2025 Sunk Cost Even though you're the captain of your destiny, you don't have to go down with the ship.

Truman Shuck

You keep hitting ↑ in your terminal because you're looking for a command that was just there. It's been thirty seconds. You can't find it. It's been two minutes. It feels like two hours. You experience sunk cost in the sunken feeling in your chest, the sinking of your fingers as they come to rest back on the home row.

Doing something because it's been done before. Doing something because not doing it anymore would feel like failure. Doing something because otherwise all of the other somethings you've done would have been a waste. There are many articles on how the sunk cost fallacy applies to software architecture and to product teams:

  • It's okay if you over-invested in those JSONB columns, but you could start making relational tables again after their performance tanks.
  • It was fun to learn that bleeding-edge JavaScript framework, but you could better support your business if you wrote the next thing in the boring technology where at least request routing is a solved problem.
  • It is neat how much you can program into those little blocks in Notion, but another hour of tweaking your home-brewed task management system is better spent moving everything to Linear and then never thinking about it again.

This article is not about the big mistakes. It's about the small ones that I find myself continuing to make today and what I try to do instead.

I wasn't kidding about the up arrow

There are many jokes, and some truths, about the virtues of laziness in software engineers. Automating the simple instead of trudging through the tedious is a hallmark of software development, and what feels more tedious than looking up the correct flags for a complicated command you know you just executed the other day? When you're trying to un-stage the latest commit on your git branch, or hoping to remember all of the subcommands required to generate that aws token. You know it starts with git reset and where does that HEAD~ something go? It's definitely aws oh of course security token service get-session-token something?

It might be in terminal scrollback, and it might be found with enough invocations of the "up" arrow. Often as not it's in another shell, or it's been pushed out of recent history. Often it's gone; back to --help for tar -x...z..? f? ....

Instead, add a shell alias, alias grs="git reset --soft HEAD~1", so you can git reset in two commands instead (wait what did I call that one? cat ~/.zprofile, oh yeah.) Better yet, grep it out: fc -l | grep git reset. Best, reverse search shell history with ctrl+r on the fly. Whatever you do, stop hitting ↑. Well, maybe just once. And just once more. I swear it's right here somewhere.

Refactoring: An Unknit Sweater

Sometimes I do think that you have to grab your seam ripper in order to pull everything apart and start fresh. But in the middle of implementing an otherwise small story is often not the time. "Ah," I've thought to myself, "it's this feature which is finally exposing what's felt so awkward about this module." I grab some coffee. "So if we could extract all of this stuff, and rename a couple of these files," as caffeine propels me, "and that would imply a change to this structure..." and slowly a whole sweater has unraveled in my lap.

So I implement a small feature in five lines instead of fifty, which is great. But it's on top of a pull request that has five hundred lines of refactoring, which is asking a lot of my code reviewers. At least the tests pass, except I had to refactor a few of those too since they were a little too tightly coupled to the previous implementation.

Save it for the backlog! No, that's where cleanup tasks go to molder. Bring it up in a retro, or your recurring technical check-in meeting. Get some folks on board or at least let them know you're planning on doing a bit of cleanup soon. But first just implement the small feature so that your users can get a bit more value out of your work and you in turn can continue to afford your coffee.

Git Rebase Heck

Implementing something novel can be a long and meandering road. I'm a proponent of the "Make it work, Make it Nice [Right, Beautiful], Make it, eh, reasonably fast — or at least don't do anything too dumb" workflow. When executed in the context of a single pull request, this means going over the same source files a handful of times of the course of a bunch of commits. It also means that folks will usually have merged at least one or two unrelated changes into those same source files while you were on your winding journey.

It's nice to freshen up a feature branch before opening a pull request, and I usually prefer to do so with a rebase from the main branch. If one of those well-trodden source files has a conflict, I often find myself resolving it three or four times in a succession of subtly different flavors. "I'm so close to getting back to HEAD," I think, as I try to remember which branch is upstream and which version of a given change I want to include. git rebase --continue becomes another mantra.

Sometimes it is better to grab the thread ripper. Grab your commit shas out of git log and cherry-pick them onto the top of latest main:

git checkout ORIGINAL_BRANCH

# keeps my original messy branch
# and keeps the commits I'm gonna
# cherry-pick in the reflog.
# They would probably stay there
# anyway but better safe than sorry.
git checkout -b ORIGINAL_BRANCH-BACKUP

# delete my local copy of the messy branch
git branch -D ORIGINAL_BRANCH

# go find up-to-date main
git checkout main
git pull origin main

# then re-create the original branch
# from the updated main
git checkout -b ORIGINAL_BRANCH

# and cherry-pick back in the work I want
# repeating for all commits
git cherry-pick <commit sha>

# finally we have to force push
# because we changed history
git push origin ORIGINAL_BRANCH --force

I've fallen victim to this fallacy plenty of times in my career, and these won't be my last sunken costs. But it's nice to recognize at least a couple of the places where I no longer have to spend time if I don't want to. Do you have tips on avoiding other time-sinks? Maybe you just like the meditative action of continuing to press ↑ until you find what you're looking for. We'd love to hear either way, below.

Thanks for your time and attention.

Leave a comment

Thoughts for our thoughts

We hope you find our writing useful and, perhaps, that it gives you something to think about. We read everything we receive and we'd love to hear from you.