I've been working as part of a product team for a while now and I've started to pick up on a pattern. It comes back to whether development is focused on building what I'm calling features or functions.
These terms are quite similar and are quite frequently used interchangeably in software engineering - they're very similar in definition.
Here I'll define them to avoid any doubt:
- Feature - part of the product that the user can use directly
- Function - a system component that can exist in isolation from any feature that uses it
When building a feature, a trade-off between reusability and extensibility exists whereby features that are developed entirely isolated from each other are infinitely extensible whereas features that share as many components as is technically possible are less extensible.
If a feature exists in total isolation, none of its components are used by other features. For example, this could be part of an app dedicated to listing blog posts that allows filtering by category and full-text search. If built in isolation, none of the other parts of the app would share any of the same components.
By nature, isolation lends itself to extensibility and so the app page in the example above could theoretically be changed in any way without impacting other parts of the app.
But this comes at a cost - building everything from scratch takes longer.
So naturally most systems are not built in this way and there are hundreds of ways to consider how to architect a system that I won't go into here.
But one thing that I've seen a lot first-hand is how the feature design process influences the boundaries of each component in a system.
Which brings me to an observation:
- feature-specific components are bound to a feature and therefore cannot be reused by other features
- function-specific components are used by a feature and can be reused by other features
By designing a system component to be explicitly bound to a feature, reuse is actively discouraged, whether it's a service, an API endpoint, a button in the UI or anything really. What this allows is for individual features to be changed frequently and without much consequence to other parts of the system (although by nature system components are never totally isolated).
This might be suitable for certain products that need to change frequently, or that have a short shelf life. But in the long-run features will be developed repeatedly that are similar in nature but differ in their components.
This can become costly - a lot of dev time that could be spent investing in components for the future is instead spent building components for today.
By designing a system component to be explicitly bound to a function, reuse is a fundamental consideration because by the definition above it can be used by new features in the future. The more features that can utilise a component, the more extensible the product is overall.
The thought process becomes more about separating the functional requirements of a feature from the feature itself - system components become functional requirements.
As an example, take a dashboard for monitoring a service that tracks employee workload by monitoring how many email threads they are active in in a single day. Let's suppose the product team wants to add a page to the dashboard that let's the user view a graph that shows the workload over time for a specific group of employees.
One way of delivering the feature is to create a new service dedicated to this page, have it expose an API that provides the data, which is stored in a new database called
group_workload_stats. The service updates the table over time and the API will return an updated view when that happens.
It's easy to see that none of the above can be reused as part of another feature.
To rethink this implementation, we could consider making each component function-specific.
For example, the new service could be dedicated to the function of returning workload data over time - something that is predictably reusable, something required by other features that are planned for the future. It's true that this restricts new features by constraining them to the functionality provided, but it is possible to build such
services with extensibility in mind.
In summary, I think there are a lot of benefits to thinking of system components in terms of whether they are feature-specific or function-specific. This in theory encourages features comprised of functional components, which in turn saves time in the long run.
In a later post I'd like to discuss how this relates to design patterns such as microservices and how some parts of the system must define the composition of functions in order to deliver features.