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.
Benefits: What makes it a hero!
Reusability offers several benefits in the programming world, including:
- Eliminates code duplication: By reusing code, we can avoid having duplicate code in our codebase.
- Single source of truth: Since the same code is used in multiple places, we have a central location to check and fix any errors.
- 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>
);
}
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
}
}
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,
}
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>
);
}
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')
});
// 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')
});
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)