WDYWYM (pronounced “witty whim”) is a software engineering concept that is as familiar in code as in everyday life. We all deal with some degree of clutter in our lives, and sometimes our efforts to eliminate it create a bigger mess than we started with. WDYWYM attempts to reframe the question: rather than asking how we can be rid of mess, we should be looking for the most effective place to put it.
The Junk Drawer and the Shelving Unit
In daily life, the (false) dichotomy of WDYWYM is best illustrated by a pair of concepts: the junk drawer and the shelving unit.
The junk drawer is a familiar institution in many homes. It can establish itself naturally, seemingly without human intervention. Semi-related items cling together in an unplanned equilibrium, and attempts to organize or categorize them routinely come up short. Residents of households everywhere bemoan the state of the junk drawer, but at the same time, they access it regularly. The odds and ends of the kitchen or office are, admittedly, within reach there. The cook or the office worker opens the drawer often enough to have a decent mental inventory of what’s inside, and can often find what they need in seconds. When the desired tool has achieved its purpose, it can simply be tossed back in without regard for order.
On the downside, the household junk drawer has been known to overflow. The seldom-used potato masher at the back of the gadget pile becomes wedged under the lip of the drawer, preventing it from opening fully. Then the cook curses the existence of the junk drawer and begins searching for alternatives.
The shelving unit is one such alternative. Many garages, basements, and attics are populated with these — a series of sturdy shelves stacked with a regiment of neat crates or boxes. The most devoted organizers may develop a system of labels, perhaps even an inventory document. The unordered clutter of the junk drawer is abstracted away, here, behind the façade of a grid of containers. It is tidy and pleasing to the eye. Because of its propensity for neat stacking, this system utilizes space efficiently. When something is needed, the inventory or the box labels can be consulted with relative ease, to guide the seeker directly to the desired item. For things that are seldom used, this solution may even be said to be beautiful.
Of course all solutions have their drawbacks. The shelving unit requires the user to engage with several extra steps to retrieve the desired item, often including a roundtrip up or down the stairs. It also requires a degree of commitment to maintain: everyone who uses the system must be willing to consult and abide by its rules, returning things promptly — and exclusively— to their dedicated locations after use.
Many readers can probably recognize each of these systems. In fact, it is likely that many homes contain some version of both. The beauty of WDYWYM is that it absolves us of the persistent guilt of mess. If mess is taken to be inevitable, then every clutter problem becomes a “WDYWYM?” problem. The trick lies in finding the right answer to the question.
Technological Junk Drawers
In software, the junk drawer is an easy metaphor. Sometimes these classes are called a “dumping ground” or a “garbage can” — it’s the class that has it all! These are broad, sweeping logic-layer catchalls that seem to handle every use case under the sun. And they are as easy to despise as the junk drawer that probably lurks in your desk even as you modify them.
The obvious downsides to the “junk drawer” class are that it is bulky, tends to grow beyond its scope, and creates a potential for duplicate, orphaned, or unreadable code. It also encourages new developers to add to the problem — “what’s one more method?” It’s unlikely that readers of this article will need much prodding to agree. How many times has each of us scrolled to the depths of a lengthy junk drawer class only to scratch our head and wonder aloud to our rubber duck: “is this even used?”
The purpose of discussing the junk drawer class as a WDYWYM problem is really to extoll its virtues. If the logic-layer catchall is anything, it’s easy to grok. A modern IDE makes navigation a non-issue. Debugging is a breeze, because all of the code is right there, easy to access and step through. Put a breakpoint anywhere in a mass of procedural logic and you can walk through it from start to finish. Fast debuggers will make quick work of any issues, and even new developers can get their heads around it quickly enough to begin submitting bugfixes. The benefit to newly onboarded team members may be its greatest advantage, as no prior or external knowledge is required in order to locate the logic. It can simply be read like a (rather dull) book.
Is there a time and a place to allow a class to grow into a junk drawer? The knee-jerk reaction is to say, “absolutely not”. But in some circumstances, it may not be the worst choice. Code that is being touched as often as your trusty pen might be best served by living in an obvious, straightforward location. Code that’s as central to your app as your chef’s knife is to your dinner might benefit from being easily debuggable, even by a new hire who hasn’t quite retained their required reading on the department’s OOP philosophy.
Software Shelving Units
The OOP space is absolutely packed with shelving units. A simple example is the Decorator pattern. A class’s base functionality can be wrapped with one or more decorators, each of which is simple, clean, and reusable. They have meaningful names and likely live in a meaningful location inside the logic layer.
Because we tend to live in an OOP world, it’s easy to mark the advantages of a pattern like Decorator. It conforms to SOLID principles. The code stays clean, and the classes stay small. It’s testable, reusable, and encapsulated.
But because we also live in a WDYWYM world, it’s important to consider the drawbacks. A developer who is working in a class, particularly in a debugging sense, must know in advance whether the class is decorated at all. A junior developer may have no earthly reason to even suspect this. They will need to locate the code that registers decorators, search the codebase for references to the class, or ask someone else for help. Once the dev understands that a decorator is in play, they’ll need to know where in the code to look for decorators. Are all decorators kept in the same place? Do they live alongside the classes they decorate? Are they organized by domain? Again, advance knowledge is needed. And advance knowledge, as we all know, is a dirty euphemism for documentation.
Is it justified to ban the use of decorators and similar patterns? Unlikely. But using them everywhere, and using them for their own sake, is not always the best answer to WDYWYM. Code that is fairly solidified and unlikely to change is a good candidate for encapsulation. So is code that is related to the main functionality in a tangential or meta sense.
An Example: Catch and Cache
Our team recently needed to reduce the load on our cache. One entity in particular was caching a collection of fairly large child objects as a property, and we wanted to change that behavior to cache only the child objects’ keys.
One developer chose to approach this by simply changing the type of the collection property to store only the keys, and then updating all the methods that populated the property. Another developer preferred to write a decorator for the caching logic itself, intercepting any instance of the parent class to be cached and converting it to an internal type that ignored the child entities and aggregated only their keys.
The decorator approach has some apparent advantages, primarily that it follows the open-closed principle. It may also reduce the load on the developer implementing it by preventing them from having to touch every reference to the parent class whose relationship with the cache is changing.
However, it also has some disadvantages. It allows the code to be misleading; to say one thing and do another. Rather than getting rid of the code that perpetrates the offending caching, it allows that code to appear to remain. Then, out in the garage where we keep our decorators, it does something else entirely.
So which approach is better? The answer, of course, is “it depends”.
If the parent class in question is heavily used throughout the code, and changing it would require updating many files and incur a need for intensive testing, then the decorator approach may be preferable. On the other hand, if the overhead of updating it is small, and the class is likely to be at the heart of frequent debugging and new features, allowing it to say one thing and do another might create a significant disadvantage for the next developer who reads the code.
The makeup of the team and the lifecycle of the software are factors as well. Will the same person be maintaining this code for most of its lifetime? Is this code likely to last forever, or will it be replaced soon? Will the caching behavior be changing again? Is this a pattern the team uses frequently, or is this the only example? Will it hinder a developer if the source of the entity’s data (i.e. cache vs database) is allowed to be unclear?
There are too many questions to provide a prescriptive answer. In our case, the code is not expected to live forever. The references to the parent class are relatively few. The caching behavior will probably change again soon. It is unlikely that a developer will be negatively impacted by not knowing the source of this particular data. After some spirited disagreement, the decorator pattern was ultimately used.
Let’s get this map folded back up first
So What the Func Do We Do?
The main takeaway from WDYWYM is that mess is inevitable. WDYWYM is about abstraction. We can stand at one level and abstract away all the mess in sight, but zoom out to a higher layer of abstraction and the whole tangled mass appears again, just as jumbled as ever. A mess of lines in a file is just as cluttered as a mess of files in a folder.
When we accept that our mess is here to stay, we can begin to consider the next logical point: picking the right place for mess is more important than eliminating it. This often comes down to philosophy. Developers have a full spectrum of preferences regarding what constitutes Clean Code, and one person’s spreadsheet-governed plastic tub hoard may look like a garbage dump to someone else. It’s important that teams examine the question of WDYWYM together, regularly, in order to build something that meets the needs of all the members.
At the end of the day, both the accessible junk drawer and the out-of-the-way inventory are useful and valid problem-solving approaches. When evaluating what solution to pick, the most important consideration is the problem at hand. Different solutions serve different problems, and developers with soft eyes can see both the cleanliness and the messiness in a given solution. Weighing these things together, openly and transparently, leads to efficient collaboration and maintainable code.