Over the past few years I’ve been driving an architecture and tech spec review process at work. This has been helpful in breaking down certain silos, distributing knowledge across the organization, and building skills and experience in more junior engineers. A recent tech spec caught my eye, and resulted in a really valuable learning opportunity.

The specifics of the spec aren’t terribly important, but at a high level the proposal was to introduce a single Composable to represent lists of files anywhere in the app. Due to the multitude of sources that can provide lists of files this was accomplished with a relatively complex set of generic data source factories that were registered via our dependency injection system to later be looked up and constructed.

To me this felt like a complex system for something that was designed to make consistently displaying lists of files easier. To test the proposal I started thinking through how callers might adapt the system to future requirements and an interesting pattern emerged. All solutions required support to be added within the proposed library of code. This made it clear to me: this “composable” element was no longer composable.

I shared these concerns with the spec’s author and we hopped on a call to iterate on the proposal to try to simplify things. We worked through use cases and iterated on the code by using a few approaches that have served me well over the years.

First, we tried extracting things that were being created or looked up internally that callers would have to know about anyway. For instance, callers would indicate the data source that should be used by passing a key which contained configuration information like sort order or search terms. The composable would then get the registered data source factory for that type of key and construct the data source. But why not just let the caller construct the data source and pass it to the composable? That’s true dependency injection!

Second, for each newly proposed data type or interface we asked “what value does this add”. There were several interfaces being introduced which simply mirrored existing ones, sometimes with subtle, unrequired changes, which didn’t actually add any value. When we found one, we simply removed it.

As we worked through the proposal thinking about current and future requirements in this way an interesting thing happened: the code proposed in the tech spec became simpler and simpler until it was just…gone!

We’d consider how the proposal might handle some edge case or future requirement, like paging data sources, we’d move that complexity out of the internals into a self contained thing that could encapsulate the specifics of the requirement, and it was no longer something that the proposed component needed to be concerned with. After about an hour of this the author of the spec declared “I don’t think this needs to exist!”

Upon reflection, the author shared that there were some fairly complex emotions involved with this realization. He was originally excited about the proposal, because if felt like a big, meaty, impactful project. While he was also excited at the simple, elegant solution that we had come to, he was a little sad that he wouldn’t get to work on that big, meaty, impactful project.

I was excited to share how much flexibility you can get from simple, elegant APIs. I shared with the author that this approach of breaking down complex problems into the simplest form that can solve the problem is what I love about programming. In the best case you can do what we did and completely remove complexity from the code base. In many cases you can work a solution down to a small, elegant design that not only makes it easy to understaand, but also makes it extremely flexible.

Programmers often like working on complex systems, but I believe that it’s important to practice breaking down that complexity into simpler systems. Ideally we’d have incentive structures that encourage this behavior. While there will always be complexity somewhere in the systems we create, it’s important to also celebrate the code you didn’t write.