Mastering Component-Driven Development: Improving Maintainability and Collaborative Efficiency

Or: How to define the boundaries of an independent Bit Component in your distributed codebase

Eden Ella
Bits and Pieces

--

The question of what should be an independent component, or how we should define the boundaries of a component, comes up quite often when developers start using Bit to implement a component-based architecture.

In case you happened to overlook Bit, Bit is an open-source toolchain that helps developers build component-based applications and collaborate on components independent of a git repo or any other development setup.

As you’ll see, the same reasons for choosing a component-based architecture should serve as the criteria for defining the boundaries of a component.

Codebase Maintainability

A maintainable codebase is one that can be easily understood, modified, and extended by developers over time. Several factors contribute to making a codebase maintainable. We’ll focus on two: Readability and Reusability.

1. Readability

Bit Components, which form your distributed codebase, are small decoupled units of code that are easy to understand and reason about.

A component has an ID that is meaningful and descriptive. A component’s relation to other components, dependencies and dependents, is clear and explicit.

The dependency graph of a component-based app

Changes to a component are isolated and easy to review. The effects of a change made to one component are easy to predict.

Changes propagate from one Bit Component to its dependents

To enjoy these benefits to the fullest, it’s important to keep components small and focused. A component should generally be a small group of files, no more than a few hundred lines of code, and with a single responsibility.

A component should “do what it says on the tin”, and nothing else.

Bit components in a demo scope

For example, the ‘netlify-deployer’ component should only handle the deployment of app components to Netlify. It should not, for instance, generate the deployment artifacts.

Following descriptive naming conventions and separation of concerns are essential to get the most out of this approach. It allows you to inspect dependencies and dependents, and understand the relationship between components without having to read the components’ code. This translates to a faster and more efficient development process.

2. Reusability

Reusing existing code, existing components, can reduce complexity and improve maintainability by leveraging tested and proven solutions.

Imagine a world where buildings and bridges fail (collapse) as often as software applications do. This, fortunately, does not happen. The reason is that physical structures are designed and built once, using proven and tested solutions. They are not modified and extended on the fly, as software is.

We can’t stop modifying and improving our software systems, but we can still learn from the way physical structures are designed and built.

The more you reuse components, the more you use “proven solutions”, the more stable and maintainable your codebase will be.

A reusable component is one that can be used in different contexts by different components or applications. It should be extensible and configurable to allow for different use cases.

It should also abide by the Single Responsibility Principle and not include any code irrelevant to its purpose.

On the other hand, a component should not be “too focused” that it can only be used in tandem with another specific component. For example, a UI ‘table-row’ component used to render a single row in a table will not be used by itself but rather as part of a specific ‘table’ component.

If separate components are frequently used together, so much so that they are virtually inseparable, they should make one component. This will simplify maintenance as it will require handling just one component instead of several to update a single feature.

Note: In contrast to traditional monolithic projects, components in component-based software are not only used as “libraries” but rather make up the entire application, from the very elementary and generic building blocks to the most complex and concrete components.

That means “reusability” should never be the only criterion for defining the boundaries of a component.

Independent delivery and team autonomy

The component-based architecture allows individual developers and teams to author, maintain and deliver components in parallel, independently of each other.

Each team hosts its components in a Bit scope with specific permissions. This guarantees harmonious, effective collaboration and prevents conflicts.

Scopes in a demo Bit organization

A component, excluding its dependencies, should be authored and maintained by a single team and, correspondingly, a single Bit scope.

For example, a ‘sign-up-form’ component should probably not include the implementation for the ‘terms-and-conditions’ section in its source files.

Instead, this section should be maintained as an independent component by the ‘legal’ team/scope of the organization and consumed as a dependency by the team/scope that maintains the ‘sign-up-form’ component.

The ‘sign-up-form’ component uses the ‘terms-and-conditions’ component, maintained by the legal team.

Conclusion

In conclusion, adopting a component-based architecture and utilizing Bit can significantly improve your development process's maintainability, reusability, and team autonomy.

By keeping components small and focused, adhering to the Single Responsibility Principle, and enabling independent delivery, you can optimize your codebase for both readability and reusability.

This approach encourages collaboration, reduces complexity, and results in a more efficient development process. Embracing component-driven development will help your team create stable, maintainable, and extensible software systems that can be easily understood, modified, and extended over time.

Find out more:

--

--