DEV Community

Cover image for Please Reinvent The Wheel
Jason C. McDonald
Jason C. McDonald

Posted on • Edited on • Originally published at indeliblebluepen.com

Please Reinvent The Wheel

Don't reinvent the wheel.

I submit the aforementioned for admittance into the "Terrible Folk Wisdom Hall of Fame." We bandy the phrase about as if it is holy scripture, but think about it...

How many times in the history of the world have we literally reinvented the wheel?

Different types of wheel

If it hadn't been for a plethora of inventors and engineers throughout history looking for ways to improve and adapt the wheel for various uses, we'd all be driving around like Fred Flintstone.

The Flintstone family car

The same is true in programming. There are many who will say, often quite vehemently, that you shouldn't waste your time writing original implementations of existing patterns or algorithms; indeed, some would say you shouldn't even bother developing new patterns and algorithms. "We have all these great libraries, so why waste your time? You could never write something as good!"

Aren't we glad none of the authors of those patterns, algorithms, and libraries listened to their contemporary detractors?

Now, before a whole bunch of y'all take this as license to go reimplement bubble sort for the ten-thousandth time, let me assert that we don't reinvent the wheel for the sheer sake of reinvention. You have to know the appropriate situations.


(Re)Invention Reason 0: Learning

I'm going to get the first reason out of the way, because it isn't technically an motive for reinvention so much as reimplementation. One of the most effective ways of learning an algorithm or pattern is to implement it yourself.

You'll notice that I listed this as Reason 0, and that's intentional. Implementations written for this purpose rarely see the light of day, unless they wind up growing into one of the other categories.

Implementations written for Reason 0 usually...

  • Implement the exact algorithm, without any improvements,

  • Are slower than their mainstream counterparts, and

  • Are less stable and maintainable than their contemporary counterparts.

Thus, even if you write a fresh implementation for learning, you should think twice, and twice again, before you use it in production (or distribute it to others).

(Re)Invention Reason 1: Better

One reason to reinvent the wheel is if you can improve upon its design. Wheels have seen the addition of spokes, tires, tubes, rims, treads, hubcaps, and all manner of improvements. These enable wheels to perform better in specific circumstances.

In the same way, reinvention may be justified if you can improve on an existing design. For example, I developed IOChannel as part of PawLIB. My library added a lot functionality on top of std::cout and fprint, allowing for easier printing and formatting of many types of information, including memory dumps.

However, a word of caution: be wary of piling on bells, whistles, and gongs! Don't add functionality just for the sake of functionality. "YAGNI" (You Aren't Gonna Need It") is an excellent acronym to remember here: only add features for which you or your target user has a specific use case (or user story).

In other words, your invention shouldn't make toast unless it's a toaster.

(Re)Invention Reason 2: Faster

Another common reason to reinvent wheels is the need for speed. A racecar cannot race with wagon wheels; it needs a type of wheel specifically designed for driving on the raceway at high speeds.

Many libraries are designed to be all-purpose tools, sacrificing many specific optimizations to remain useful to the largest possible set of users. However, once you know what your specific use case is, you will often achieve superior performance by creating a custom implementation of the algorithm, pattern, or data structure.

Writing high-performance code isn't something you can approach casually. It is a serious investment, requiring lots of time, careful attention, extensive testing, and a lot of study and source reading!

Aside from this, sometimes a coder can develop novel optimizations to improve the general implementation. At times, one may contribute these improvements to a mainstream library, while other times call for a fresh new library. This decision is not something to take lightly.

Personally, I have been discouraged by some naysayers to try and beat the performance of mainstream implementations, but the fact is, I have done it. Time marches on, revealing new innovations and improvements every day. Our craft needs people who are willing to challenge the Tried and True.

Failure here is not something to be ashamed of. Performance optimization is an art. You learn something new with every failed attempt at a faster implementation. If you have a serious need for speed, keep at it!

I'll conclude this reason with another word of warning...

"Premature optimization is the root of all evil." -Donald Knuth

Before you start fine-tuning your racecar to be the fastest around, you need to build a functional vehicle first. Get it working, then working well, before you embark on a full-blown optimization. Again, this is not an endeavor to take lightly. Optimization is a feature all its own.

(Re)Invention Reason 3: Cleaner

Sometimes a new implementation doesn't need new features or faster speed to be worthwhile; sometimes it merely needs to be cleaned, refactored, and made maintainable!

Abstractions are tricky things. For every language, library, and tool we use, we are adding another level of abstraction, and as Joel Spolsky pointed out...

"Abstractions fail. Sometimes a little, sometimes a lot. There’s leakage. Things go wrong. It happens all over the place when you have abstractions."

The truth is, we have to maintain this stuff, and sometimes that maintenance falls on the user, rather than the author.

With that in mind, which code base you rather have to jump into?

A) A popular library with a 10-year-old code base half-conforming to a 1998 standard; completely uncommented, sparsely documented, with hundreds of open issues, many dating back to the early 2000s.

B) A new library with the exact same features, written to strictly confirm with a recent standard; well documented, intent-commented, with relatively few open issues.

Now let's make the decision even simpler:

A) Use feature X from Library A (above).

B) Write a fresh implementation of feature X, and skip using Library A altogether.

Quite often, it's easier to write a new implementation, rather than untangle a ten-year-old plate of spaghetti.

Once again, the naysayers will probably start shouting at you: "What makes you think you can do it better than Library A, you cocky little whippersnapper?"

Your response should be "What makes you think I can't?"

As with Reasons 1 and 2, this is not something to embark on flippantly. You have to be prepared to write code that achieves the exact same goal as the old implementation in question, but cleaner and with less bugs. What you don't want is to create Yet One More Terrible Implementation that other coders will have to put up with.

By the way, for a real life example of this, compare and contrast the GCC compiler and standard libraries with those of LLVM/Clang. My company uses the latter primarily on grounds of the code base!

Other Reasons To (Re)Invent

There's a few other reasons that might necessitate reinventing the wheel:

  • Licensing reasons. Maybe the existing implementations are all proprietary, or by contract, all GPL-licensed. Sometimes the only reason you need for reinventing the wheel is so you can use it, and that's okay.

  • The existing implementations are abandoned. Sure, you could try forking the library and developing it further, but it will depend on whether that's more practical. (See Reason 3).

  • Because dependencies = pain. Sometimes it just isn't practical to bring yet one more dependency into your project. If, after careful consideration of your options, you find that writing your own implementation is faster or better than becoming dependent on a library, or otherwise injecting third-party code into your source, go for it.

Reasons Not To (Re)Invent

Now that we've covered the major reasons to "reinvent the wheel," let's talk about a few reasons you absolutely shouldn't:

  • Because I can (unless Reason 0). Unless you have a specific reason to reinvent, please don't. We already have too many poor Yet Another implementations out there, clogging up GitHub and Sourceforge.

  • Because NIH [Not Invented Here]. Believe it or not, there are talented coders that don't work for your organization. Thoroughly explore all your options before you decide to reinvent the wheel.

  • Just because I don't personally like the developer. I'm sorry, get over yourself. If your only reason for writing a fresh implementation is to show up another developer, you're just feeding your ego. Nobody wins in that scenario.

  • Because I can do better with one hand tied behind my back. Yeah, you probably can't. Always assume that reinventing the wheel is hard.

  • Because it will only take a few minutes. Ha ha ha ha, nope. Any time I catch myself thinking "I'll take a few minutes and...", I know I'm setting myself up for failure.

  • Because I didn't do my research. There is never an excuse to not do your homework on existing implementations!

Don't Be A Naysayer!

If you only remember one thing from this article, let it be this:

For the love of all things digital, please IMMEDIATELY strike "don't reinvent the wheel" from your vernacular!

This phrase encompasses all the muse-dousing pessimism that stalls progress. We have not run out of ideas, and we never will. Every time you tell someone not to invent the wheel, you may be shutting down the next Guido van Rossum, Alan Kay, Tim Peters, or Donald Knuth. You may literally be the voice that prevents someone from creating the next breakthrough in language processing, the next fastest sorting algorithm, or the next watershed programming language.

Instead, encourage people to approach reinvention with clear, careful intention. Encourage is the key word here! You may be igniting the next spark that changes the world for the better.

Top comments (6)

Collapse
 
martinhaeusler profile image
Martin Häusler

I could not agree more with this article. The attitude of "don't even try, there is a solution out there already" is frustrating at best and infuriating at worst, but misses the point 99% of the time. I do write spring-based Java severs. I do use Jackson, Hibernate, Logback, you name it. But I am also writing a custom graph database. It can do things which are not provided by any other graph DB, and only in a limited fashion by the most expensive of commercial SQL databases. And yet... I keep hearing the same darn question over and over and OVER again: "Why didn't you use X, it already has feature Y!" Except that, of course, it always turns out that X doesn't support Y nearly as much as necessary. When it comes to seeing somebody else writing innovative software, suddenly everyone turns into an expert in everything and is pointing fingers.

So yes, there are very valid reasons for re-inventing the wheel.

Collapse
 
jvanbruegge profile image
Jan van Brügge

It's funny, because I was about to write this exact post myself. I am a self thought Programmer, with Java being my first language I learned when I was 14 years old.
About one year later, I decided that I want to write a Super Mario Game. I already wrote a few GUIs with Swing, so i just abused the Java GUI system to paint sprites on a pane. Point is, it worked. At this point I did not had any formal education or had read about patterns at all.
So I saw issues popping up. First one being: I have to write the same animation logic multiple times, why not put that somewhere else and just initialize it with the path of the image and a few settings? So I learned to seperate and reuse logic.
Then I wanted my GameObjects to be notified if certain events happen, but not every GameObject needs every event, so i wrote a Manager that you can subscribe to in your constructor if you implement a certain interface (the event handler). I just "invented" pub-sub or event driven programming without knowing it.
Then I thought that it is quite inconvenient to pass this manager per constructor all the time, as it limits where i can create GameObjects. So I thought if static variable are the same for all Objects, I should be able to have a static instance that is initialized only one. Later I read that this was called a singleton.
Reinventing stuff is the best way to learn not only concepts, but also deepen your programming knowlege in general.

Collapse
 
codemouse92 profile image
Jason C. McDonald

You may enjoy the book "Game Programming Patterns" by Robert Nystrom. Don't let the title fool you; these are the same patterns used in non-game programming, only presented from a game design angle to make them easier to visualize. He not only covers the what, why, and how, but also the when (as in, when NOT to use the design patterns).

Collapse
 
nebojsac profile image
Nick Cinger

Great article!

You really went in depth on the subject, and I love all the DOs and DON'Ts: very practical takeaways.

The 3 Reinvention reasons are worthy of their own posts, these are big topics to cover.

Key takeaway for the naysayers:

we don't reinvent the wheel for the sheer sake of reinvention

Collapse
 
codemouse92 profile image
Jason C. McDonald • Edited

In all irony, this article concept has been reinvented too...

Much of the same points as mine, but in a lovely, concise form:

Unbeknownst to me, this one predated my own article:

Collapse
 
johannesvollmer profile image
Johannes Vollmer

Yes! So true! <3