Component-Based Software Development

📖 3 minute read

Of the many ways that you can build software two are used most often: based on use cases (vertical slices of end-user functionality through the system) or layers (horizontal sets of related technical functionality). Either way, when done, you'll end up with a set of cooperating components that implement the system's business.

The key insight is this: to build a high-quality large system you need to start with high-quality components. Take high-quality components and assemble them into the finished system.

To build high-quality components start with code library writing principles: make your components highly cohesive units of code that are used through well-defined interfaces; ensure that they are independently tested and versioned. A better term for these code libraries would be software parts, since they can be of varied sizes.

A Structure for a Component-Based System

The term component is often used generically when we speak of a software part. It helps to standardize the language when talking about component-based development. Here is one that works well:

  1. Class is an Abstract Data Type (ADT) that extends the language's vocabulary. A class is still firmly in the computer language realm.
  2. Component is an abstractions from the problem domain. This is where the language of the system transitions from the computer domain to the problem domain.
  3. Module and subsystem represent composite abstractions from the problem domain.

Each part is built from about 5 to 10 subparts. The top part is the system. A system is built out of subsystems. Subsystems are built from modules. Modules are built from components, and components are built from classes. Here is a rule of thumb for sizing these parts:

  1. Class: 100 to 200 LOC
  2. Component: 1,000 to 2,000 LOC
  3. Module: 10,000 to 20,000 LOC
  4. Subsystem: 50,000 to 100,000 LOC
  5. System: 100,000 to 500,000 LOC

Following this method of sizing and naming allows you to build a moderate sized system of about half a million lines of code. If you need to build a larger system, then build it as a set of cooperating systems. Use these ideas as a starting set of terms and rules for your next component-based development effort.

Update on August 8, 2010

When I wrote the first version of this article, I wrote it from the perspective of a rather small team where each of us had to write more than one component that we assembled into a finished system. Therefore integration was assumed and obvious: I designed the component in a way to fit perfectly its intended use by another component that was also my work. Given that we were a small team, it was also self evident that whatever I design will have to work well when used by my teammate's component.

Our emphasis was on componentization. The need for integration was evident and obvious. After all, a system that doesn't integrate is no good. We were replacing the idea of building the system as a monolith with the idea of assembling the system from components.

Over the years I saw systems built out of components that don't come together, especially as team size grows. As a reminder, let's make sure that we heed Deming's admonition: "Why did the parts work together? Because they were designed to work together."

It is the responsibility of each component designer to work with his or her clients to ensure that the component they design will fit the intended use. And as Dijkstra advises us: "programs should be composed correctly, not just debugged into correctness."

Update on March 17, 2024

Building the system from vertical slices, or features, tends to be harder at first. Mostly because we are not used to working this way. The major benefit of this method is that each feature can be independently tested before being added to the system. Features can be small, a few classes, amounting to a component based on the terminology above, or they can be larger, the size of modules, or even subsystems.

A note on Lines of Code, or LOC: knowing the size of source code in lines of code is relevant for roughly figuring out how long will it take to read that code. When someone needs to work on any code they will have read it. If a software part has many lines of code, it will take longer to read through it, and then decide where to look to fix a defect, or to add a new capability. Knowing the size of code is also helpful in figuring out the defect density of a given part. Knowing the defect density helps in making repair or replace desicisions. Using LOC beyond this scope is not advised, however well intentioned.

References

  1. Deming, W. Edwards. The Deming Videotapes. MIT. Boston, MA. 1981-1983.
  2. Dijkstra, Edsger W. Turing Award Lecture. 1972.
  3. Farley, David. Modern Software Engineering. 2022.