Life is rewarding me with some tranquil moments, and they allow me to reflect on the thinking that went into creating Xsemble. The component isolation is a very important desirable but existing technologies cannot achieve that due to the implicit coupling that exists within the components. The way Xsemble addresses this concern has a lot to do about the unique concepts it has evolved.
This is a 2 part article. In this first part, we see the problem and its repercussions, and then what benefits we could see if there were a way to address them. In the second part, we shall cover how Xsemble addresses it.
The Problem: Component Coupling
We use technologies that promised us modularity and componentization. The known challenges with code reusability are covered in a March 2021 article by Miquel Canal. Over and above those, as we shall see, fundamentally the components are coupled with one another, and the intertwining makes it difficult to write truly modular applications. Feel free to read on how, or skip to the next session if this is as clear as sunlight to you.
Understanding the Problem at Method Call Level
The coupling can be seen with the example of a simple method call. Consider the following method. (It happens to be written in Java, but that is inconsequential to the discussion, as it could have any other programming language as well.)
public String sayHello(String firstname, String lastname) {
return "Hello " + firstname + ' ' + lastname;
}
To call this method, which information would the caller need? It needs the method signature, or in other words:
- The exact name of the method: sayHello
- The data types and the semantic meaning of each of the arguments — as identified by their position: that there are two arguments of data type String, the first argument has to represent the first name and the second argument has to represent the last name
The coupling is a result of these restrictions, which mandate a change to one side if the other side changes. For instance, the calling code must change if:
- the order of the arguments were reversed, or
- the name of the method changes to “greet” instead of “sayHello”
Thus, we see 3 types of couplings here, between the caller and the callee:
- Name coupling: Coupling on the method name “sayHello”
- Data type coupling: Coupling on the data type “String”
- Positional coupling: Coupling on the position of the arguments
“Named arguments” introduced in PHP8 may be argued as a robust way to guard against the argument ordering change. So, if this were a PHP8 method, both of the following calls would be correct:
$s = sayHello( "firstname" : "John", "lastname" : "Wiley" ); // correct
$s = sayHello( "lastname" : "Wiley", "firstname" : "John" ); // correct
However, on careful observation one can see that, while the positional coupling is removed, another coupling based on the argument name got introduced here. For example, if the ‘firstname’ argument gets renamed to ‘fname’, the calling code must change accordingly.
Realizing the Problem at Component Level
The component technologies — COM, DCOM, CORBA, Web services and many others — use the same method calling construct, some times remotely. The same kind of coupling therefore exists between the caller and the callee. This is the reason why these component technologies cannot rid you of the component coupling problem. In other words, you cannot achieve component isolation.
That is the reason why fixing the interface (the API) is a big deal while using these technologies. Once the interfaces are fixed, then different teams can work on different pieces independently.
Identifying the Problem in HTTP Calls
Another, perhaps a little less obvious place to look for such kind of coupling is when a web page displayed on the browser makes an HTTP call to the server. Let’s look at the familiar HTML form:
<form action='/hello'>
<input type='text' name='firstname'>
<input type='text' name='lastname'>
...
</form>
With the URL ‘/hello’ this call is coupled with some other piece of code which declares the URL mapping of exactly the same string. The coupling is further evident in the names ‘firstname’ and ‘lastname’ because these are the exact ones being used while processing the request on the server side that the page is compelled to use.
Our argument about coupling is not specific to page calls making HTTP calls. It is exactly the same in case of mobile apps making HTTP calls, and it can be further generalized to other types of calls beyond HTTP, such as web services.
Thus, the component coupling mechanisms are not isolated to just some parts — they are omnipresent in all the coding that we do.
The Repercussions of Component Coupling
After seeing how rampant component coupling is, it is easy to see that it is the main villain playing a role in almost all the obstacles that come in the way of writing good, durable software.
But before going there, let’s acknowledge that tools make the job easy in some cases. For example, a modern IDE can easily catch a method name mismatch (even before it goes to a compiler in case of a compiled language, in which case the compiler would catch it). But there are limitations though. It would not catch the reversal of order between the first name and last name. Also, in case of remote calls, the problem becomes harder and you cannot depend on the tool support.
OK, here we go with the problems.
- The lack of component isolation makes it difficult to create a clean WBS and work in silos. We need all team members to sit together and interact with each other with minimum impediments, and pray(!) that they sort out such mismatches through this interaction. This is not foolproof, and anyway very hard when teams are remote or where team members are working from home.
- It also means that your codebase will not be as robust as you like. Sooner or later, errors because of such mismatches will surface. Here, one must note that strongly typed compiled programming languages (such as Java, C#, C, C++, Kotlin, Scala etc) are a safer bet than the dynamically typed or loose typed programming languages (such as Python, PHP, Javascript, Ruby), as they catch some mismatches automatically. There is no wonder that the former set of languages rule the enterprise software game.
- The code reuse takes a hit. Alternative implementations from different sources are not expected to implement the same signature, and you will need to write different lines of code to call them differently. For example, if my friend gave me a better implementation of sayHello method with the name greet, then I’ll have to write another caller for the same. This is applicable to other environments also, for instance, while trying to use alternative services provided by Amazon AWS and Google cloud.
- Enhancing your application is difficult, time consuming and high risk. With the spaghetti of the intermingled components, it is hard to figure out where to start. A developer often loses their way in the spaghetti of such codebase.
- Maintaining your application is difficult for the same reason. Especially given that maintenance is needed over a long period and work changes hands, the new team members find it especially difficult to navigate through the mess of intertwined components.
- As we saw before, having the interface defined right becomes crucial, and once defined it needs to be treated as rigid. It is reminiscent of how the Waterfall methodology made it crucial to have an air-tight requirement specification. Just like in that case, creating a change-proof API specification is tedious, consumes disproportionately high resources, and still practically never happens. As we move towards more complex applications, the number of interfaces you have to manage skyrockets, and it is not trivial to keep track of all the changes all the time — even with the best tools.
- That brings us to the manpower issue. Managing this reasonably well over time needs highly mature, skilled and disciplined people… people who can code with a cool head even after having a heated argument with their spouses. Needless to say, such people are extremely rare, and companies keep pushing their HR departments to recruit the best ones for them. Can they all get them?
In conclusion, the object oriented / component technologies have not helped us to completely get rid of the spaghetti-like mess that they set out to eliminate. There is no wonder that writing a reliable software is hard. The rate of failure is high and it further increases with the size of the software. To get a feel of how staggering corresponding costs are, one may refer to the CPSQ reports.
Benefits of Achieving Component Isolation
If we had a way to address the component coupling problem to make them decoupled and isolated, we could do all the following:
- Let programmers work from home to deliver components
- Get a good quality software even with junior / low-skilled programmers, as any problems will stay isolated within the components
- Write large programs easily, as looking at it as a combination of many small programs, which are themselves made up from combining tiny components
- You could save a lot of effort writing almost the same code again and again, because you could reuse code better
- Enhance your applications easily, by isolating your changes to fewer components
- Maintain your applications easily, with new team members finding it easy to deal with isolated components
- Easily pull apart existing legacy applications and retrofit for more benefits (such as increasing scalability by breaking it into microservices or ease of administration by porting it to a cloud)
In the next part of this article, we shall cover how Xsemble addresses the component coupling issue, and the specific provisions it has to address it. Feel free to leave your comments below in the comment box. I might refer to some good ones in the next part.
Attributions: Featured image cropped from original one by Uriel Shuraki from Pixabay