What Is Software Rigidity?

The deeper we penetrate the realm of ‘The Code Smells’ — common symptoms afflicting predominantly older software systems — the worse the stench of putrefying software will get. However, smelly code is not always ancient — I also see programmers bake problems into greenfield projects.

Last time we examined Software Fragility, its causes and symptoms. Today we’ll peek under the hood of Software Rigidity. What does it mean for software to be rigid? Why is rigidity problematic?

Rigidity

In simple terms, software rigidity refers to source code being hard to change — meaning we must make many code changes to adjust the behaviour of the software. The opposite of rigidity is flexibility. Flexible software allows us to change its behaviour with few code changes.

For example, in a rigid system, we may need to modify 57 separate code locations to produce a new behaviour for system users. A similar, more flexible program may require only two line changes to generate the same result.

Not only is the rigid system inefficient — since altering a codebase 57 times takes longer than one or two changes — but such extensive churn-up amasses a significant risk of accidental system breakage, i.e. bugs.

Rigid systems behave more like hardware than software. Most hardware is exceedingly difficult to change. Yet software should be easy to change, as its name suggests.

Rigid code resists change.

Rigidity in Pictures

Say, we want to write a new feature (or other change that will help the system’s users) into an existing application.

The following image conceptually represents this situation: We will integrate the new feature with our existing system, which the images will depict as a ‘tacking on’ action. The degree to which we must change the application code to incorporate the new feature represents the degree of Rigidity.

A new feature is to be integrated into an existing software system.

When the degree of rigidity is high, many application code modifications are required to produce a new feature:

The new feature has been integrated with the existing system. The system possesses aspects of rigidity as many code changes needed to be made to integrate the new feature. We have a couple of bugs that result from the changes.

Compared to a rigid application, a more flexible system will require fewer code changes to produce the feature:

This system is more flexible (or less rigid). Fewer changes to the application code are needed to integrate the new feature than was necessary with the rigid system.

Ideally, we would only write new code and make no modifications to the existing system at all — the system is pluggable concerning the new feature or requirement:

The theoretical ideal — a pluggable system (with regard to the new feature, at least). No code changes are required in the application code to accommodate the new feature. Nonetheless, entirely new code will still have to have been written to effect this integration.

It may not always make sense to produce pluggable systems since it would require us to have perfect foresight of our system user’s future needs. Nonetheless, in many instances, we can anticipate and design for changes in user requirements.

For example, instead of engineering the code to send an email to a customer when they create an order, we could instead send them a less specific message and polymorphically switch the type of message they should receive, an email or SMS/txt message:

public interface ICustomerNotifier
{
void SendNewOrderMessage(Customer customer, Order order);
// ...
}

and

public class CustomerEmailer : ICustomerNotifier 
{
public void SendNewOrderMessage(Customer customer, Order order)
{
// Send the customer an email about the new order.
// ...
}
// ...
}

as well as

public class CustomerSmsSender : ICustomerNotifier 
{
public void SendNewOrderMessage(Customer customer, Order order)
{
// Send the customer a SMS/Txt message about the new order.
// ...
}
// ...
}

Whenever it makes sense to do so — i.e. when we have identified a possible ‘axis of change’ — a part of the system where we may want to alter the behaviour without changing the code in that place, we should architecturally leave ourselves the wriggle-room to do so.

The Cost of Rigidity

When an entire squad of software developers takes more than 12 months to replace a database implementation with another in a medium-sized line-of-business application, in all likelihood, we’re observing the enormous cost associated with software rigidity. A review of the software system’s source code may reveal that the implementation-specific data access is strongly coupled to business and presentation logic.

A better, more flexible design and implementation might have seen the same project completed in a fraction of the time, say, two engineers for a couple of months.

Rigidity & Fragility

Almost always, rigidity arises in older systems, and fragility joins in as a companion complicating factor. These two horsemen of the software apocalypse cause havoc and destruction in legacy systems to the point that programmers are reluctant to change such systems for fear they break multiple expected behaviours.

Here is the image of the same rigid system, but now the system is also affected by significant brittleness. All hell is breaking loose — getting the feature into the system causes a cascade of bugs throughout the application. It’s no wonder software developers dislike working with rigid, fragile software. Rigidity and fragility together are much worse than either of them alone:

The tragedy of the rigid and fragile system. We must make many changes to integrate the new feature (rigidity), and those changes have a tendency to produce bugs within the wider system (fragility).

Mitigation Strategies

All right, Software Rigidity is a real problem — especially when paired with fragility, as it often is. So, what can we do to reduce rigidity?

Unfortunately, this question does not possess a straightforward answer. This entire blog aims to impart knowledge on how to engineer flexible, low-complexity software systems — and there are now over 200 articles. Excelling in software engineering is not something that happens overnight. It takes years of experience, making mistakes and learning from them. In this way, software engineering is no different from other engineering disciplines.

If I had to put my finger on a winning formula, it would be simply this:

To recognise that the most maintainable, clean software is built using engineering principles.

It’s no different from how other engineers design skyscrapers, planes, computers or even toasters — with care, planning, iteratively and continuous learning.

It should be the same with us, the software engineers. To live up to the engineering ideal, we want to avoid writing software in the traditional way, where we type out lines of code as we think of them, run the program and debug. That’s not engineering — that’s trial and error. This approach to writing software — a world away from engineering — produces rigid and fragile systems that land us in the legacy code mess we all too often find ourselves in now.

Here is my best set of articles on how to build flexible systems:

If you enjoyed this article, please leave a few claps. Thank you very much.

Join my email list to fast-track your software engineering career.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store