You have to code some functionality. You’re not sure what the final scale of it will be. You begin and first piece falls into place. You keep hammering away. After a bit you have something that works. It’s not perfect, but it gets the job done at the scale that it needs to be done. You move on to the next task.
Weeks later you come back to that code you implemented. The scale of the project has changed. Your quickly written code is suddenly woefully inadequate. You built the foundation for a hut but now you need to build a mansion.
That’s what happened to me yesterday. Weeks ago I coded the quickest solution possible for playing music in our game (we use Unity) — play the music layers when we need them. Yesterday we wanted to add all of the music for a new level meaning complexity had effectively doubled. I opened up my script that manages music and immediately felt pain. My code couldn’t handle the level of complexity we had reached.
I looked into possible solutions and came across Unity 5’s audio mixer system. The more I looked at it the more I realized that over time I had been implementing a janky version of the same system. Not only did I feel the pain of outgrowing my system, but I felt the pain of knowing that the answer I needed had been right there in front of me the whole time. The day felt like a waste.
Later that night I watched Nathan Kontny and Jason Fried record an episode of Work In Progress. They asked for questions and I submitted one that attempted to capture my mistake. They answered it really well.
At one point in his answer Nate said the following:
“I’m a big fan of doing something until it becomes painful … Give yourself pain. Keep finding places where you can actually feel something and you have to fix it. Same thing with hiring. Don’t hire in advance of stuff. Start doing all the roles, start doing support, do sales, do whatever, and when that stuff starts feeling painful and you can’t bend anymore and there’s no more time, then it’s a really good time to start hiring people. You understand the problems, which makes so much more sense than hiring a person in advance when it’s an imaginary problem.”
It took a while for it to sink in, but I realized what I perceived as a mistake in my implementation was actually a huge success. Trying to guess what you will need ahead of time is a fool’s game. You can never know what you’re going to need. Especially when you’re a solo developer — you just don’t have the time to implement your best guess of what the future will hold. Your needs in the current moment are the only things that are certain.
Here’s the insidious thing: when you don’t implement something perfectly the first time it feels bad. You feel like you’re redoing work — if only you had done it right the first time. However, chastising yourself for not engineering a correct solution the first time, you’re not seeing the benefitof dodging dozens of over-engineered trainwrecks.
I’ve actually probably erred too much on the side of over-engineering. I can’t begin to count the hours I spent attempting to future-proof code that we ended up throwing out a couple of weeks later because the project changed. It doesn’t feel like a waste of time in retrospect because the pain wasn’t there. It was fun to implement those systems in a robust way, picturing what they could become. However, fun does not equal functionally useful or a good use of time.
In the end it might be the things that aren’t painful that are more dangerous. Our sense of pain is misguided because we have a hard time judging what actually pushes projects forward. In the future I’ll try to worry about dodging the real trainwrecks — overengineered systems designed for a future that never exists and cause projects to stretch out in perpetuity — not some misplaced idea that I should get everything right the first time.