DEV Community

Alson Garbuja
Alson Garbuja

Posted on

Reusability: A magic potion or curse in disguise

Reusability is a common term in programming, known for its benefits. But is it really as helpful as people say, or is it just an overused concept? In this article, we’ll explore what reusability truly means and whether it’s the hero people claim or just another fancy over used software term.

What is Reusability?

Keeping it simple. Reusability in software engineering refers to the idea of creating components or functions that can be reused in the future. In simple terms reusability is like making a handy tool out of your broom in order to clean tight corners of your room then instead of taking it apart you keep it intact so that you can use it next time you clean your room.

Illustration demonstrating reusability with concept of same box used in multiple places

Benefits: What makes it a hero!

Reusability offers several benefits in the programming world, including:

  1. Eliminates code duplication: By reusing code, we can avoid having duplicate code in our codebase.
  2. Single source of truth: Since the same code is used in multiple places, we have a central location to check and fix any errors.
  3. Reduces code size: Along with cutting down on duplicate code, reusability also helps minimize the overall number of lines in the codebase.

In addition to these, there are many other advantages we can gain by applying the concept of reusability in our projects.

Examples of applying reusability

Now, let's look at some examples of how to apply the concept of reusability. For this demonstration, I'll be using the React library, though this concept can be applied to any programming language.

Reusable component/Widget/UI

export default function PhoneTile({ name, avatar, number }) {
  return (
    <div className="px-2 py-4 rounded-md bg-white shadow-md text-black">
      <div className="flex gap-4 items-center">
        <img src={avatar} alt={`profile pic of ${name}`} className="w-12 h-12 rounded-full object-cover" />
        <a href={`tel:${number}`}>{name}</a>
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

A react component showing user name and their avatar

In the example above, we've created a PhoneTile component, which can be placed in a separate file and reused across different parts of the project. To use it in various components, all you need to do is pass the necessary props, which are name, number, and avatar.

Reusing functions

export const apiCaller = async (url, method, payload) => {
  try {
    let options = {
      method,
      headers: {
        "Content-Type": "application/json",
      },
    };

    if (payload) options[body] = JSON.stringify(payload);

    const res = await fetch(url, options);
    const data = await res.json();

    if (!res.ok) throw new Error(data.message); // considering your error is present in the message key
    return data;
  } catch (error) {
    console.error(error); // Or you can throw it and handle it elsewhere
  }
}
Enter fullscreen mode Exit fullscreen mode

We've created a function called apiCaller that takes a url, method, and an optional payload to fetch data or log errors using the fetch API. By defining this function as a separate global function, we can reuse it throughout our project for various tasks, like fetching user data, updating user information, and more.

Reusable constants

enum Direction {
  Up = 1,
  Down,
  Left,
  Right,
}
Enter fullscreen mode Exit fullscreen mode

We can also apply reusability when defining constants in our program. For instance, I've created an enum called Direction in TypeScript, with four values ranging from 1 to 4 (Note: the values can be any numbers we specify). Instead of using the number values which are difficult to remember, we can use the more inuitive and understandable labels Up, Down, Left and Right. These values can then be reused anywhere in our codebase, improving readability and reducing errors.

Potential danger lurking behind the scenes

So far, we’ve discussed the positive aspects of reusability, and it’s clear that it offers many advantages for writing cleaner and more efficient code. However, what many, especially beginners, may not realize is that reusability also comes with certain disadvantages.

Let's look at those disadvantages one by one.

  • Correctly spelling reusability: The frustration i had while attempting to spell reusability and instead typed resuability is unmatched. Just joking here, jokes apart below are the real disadvantages listed.

Over reuse

While reusability is excellent for reducing code and making it cleaner, it doesn’t automatically guarantee maintainable code. Returning to our analogy of the broom tool—which we created to clean tight corners efficiently—if we try to use the same tool to clean regular floors or rooftops, it could lead to issues.

Similarly, if we use the PhoneTile component to represent a contact folder by adding optional isFolder and onClick props, and also reuse it to display an email list, we might start facing problems. While reusability is great, stretching a component beyond its original purpose can make the code harder to maintain and understand, potentially leading to confusion and bugs.

export default function PhoneTile({ name, avatar, number, isFolder, mail, onClick }) {
  return (
    <div className="px-2 py-4 rounded-md bg-white shadow-md text-black">
      <div className="flex gap-4 items-center" onClick={onClick ?? () => {}}>
        {isFolder ? <FolderIcon /> : <img src={avatar} alt={`profile pic of ${name}`} className="w-12 h-12 rounded-full object-cover" />}
        <a href={number?`tel:${number}`:mail?`mailto:${mail}`:"#"}>{name}</a>
        {mail && <MailIcon />}
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

list of react components showing user name and their avatar

As we can see, the code begins to get messy. While we may reduce code duplication and write less code, the overall developer experience deteriorates. It becomes much harder to maintain and manage this kind of code over time, leading to increased complexity and confusion.

Difficulty in testing

Another problem that will surface up when over reusing or not correclty implementing reusability is the difficulty while testing.

Unit testing is a crucial step in the software development process, and used to test different components or functions in a unit level. Hence excessive reliance on a single component to accommodate various functionality can make it nearly impossible to test each case separately

Here is an example of how the unit test for well-designed component and poorly created reusable component might look like. (NOTE: I am using jest along with testing library)

// Proper reused component unit testing
test('Check if phonetile shows correct info', () => {
  render(<PhoneTile name="Alson" number={1234567890} avatar="https://api.dicebear.com/9.x/bottts-neutral/svg?seed=Alson" />)
  expect(screen.getByRole('a')).toHaveTextContent('1234567890')
});
Enter fullscreen mode Exit fullscreen mode
// Poorly reused component unit testing
test('Check if phonetile shows correct info', () => {
  render(<PhoneTile name="Alson" number={1234567890} avatar="https://avatar.com/alson.png" />)
  expect(screen.getByRole('a')).toHaveTextContent('1234567890')

  // We have to test for different types again which defeats the purpose of unit testing
  render(<PhoneTile name="Families" isFolder={true} onClick={navigateToFamiliesList} />)
  expect(screen.getByRole('a')).toHaveTextContent('Families')
});

Enter fullscreen mode Exit fullscreen mode

Conclusion

Reusability, like many software concepts, is neither an absolute hero nor a complete villain. Its effectiveness largely depends on how developers implement and leverage it. Ultimately, reusability can be a powerful asset or a source of complexity, based on the choices made during development.

Here are some of the questions and steps for beginners to filter out if they need to make any component/function/constant or block of code reusable or not.

  • Do not over complicate the reusable blocks of code by over reusing (over engineering) it. Have one block of code handle only one function.
  • Reuse any component/function/constants if and only if, it is needed to be reused again in an Idompotent manner.
  • Divide and conqure can be applied in cases where we need to reuse some block of codes for different functionality. By futher dividing parts of reused codes into smaller reusable codes.

Top comments (0)