Make it reusable after you've validated the need
Increase quality and speed of development by creating reusable components and patterns and leveraging them often. Don't stress reusability or perfection before validating the need.
Overview
Modern software stands on the shoulders of giants. We're able to deliver systems today that would have been unthinkable a few decades ago because we can build on top of frameworks, libraries, and tools created by others, allowing us to focus on solving unique problems rather than reinventing the wheel.
Within our own codebases, we can increase both the quality and speed of development by keeping an eye out for opportunities to create reusable components. When there is a repeated need for a particular functionality, we should spend time to extract and modularize that functionality, creating building blocks that can be leveraged across the system.
On a higher level, this principle also applies to the architecture and design of our software - when we find a common pattern or solution that addresses a recurring problem, we should formalize it into a reusable approach. This not only saves time in future development but also promotes consistency and maintainability across the codebase.
How to do it
- Checked item: On the front-end, create reusable UI components for common elements like buttons, forms, and navigation menus. Use component libraries or frameworks that support modular design.
- Checked item: On the back-end, identify common services or utilities that can be abstracted into libraries or microservices. This could include authentication, logging, data access layers.
- Checked item: Use design patterns that promote reusability, such as factory patterns, strategy patterns, or dependency injection.
- Checked item: Leverage package managers and repositories to share reusable components within your organization.
- Checked item: Open-source reusable components when appropriate, contributing back to the community and benefiting from external contributions.
- Checked item: Document reusable components well - you want others to use them!
- Checked item: Provide thorough test coverage for reusable components - you want them to be reliable!
- Checked item: Encourage a culture of reuse within the team. Promote the idea that building reusable components is a valuable investment that pays off in the long run.
Details
Knowing when to invest
Not every piece of code that is reused should be turned into a reusable component. It's important to validate the need for reusability before investing time and effort into creating a reusable component.
Reusable components should be created when:
- There is a recurring need for a particular functionality across different parts of the system.
- The functionality is complex enough that re-implementing it multiple times is non-trivial.
- Inconsistencies in implementation across different parts of the system are causing maintenance challenges or bugs.
Balancing reusability and complexity
Over-engineering for reusability is a real danger. Don't fall down the rabbit hole of trying to cover every possible edge case. The component does not need to be perfect; it just needs to be good enough to solve the current use cases effectively, and it needs to provide enough test coverage that the current use case won't be broken by future changes.
The beauty of making a component reusable is that it can always be improved and extended later, and those changes will benefit every place the component is used. Start with the simplest implementation that meets the current needs, and iterate from there based on real-world usage and feedback.
Backward compatibility and the burden of change
The other risk of creating reusable components is that they can make future changes more difficult. As more parts of the system depend on a reusable component, fundamental changes can be difficult to make without impacting the existing use cases.
There are entire books written on refactoring and managing change in software systems, but here are a few key principles to keep in mind:
- Shoot for backward compatibility whenever possible. The best kind of change to a reusable component is one that doesn't require changes anywhere else.
- Lean on static typing (ie: Typescript, Python
typing, etc.) to help manage changes. Strongly typed systems can help catch potential issues at compile time rather than runtime. - If you don't already have unit test coverage for the reusable component, add it before making changes. This will help ensure that existing functionality is not broken by the changes.
- Make the change to the reusable component in a separate branch/PR. This allows you to test the changes to the existing functionality without adding on the complexity of the new implementation.
Publishing and versioning
Extracting a reusable component for use across multiple teams or projects introduces the challenge of managing versions and updates. This advice applies whether you're publishing the component to a public package registry (like npm or PyPI) or just sharing it internally within your organization.
Use semantic versioning to communicate the nature of changes to users of the component. Changes that will break existing implementations should be clearly indicated through major version increments.
Keep a changelog to document changes made in each version. This helps users understand what has changed and how it might impact their use of the component.
Have a plan for maintenance and support, and communicate it to potential consumers. Decide who will be responsible for managing updates, addressing issues, and responding to user feedback. Even if your plan is no more than "best effort, as time allows", saying that clearly helps set expectations.
Examples
- Extracting a bit of common utility code (eg: date formatting, data validation) into a single function or module that can be imported and used across different parts of the application.
- Creating a reusable Github Action that can be shared across multiple repositories to standardize CI/CD workflows.
- Building a component library for a design system that can be used across multiple web applications.
- Setting up a starter template repository that can be cloned to quickly bootstrap new projects with a consistent structure and configuration.
Help us improve
Your feedback helps us create better resources for teams like yours.
Last updated on