Keep It (Mostly) Simple, Stupid

Yuzu
9 min readOct 5, 2022

--

“Keep it simple, stupid”. Easily said, less often applied correctly. I’ve seen great ideas slain by those operating in the name of simplicity. “Simple” is not always the right answer. Deep analysis and careful consideration of a problem can be important, and there are situations where failure to do so is catastrophic. Like all things in life, it’s a balance. That said, I still think KISS is a good thought to engineer by. The nuance I want to capture here is this: just because the solution you have chosen is simple does not mean the path you used to get there should be. KISS, but KISS responsibly, and make sure you know that the simplest way to do something is the best way. What do I mean? Story time.

Calling Myself Out

I recently worked on what were outwardly simple improvements to a system. There were buttons that were being made non-interactive, and the reason was not clear to our end user. My solution? When the user hovered over these buttons, show a callout explaining why the button was not clickable:

Picture of two buttons saying “invite” and “go to”. Above the invite button is a comic-book style callout reading “sorry babygirl, can’t do that”
A mockup of how it should look. Definitely.

I wanted to use this system because I had just worked on it and knew it would be clear, unobtrusive way to show the user what’s up. So far, KISS was in full effect. This was a known, working system that I had just used. We knew it would work for our user case: The string we wanted to use would fit, and it would be clear to the user the button they were hovering had a problem. We shook on the design, and started implementation.

Immediately, I hit a problem. The callout system’s API was weird. ALL calls to display a callout ran through a third-party library that would check if the user was in a particular A/B test before we displayed a callout. ALL callouts needed a unique ID, and the system would not display a callout with the same ID as one it had shown at any time in the past. None of our UI classes had an easy way to get an instance of the manager so a callout could even be shown. I had no idea why it was built this way, but knowing how it worked, this wouldn’t work as a general purpose error display.

So let’s pause here. KISS, right? What’s the easiest, simplest way we could address this problem? Well… I’d argue the “simplest” way to do this is to change as little as possible. I could make an experiment in our A/B testing system and generate a unique ID each time I wanted to show an error. I could spawn a new instance of the manager class for this screen and I’d be good. This would be the least amount of work and the least invasive change I could make. But is the the right way to do things? Absolutely not. Hitting a 3rd party SDK just to show a simple alert dialog is ridiculous. The records tracking the unique errors would grow without end, and eventually fill up our user’s save data for no reason whatsoever.

Sometimes, to keep things simple, you have to do some much more complicated engineering first. I researched. Why was this seemingly general purpose system built this way? Well, turns out this general system was only used in one place in our codebase, and the engineer who had built it had tailored made it to serve that purpose. We needed the experiment because we wanted to A/B test whether or not showing a callout to advertise the feature was actually effective in getting users to engage with the feature. Callouts were all tracked and only shown once because we wanted to show this callout exactly once to each user the first time they logged in. UI classes didn’t have access to the manager because at the time, we hadn’t established the good patterns we now had for managers for these kinds of dialogs. Perfectly good business case, good user experience, just a poor implementation for something that was supposed to be general purpose.

So knowing what we know now, can we still KISS? I don’t think so. The simplest solution is not the right one. But what I can do instead is take the time to make it so that you and other people can KISS well into the future. I knew what was wrong with the system. I put in the leg work to understand how we were using the system so I knew why it was built the way it was. Now I could refactor the system to make it simple and easy to use while preserving the functionality the original implementer had. Simple? Absolutely not. Correct and would let me KISS in my original work? You bet your buttons.

I let my manager know I was going to fix this system and got signoff from my team to do the work. The experiment we were running had been completed, so I removed the code that plumbed it through the third party. I made “show a callout once” a specific method of the manager class so that users of the manager had the option to use it in both the old way and to use the same callout data repeatedly. I brought the system in line with our best practices, and made it available to all of our UI classes in the same way our other dialog managers were. I made the easy-to-use system the original author had intended it to be. It took a week. It was absolutely not the simple way to do things. In fact, it disrupted efforts that had nothing to do with my original work. But it provided a new tool to my team and made my original work simple to do once everything was said and done. I did KISS. Eventually! And I thought I was ready to roll on my original work item…

The Tail End

I went to use the shiny new system I had just refactored and…

A screenshot of the game I worked on with a callout showing over one of the buttons. The callout is not pointing at the correct UI element.
Sometimes software dev can feel like this clip from Smiling friends.

Fuck me, right? All this grandstanding to do things the right way and the system still doesn’t work. Looking closer at the code made me realize that the positioning system was trying to keep the callout in the bounds of the screen, which was the right behavior. It would be clipped if that wasn’t kicking in. This simple version of the system couldn’t display callouts over these specific buttons I needed it for.

Pause for some additional context. I had delayed this project by about a week to do things right. I’d also gotten COVID and had to take additional time off to move across the county. We were behind. Like almost a month behind. I had about two hours before I needed to kick a build so it would be ready for testing the next day. Remember that keeping your deadlines is part of engineering as well. We build the best thing we can with the resources we have, and one of those resources is always time. There wasn’t enough time to do a big fix like I had just done.

Immediately my brain started riffing solutions:

  • Arguably, the correct thing to do was to move the arrow so it was above the UI element we were calling out. This was the best solution I could think of. It would make the system much more useful and make it look right in these two cases. Problem was that that affected the older callouts the system was already servicing, and would require a full retest of the system I just finished refactoring. In an already fairly extensive PR that was fixing a bunch of other things. While it was correct engineering and long term wise, it was not correct given the deadlines we were trying to meet and the undue effort it would put on QA. No deal.
  • I could put the callout off to the side, but the system didn’t have any callout prefabs configured that would look correct there. It’d be the same problem in a different skin. Not an option.
  • I could hackily create a special version of the callout that would look right in this particular case. Would be simple, but would create some confusing and nasty code to either me or someone else clean up later, assuming it even made it past code review. Would likely delay my already late feature from being merged. Nope.
  • I could try to use one of our other alert systems we had. We had several different kinds of on screen alerts, some intrusive, some not. But the more I thought about this, the more I realized how right I was initially: callouts was the solution. These other methods would be confusing. No deal.

I reached out to a UI designer on my team named David. He riffed with me, running through some of the same thoughts. Then he hit something. Something KISS worthy. “Hey, crazy idea but… can you just remove the tail?” Initially this fell in the same bucket as “create a special version…” in my head, and I said we shouldn’t do that. But after hanging up, I thought about it more and more. I could do it in a way that didn’t impact the other callouts. If we added to the API, I could provide the new functionality without requiring a retest of the old callouts. So that’s exactly what we did. Here’s the end result in action:

Animated GIF showing the callouts discussed appearing over the buttons when a cursor mouses over them without a tail pointing to the buttons.

Is it as good as the proper callouts? No. But does it work and clearly communicate the problem better than any of the alternatives? Hell yes. Thanks David! Thanks brain for thinking of a safe way to do it that didn’t require retesting our old callouts!

This is what I think of most often when I hear KISS. Clever solutions that are far easier to do than the alternatives. But what I want to emphasize is how much thought went into ensuring this was the right way to do it. We didn’t just zoom right to this simple solution. It came alongside solutions that had benefits and disadvantages. And when stacked against them, simplicity did indeed win. The path to KISS was not simple, even though the solution it weighted towards was. This was the right choice because in the presence of all the other options we could think of, it was the best in time spent and complexity.

Conclusion

So what’s to be taken from these two examples of keeping it simple?

Even if the end solution is simple, the path to get there doesn’t have to be.

Good engineering is the careful balancing of needs against resources and capabilities. It’s going slow enough to know that what you are doing is correct, but fast enough to make sure that things still get done on time. Sometimes that can be accomplished in a way that aligns with simplicity. Sometimes that means asking for more time! Making sure things are done right is a core responsibility of the engineer — at the end of the day, we’re the only ones who can really say things were done right.

Part of KISS is being open to non-intuitive solutions

It’s easy to get pigeon-holed into thinking along one axis. Engineers often get aligned along “the right way” or “the correct way”. And sometimes, you need to break away from that axis to fix a problem in a way that respects resources and the needs of others. Engineering is never a one-person job, and it rarely occurs in a vacuum devoid of deadlines or money. Simplicity can manifest in all sorts of ways. Don’t be afraid to try them!

Don’t KISS in the bikeshed.

Applying what I’ve suggested here can lead to what’s called “bikeshedding”: overengineering and excessive thought about trivial aspects of a problem. Knowing when you are bikeshedding is a skill that comes with time and mindfulness of the problem you’re working on. Again, be conscious of resource usage. Limit the time you, your team, and your customers spend finding an alternative. All told, the discussions I mentioned about about the second solution took all of an hour to have and think on. I’d consider that to be the limit of how long I’d be willing to spend on that hack.

--

--

Yuzu
Yuzu

Written by Yuzu

Professional games programmer, aspiring game designer, musician, DJ

No responses yet