In Defence of Simplicity
In his essay "A Complexity Measure", published in December 1976, Thomas J.Mccabe makes the following opening remark:
There is a critical question facing software engineering today: how to modularize a software system so the resulting modules are both testable and maintainable? That the issues of testability and maintainability are important is borne out by the fact that we often spend half of the development time in testing and can spend most of our dollars maintaining systems.
It amazes me that his remark is still relevant today, almost 50 years after his invention of Cyclomatic Complexity. Writing new code is fun and easy. Making sure you can test it and maintain it for the long run is where things usually start to break.
Mccabe's idea was to quantify complexity in a way that will help software developers decide when their software has passed a certain point in complexity, from which it should be modularized.
Essentially, you'd start coding while eyeing this complexity metric, and when it hits a certain number (say 10), it's time to refactor your code into smaller modules.
His approach was to measure and control the numbers of paths through a program as a means to reduce complexity. But in my experience (and probably yours), the number of paths in a program is just one single aspect of complexity.
Complexity, as "...the state or quality of being intricate or complicated..." (definition from Oxford Languages) can arise from more than just the number of paths in a program.
For example:
- I noticed that projects which contain modules written in different programming languages (say, Java and Scala, or a mix of Python and Rust) are more complex to extend and maintain than projects written entirely in a single language.
- Usage of 3rd party libraries - while increasing productivity in the short run - sometimes hinder progress at later stages because of the added (hidden) complexity of a black-box in the project. Compatibility is a beast waiting to lift it's ugly head.
- A well tested project (unit-tests, integration-tests, simulators) checks the assumptions about the behavior of its modules - but now developers need to deal with with the project's test code in addition to the project's code to make sense of it. And we know less is more when it comes to the number of LOC...
- Using abstract data-types, polymorphism and generalizations reduce the duplication of information in a program, but the number of times I had to peel layers of single line classes to get to the real single line of actual useful code caused me to question some life choices I made.
- Naming variables can do so much to increase readability of code, allowing a developer to make sense of code even without too much context. And we know that Context is for Kings.
{
char *end;
if (!arg)
return -EINVAL;
elfcorehdr_addr = memparse(arg, &end);
if (*end == '@') {
elfcorehdr_size = elfcorehdr_addr;
elfcorehdr_addr = memparse(end + 1, &end);
}
return end > arg ? 0 : -EINVAL;
}
Fools ignore complexity; pragmatists suffer it; experts avoid it; geniuses remove it.
Comments
Post a Comment