The Shape of the FLUID

Giancarlo Niccolai
The Elegant Code
Published in
7 min readFeb 7, 2024

Be water, my friend. Water can flow or it can crash. Be water, my friend.

— Bruce Lee.

If you think that early optimisation is the root of all coding evils, it means you’re fortunate enough not to have met early engineering yet.

In this article, I’ll introduce ‘FLUID’: a new programming methodology that I have been developing through my career, based on the early principles of the AGILE manifesto, and on the second principle of minimality I refined through my career: the principle of minimal code per action.

The principle of minimal code per action complements the earliest of the programming principles I proposed, the principle of minimal incompleteness. Together, they provide a framework to understand the source of readability and flexibility of software, and the axioms from which any programming methodology moves.

While the FLUID methodology is diametrically opposed to the more renown SOLID, the minimality principles (incompleteness and code per action) inform both of them. I’ll explain how this is possible in the rest of this article.

Demystifying SOLID

The SOLID methodology is well known throughout the industry, but I still find that many developers have a hazy understanding of its definition. I like to think this is due to the over-stress of these rather simple concepts for glamour and contractual opportunities. In the most simple terms, SOLID, more than a methodology, is a framework, comprising practices that will keep large code bases readable and maintainable. The acronym means:

  • Single Responsibility Principle: A class should have only one reason to change.
  • Open/Closed Principle: Objects or entities should be open for extension but closed for modification.
  • Liskov Substitution Principle: Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program.
  • Interface Segregation Principle: Many client-specific interfaces are better than one general-purpose interface.
  • Dependency Inversion Principle: Depend on abstractions, not on concretions.

It is immediately evident that this framework presumes being applied in a object-oriented environment, where an excessive liberality in creating the object-abstractions called classes may result in spaghetti code and ad-hoc solutions that make sense only to the original developers — if even to them.

Large object-oriented projects require discipline and clear design patterns, and SOLID offers a framework to standardise, and thus, simplify the process of writing large code bases.

But… not all coding happens in large code bases, and not all problems in cybernetics and computer science are better solved through an object oriented approach. This alone means that SOLID is not the best way to go about writing code in every possible situation.

Minimal Code per Action

The second minimality principle I discovered through my career is that the smallest the code to achieve a desired result, the better.

Ideally, you want to be able to just write “do it”, and have all the automation in place for the system to actually “do it”.

The action named in this principle is a single activity a program can perform. Transforming a certain data set, summarising content, performing statistical analysis, offering a single service… these are all single actions, types of output that a program can generate.

Intuitively, it is clear why the less code is necessary to perform a single action, the better. Less instructions a code comprises means that it is easier to understand (feeding back in the principle of minimal incompleteness), easier to maintain and extend, and less rid of technical debts. If the need ever arise to rewrite it all, a smaller code is less a liability than a larger one.

Not surprisingly, every component of the SOLID framework reduces the code-to-action ratio in large, object oriented code bases.

Single responsibility means less decision branches, and less special-cases management, all of which requires additional code.

Open/closeness encourages extensibility of existing code, which encourages writing the base classes in a way that minimise the amount custom code needed by the subclasses.

The Liscov substitution principle suggests that superclasses should not have special code handling special cases, which should be handled by the sub-classes only, further reducing the amount of special-case handling code.

The interface segregation helps implementing only the necessary functionalities in each concrete implementation.

The dependency inversion principle prevents program entities to be forced to check for the type of their caller to perform special handling.

This, in a large object oriented project. However, outside this scenario, the SOLID framework can be detrimental to the minimality principles. In particular, in a microservice environment.

The Pitfalls of SOLID

The SOLID framework aims to minimise incompleteness and code-per-action in large, object oriented code bases. However, recently, a different industry-standard application model has emerged: the microservice.

In a microservice environment, compactness of code plays a major role; moreover, microservices are designed around a functional interface, not an object-oriented one. Describing the subtlety of this difference is beyond the scope of this article, but in a nutshell, object-orientation is more adequate to treat with data abstractions, while the functional API interface is based on the type of service offered and required by automated agents and users alike.

Also, microservices are not the only application domain where an object oriented design is not the most adequate, neither is the only domain where keeping the code base small by design is a central requirement: they are simply a clear and industry-standard example of how such design can be successful.

The SOLID principles fail to meet the needs of such a different domain:

  • Single Responsibility Principle: applying SRP at a micro-level within each service can sometimes lead to over-engineering.
  • Open/Closed Principle: can be challenging because microservices often evolve rapidly, requiring changes that might not fit the “closed for modification” aspect comfortably.
  • Liskov Substitution Principle: might be less applicable in a microservices architecture where services communicate via well-defined interfaces or contracts rather than through shared inheritance hierarchies.
  • Interface Segregation Principle: viable between microservices — but not necessarily within them, as a microservice should be dedicated to one task; as such, the interface concept itself should be superfluous.
  • Dependency Inversion Principle: the practical implementation of DIP in a microservices architecture might focus more on inter-service communication patterns and protocols than on abstract class design.

The FLUID framework

FLUID encapsulates the essence of flexibility and minimalist design, especially for use in contexts like microservices. It highlights the importance of adaptability, simplicity, and efficiency.

The FLUID design comprises the following principles:

  • Flexible Design: Embrace changeable and adaptable design patterns that can evolve over time.
  • Lean Implementation: Focus on minimalism in code and functionality, doing more with less to achieve the desired outcomes efficiently.
  • Universal Interoperability: Ensure components and services can easily interact and integrate with diverse systems and technologies.
  • Intuitive Interfaces: Design interfaces that are clear, simple, and user-centric, facilitating easy use and integration.
  • Decentralised control: Empower individual components or services to make decisions based on local knowledge, enhancing agility and responsiveness.

The FLUID methodology aims to guide the development of systems that are adaptable, straightforward, and efficient, prioritising ease of maintenance and the capacity to swiftly respond to changing requirements or pursuing new opportunities.

Fluid is Minimalist

The FLUID framework is based on the principle of minimal code per action, other than the even more basic principle of minimal incompleteness. That may seem counterintuitive, as FLUID is opposed to SOLID, and SOLID is itself based on the same principles.

But the point is that FLUID and SOLID embed the same minimalist principles in different realms. With limited code base size and action scope by design, the FLUID principles respect minimalism:

  • Flexible Design: write code so that the effort to adapt to changes is minimal.
  • Lean Implementation: never add more code than what is strictly needed to complete the required task.
  • Universal Interoperability: if components are interoperable, it’s easier to swap them out with a new, simple implementation.
  • Intuitive Interfaces: simple interfaces have as few entry/end points as necessary.
  • Decentralised control: as no element is crucial, any component may be easily substituted with a minimal implementation.

Fluid to a Point

As the system grows, and as requirements shift from functional (i.e. do this for me) to object-oriented (i.e. produce a list of car-like-objects for me) the FLUID framework should naturally flow into SOLID.

If the developers are too entrenched into trying to keep things simple, even when they become complex, when the project grows complex enough, the initial fluidity that allows for a rapid growth and flexibility might cause the code base to turn into unmaintainable spaghetti code.

It is crucial to recognise this flexion points and either:

  • Branch out into a new, separate project (eventually a microservice) so that both the spawn and the original one keep their planned simplicity; or
  • Re-engineer the code base from FLUID to SOLID, in order to enforce those principles that make a large code base both readable and flexible.

Conclusions

In this article, I introduce the concept of FLUID coding methodology (or framework), opposed to SOLID, and put them both in relation with the principle of minimal code per action. I described how both methodologies respect the minimalist principles: they do that in different ways and contexts.

The takeaway of this article is that some domains require a different, FLUID approach in order to develop applications according with the minimalist principles.

--

--

Giancarlo Niccolai
The Elegant Code

Informatico di professione, scrittore per passione. http://www.niccolai.cc — opinions are mine, sharing is not endorsement.