Why it is bad to be opinionated
Rense Bakker

Rense Bakker @brense

About: I am a fullstack developer with experience designing and building responsive web apps. Proficient with ReactJS, Typescript, Javascript, HTML and CSS.

Joined:
Feb 20, 2022

Why it is bad to be opinionated

Publish Date: Jun 2 '23
5 5

As we all know, everybody always has an opinion, but that is not what this article is about. I want to talk about opinionatedness in relation to software engineering!

If you're a developer for a while, I'm sure you've heard someone (or yourself) dismiss an external library or framework, because it is "too opinionated", but what does it actually mean? And why is it so important for an external library to not be opinionated?

Defining Opinionatedness

Opinionatedness is when something makes a choice for you, outside of the scope of that thing. Internet Explorer is a good example here. If you are a Windows user, up till a few years ago, you were forced to have Internet Explorer installed. I hope we can all agree at this point, that the choice for a web browser should by outside of the scope of an operating system (I'm looking at you MacOS/Safari).

The Downsides of Opinionatedness

Let's look at a simple example:

function processResponse(res: { body: Record<string, unknown> }){
  return JSON.stringify(res.body)
}
Enter fullscreen mode Exit fullscreen mode

At first glance there's nothing wrong with this function, but suppose that in the future, JSON.stringify turns out to be a very slow way to serialize your object? Or other serializers offer additional features that you want? You now have to change the JSON.stringify call inside the function. In this example that is not a big problem, but you can imagine in bigger applications, where the usage of a particular solution may be spread across several different files and functions, it can become quite problematic to make such changes.

Preventing Opinionatedness

Looking back at the previous example of the processResponse function, we can make it unopinionated and future proof by making a few simple changes:

// Allow for passing in a custom serializer
function processResponse(res: { body: Record<string, unknown> }, options?: { serializer?: (Record<string, unknown>) => string }){
  const { serializer = JSON.stringify } = options || {}
  return serializer(res.body)
}
Enter fullscreen mode Exit fullscreen mode

Now, I'm not suggesting that every function you write, should include an options object to customize every aspect (dependency injection). This is merely one example of how you can make your code less opinionated and more future proof, by making it open for extension (remember the open/closed principle?).

Another good way to make your code less opinionated and more future proof, is by using the middleware pattern, like ExpressJS does for example:

const app = express()
app.use((req, res, next) => {
  // Any request/response handling middleware goes here
  // Even ones that havent been invented yet
  console.log('Time:', Date.now())
  next()
})
Enter fullscreen mode Exit fullscreen mode

Possible Advantages of Opinionatedness

Sometimes the choice for an opinionated technology may have actual benefits, aslong as you are aware of the risks. A good example at the moment is NextJS. This is a React framework, with batteries included. It makes some choices for you, that may or may not be out of scope. For example, it has a very particular way of doing routing. However, because of the way they do routing, it is very easy to implement things that are generally very hard, like Server Side Rendering (SSR), Static Site Generation (SSG) and Incremental Static Regeneration (ISR). So, When something has very clear benefits, it may be worth it, even though it might limit you in the future.

So, I hope the next time you have an opinion, you will remember this article and take some time to think about the long term implications of your choices! The developers who come after you, will appreciate the effort. 😉

Some additional tips:

  • Allow configuration and customization
  • Use dependency injection
  • Document limitations and trade-offs of solutions
  • Prioritize simplicity and modularity
  • Support extensions and plugins

Comments 5 total

  • Jean-Michel 🕵🏻‍♂️ Fayard
    Jean-Michel 🕵🏻‍♂️ FayardJun 2, 2023

    I hope we can all agree at this point, that the choice for a web browser should by outside of the scope of an operating system (I'm looking at you MacOS/Safari).

    Side note: that's even a legal requirement imposed by the European Union.
    In general, all the antitrust legislation already exists, because we used to uderstand thanks to the robber barons that monopolies are bad.
    Somehow we forgot the lesson in the Reagan area
    Fast forward today and the tech robber barons can have their monopolies.
    For now.
    Because the legislation is still here

  • Dan Strokirk
    Dan StrokirkJun 2, 2023

    All my experience has taught me to use dependency injection as little as possible to solve your problem, and wait until you have a clear need to expand the API. This is not to say that all composition, configuration and customization are bad, but it can really hurt the code readability and maintainability when done carelessly.

    • Rense Bakker
      Rense BakkerJun 3, 2023

      Well yes I would not recommend to do anything carelessly! 😁 And you are right, you should not spend too much time building for future features that might never be requested. When you write code that is going to be used by other developers though, like when you're the author of a library or framework or a set of utils that's only used internally, it might be a good idea to keep extensibility in mind. (If you want to save yourself a lot of work in the future)

      • Dan Strokirk
        Dan StrokirkJun 7, 2023

        For sure - in library code it's much more common to have a good need for some sort of dependency injection. Especially since library code by necessity also needs to be a bit more rigid and change less than application code.

        Whenever I do apply DI solutions, I like your approach in the article - keep good defaults! The defaults both serve as a simplified interface for the user, and as documentation for whoever has to read and debug the code in the future.

  • Freddy Hidalgo-Monchez
    Freddy Hidalgo-MonchezJun 7, 2023

    What I enjoy the most is when frameworks or platforms give me a "paved road" approach, meaning there's a default, opinionated yet simple-to-use way of performing a common action (say logging). But then I can also easily swap that part with my own custom function later down the road. I feel a good example of this is .NET Core which allows lots of customization while providing good default options.

Add comment