The Pragmatic Programmer EP.6 — Upgrading Your Application

Natnicha R.
CodeX
Published in
7 min readMay 27, 2022
Decoupling network by Brett Sayles

Decoupling

When designing software, there is a high possibility of change due to a variety of factors e.g. requirement changes, features added, and so on. So, we need flexibility in our software. The authors said

And, to make matters worse, coupling is transitive: if A is coupled to B and C, and B is coupled to M and N, and C to X and Y, then A is actually coupled to B, C, M, N, X, and Y.

This means there’s a simple principle you should follow:

Decoupled Code Is Easier to Change — The Pragmatic Programmer

Characteristics of coupling software are:

  • importing some libraries that are unrelated to your code
  • changing something impact or break unrelated ones
  • developers are afraid to make changes because they don’t sure what will be affected.
  • brainstorming all team members to find solutions because no one surely knows about that.

These might cause these code behaviors:

Chain calling

You may see codes like this: always call a chain function to get what you want.

Thanks to Pragmatic Programmer for this example

But, as pragmatic programmers, we trust on

Tell, Don’t Ask — The Pragmatic Programmer

That means the function should return the result, do not ask or call a function for the result, like this:

Thanks to Pragmatic Programmer for this example

Global Data

Due to accessibilities from each and every function, changing global data always impact all those functions. As a result, avoid establishing global data. Remember, we are talking about global data, not global variables. Let’s imagine when we store data in global variables, once your business team decides to call your service in threads, or your application is called by more than 1 request at a time. While thread-1 stores values in the application, thread-2 is trigged and replaces them with new data. What happens when Thread-1 tries to access their data after that? Therefore, if you have enough reason to create global data, don’t forget to wrap it into an API.

Inheritance

You might be surprised to learn that inheritance can be a cause of code coupling. This is due to the fact that inherited classes are always dependent on their parent classes. If their parents change a class structure, the child classes are always affected with no warning. It might be broken during runtime. We will discuss this point again in the next section.

Juggling the Real World

In recent decades, an event generates the data util become big data. So, we can categorize it into four categories to make your application reach high productivity.

Finite State Machines (FSM)

A state machine is a basic concept to handle an event. This concept consists of a set of states, one of which is the current state. For each state, the only event that reaches the defined criteria can move forward to the next state.

Diagram by wikipedia

Turning on to the development part, there is an available library to implement a finite state machine, for example, XState. You may find yourself are writing an FSM every day because it just a simple idea.

The Observer Pattern

This pattern has an event source known as the observable, and a list of clients known as the observers who are interested in those events.

Nowadays we may pervasively see this pattern, an observer registers the interested observable. When an event occurs, the observable lists out of all registers and call to each of them to pass agreed values. See the below example here:

However, the observer pattern has a problem: because each of the observers has to register with the observable, it introduces coupling. In addition, because in the typical implementation the callbacks are handled inline by the observable, synchronously, it can introduce performance bottlenecks. — The Pragmatic Programmer

Therefore, the authors propose the next strategy which can solve this problem called Publish-Subscribe (pubsub).

Publish-Subscribe (pub-sub)

Publish-Subscribe or pub-sub can solve the problem that the observer pattern faces which are code decoupling and performance bottlenecks.

In the pub-sub pattern, subscribers have to subscribe to the publishers through the channels which separately implement to a specified domain. Sometimes, their background may or may not be served by the same process and infrastructure. Unlike, the observer pattern, it centralizes all things into one channel. That’s why pub-sub can solve the observer pattern. That means that subscribers have to independently subscribe to each channel.

Pubsub is a good technology for decoupling the handling of asynchronous events. It allows code to be added and replaced, potentially while the application is running, without altering existing code. The downside is that it can be hard to see what is going on in a system that uses pubsub heavily — The Pragmatic Programmer

Diagram by Luc Juggery

Reactive Programming and Streams

Reactive programming is a concept that once we assign a formula to a cell that refers to another cell, the referred cell is updated with a new value, then the referrer cell prompt updates its value. For example, spreadsheet in Google Sheet.

It’s clearly seen that all events can be used to trigger reactions, but it isn’t easy to update in real-time. That’s why streams come into this pattern.

Image by Thenextweb

Transforming Programming

All applications have the same objective that is they aim to convert an input into an output. For most of the designing part, we focus on architecture and structure designs. Apart from that, we worry about data structures and algorithms, languages, and frameworks. We rarely think about creating transformations. Let’s see the example of this command and think about how it works, step by step.

Thanks to Pragmatic Programmer for this example

Here is the result:

first — list all files
second — count the line number
third — sort it with the number
finally — grep it only highest 5 records

The simplest method for locating transformations is to start with the requirement and determine its inputs and outputs.

Programming Is About Code, But Programs Are About Data

Thinking of code as a series of (nested) transformations can be a liberating approach to programming. It takes a while to get used to, but once you’ve developed the habit you’ll find your code becomes cleaner, your functions shorter, and your designs flatter. — The Pragmatic Programmer

Inheritance Tax

Let’s move back to inheritance, inheritance is a concept of Object-Oriented Programming (OOP) that

allows programmers to create classes that are built upon existing classes, to specify a new implementation while maintaining the same behaviors (realizing an interface), to reuse code and to independently extend original software via public classes and interfaces [wikipedia:inheritance].

Informal words, an existing class is called “Parent Class” and an inherited class is called “Child Class”. As I said in the previous section, decoupling, the inheritance comes with code decoupling. Because this concept allows for sharing code between parents and child classes. Think about when the parent class changes the logic in their method without changing the function name. In some cases, the good news is that developers intend to change both all parent and child classes. But, in the real world, the developers may don’t aware that which functions are using this class. Other functions, that are using this class but it’s no need to change the logic in that method, have been affected. As a result, the application may respond incorrectly result or crash in the worst case.

To solve this problem, apply these below:

Interfaces and protocols

In Java, the interface concept allows developers to declare method names and arguments without implementation. Inherited classes of this class are forced to implement those declared methods. So, when the parent class is changed a method name, all child classes are forced to review and change too.

Delegation

The authors said,

If a parent class has 20 methods, and the subclass wants to make use of just two of them, its objects will still have the other 18 just lying around and callable. The class has lost control of its interface. This is a common problem — The Pragmatic Programmer

It’s time to delegate to a persistence class’s API, for example:

Mixins and traits

The authors mention mixins to solve this problem with a simple concept, that is

we want to be able to extend classes and objects with new functionality without using inheritance. So we create a set of these functions, give that set a name, and then somehow extend a class or object with them. At that point, you’ve created a new class or object that combines the capabilities of the original and all its mixins. In most cases, you’ll be able to make this extension even if you don’t have access to the source code of the class you’re extending. — The Pragmatic Programmer

Here is an example of applying mixins

Thanks to Pragmatic Programmer for this example

Configuration

If you are familiar with the configuration in your application, it means that you put all things that may change in different environments into the configuration. Bing go! You have passed this test. Keeping those values externally makes it easy to configure your application, for example, database credential, port, path, URL, external parameters (e.g. interest rate), license key, and so on. Not only let your life easily deploy, but also let your configuration securely.

Please note that all examples shown above are samples to only emphasize understanding the concept. It may be simplified to comprehend, not correctly by the language syntax.

Thanks to The Pragmatic Programming book for the above knowledge, if you are interested in reading this book, click here for more detail.

--

--

Natnicha R.
CodeX
Writer for

Software Engineer, Backend Designer, Algorithm Developer