Observability Done Right: Best Practices and Anti-Patterns for Effective System Monitoring

Image
  WHAT Observability is a concept that refers to the ability to gain insights into the behavior and performance of complex systems. In the context of software engineering, observability involves the collection, analysis, and visualization of data from software applications, infrastructure, and other components of a system. In the animal kingdom, observability plays a critical role in survival, allowing animals to monitor their surroundings, detect threats, and find food. Dolphins use echolocation to observe their surroundings. They emit high-frequency sounds that bounce off objects, allowing them to create a 3D map of their environment. Thanks for reading Knowledge Cafe! Subscribe for free to receive new posts and support my work. Subscribed WHY In today's era, architectures are becoming increasingly large, complex, and fast-paced due to the faster development and deployment of software by distributed teams with the help of DevOps, continuous delivery, and agile development methodo...

Inheritance versus Composition

The two most common techniques for reusing functionality in object-oriented systems are class inheritance and object composition. Class inheritance lets you define the implementation of one class in terms of another's. Reuse by subclassing is often referred to as white-box reuse. The term "whitebox" refers to visibility: With inheritance, the internals of parent classes are often visible to subclasses.


Object composition is an alternative to class inheritance. Here, new functionality is obtained by assembling or composing objects to get functionality that is more complex. Object composition requires that the objects being composed have well-defined interfaces. This style of reuse is called black-box reuse, because no internal details of objects are visible. Objects appear only as "black boxes."


Inheritance and composition each have their advantages and disadvantages. Class inheritance is defined statically at compile-time and is straightforward to use, since it is supported directly by the programming language. Class inheritance also makes it easier to modify the implementation being reused. When a subclass overrides some but not all operations, it can affect the operations it inherits as well, assuming they call the overridden operations. However, class inheritance has some disadvantages, too. First, you cannot change the implementations inherited from parent classes at run-time, because inheritance is defined at compile-time. Second, and generally worse, parent classes often define at least part of their subclasses' physical representation. Because inheritance exposes a subclass to details of its parent's implementation, it is often said, "inheritance breaks encapsulation”. The implementation of a subclass becomes so bound up with the implementation of its parent class that any change in the parent's implementation will force the subclass to change.


Implementation dependencies can cause problems when you are trying to reuse a subclass. Should any aspect of the inherited implementation not be appropriate for new problem domains, the parent class must be rewritten or replaced by something more appropriate. This dependency limits flexibility and ultimately reusability.


One cure for this is to inherit only from abstract classes, since they usually provide little or no implementation. Object composition is defined dynamically at run-time through objects acquiring references to other objects. Composition requires objects to respect each other’s interfaces, which in turn requires carefully designed interfaces that do not stop you from using one object with many others. However, there is a payoff. Because objects are accessed solely through their interfaces, we do not break encapsulation. Any object can be replaced at run-time by another as long as it has the same type.


Moreover, because an object's implementation will be written in terms of object interfaces, there are substantially fewer implementation dependencies.



That leads us to our second principle of object-oriented design:


Favour objects composition over class inheritance.


Ideally, you should not have to create new components to achieve reuse. You should be able to get all the functionality you need just by assembling existing components through object composition. You will see object composition applied repeatedly in the design patterns.

Popular posts from this blog

Chain of responsibility using Spring @Autowired List

Iterate Through a HashMap

Under the Hood: Understanding the Gossip Protocol in Apache Cassandra