Escape Dependency Hell!!

How to write bug free code Part III
Reading Time: 7 min

There’s method in the madness

As I have already concluded in part I and part II the idea of writing bug free code is ludicrous and utterly laughable so why am I putting myself through this sure path of ridicule and lost credibility?

If I can keep your attention for a while longer, then maybe you can be the judge, was it pure madness or is there any sense in it?
In any case there is a method, and it is rather simple:

Bugs thrive in hiding, under rocks and vegetation.
The first step is to remove as many hiding places as we can.
Then we gradually reduce the remaining spaces until there is nowhere to hide,
and when they crawl out we just KILL THEM ALL!

Not me, are you?

Or if you prefer, think Disney and forget the killing, just kindly ask them to leave and live in someone else’s code.

So let’s start with the places we have no control over, where the light never shines and bugs thrive, let’s start with dependencies!

It’s a mess!

Dependencies creates fragility in a system. A bug or failure in one part will affect the whole system or process. Dependencies increases complexity which happens to be exactly what bugs love.

Reducing dependencies is of highest priority. As in all complex production processes software development is a total mess of dependencies. Here are some of them:

  • the programming language itself. We cannot just jump to another one when we feel like it, we are kind of stuck.
  • the programming paradigm or style, the way we solve a specific problem can in the process create a web of dependencies that eventually will be hard to maintain.
  • the software architecture that might but probably won’t support future ideas and requirements.
  • frameworks and libraries that simplify our lives in the short run but eventually will become old and die, at which point we wish we had stayed out of the relation.
  • the environment that the code is running in, be it browser or native, that is constantly evolving regardless if it needs to or not.
  • the hardware that gets new features and old features are removed.

And more. And that was only the technical ones. Then we have the dependencies on the dev team, the work process, the product design and development cycle, customers and whatnot.

Let the untangling begin! Where better to start than with the elephant in the room?

Picking the right tools for the job…

…is a metaphor that makes perfect sense for a carpenter. They pick whatever tools they want to build the perfect chair. After that the chair doesn’t require any tools to be functional (IKEA being an exception).

A framework or library is not a tool. They are dependencies and required for the final product to work at all. No sitting in my nice Angular chair if you don’t have an Angular butt. No, not in my Docker sofa either.

Building on others work has obvious advantages, why solve problems that someone else has already solved? If we are to achieve bug free code, here are the main reasons why:

  • We cannot invite any Trojan horses into our repo. There is no way that we can audit the code and verify its quality unless it is very basic, in which case we don’t really need it anyway.
  • Frameworks are made to make everybody happy which makes them become bloated over time. There is a lot of “features” that essentially is dead code of limited or no use for us. More code means less optimized and more bugs.
  • When they are no longer the best way to solve the problem they are a fundamental dependency that makes you stick with it for far too long. And during that time it will be updated again and again, each time potentially breaking your system.

Skipping external code might seem like an unsurmountable challenge, and depending on your goals it might not make any sense at all. If we want to achieve bug free code then there is nothing to do except go on an as strict LFHC diet as possible .

Plain vanilla, skip the cone, please!

Mint chocolate chip is the most popular ice cream flavor…

Depending on how much we depend on external code we could very well be on square one now. Maybe some of the external code can be brought in and become assimilated, but many parts might have to be written from scratch.

Although not feasible for most, this is exactly where we need to start. I agree with that it is impossible to write bug free code IF we stick to the way we do it today. So we need to build software in a different way. What better place to start than with a blank slate? Vanilla without the cone.

Going back to the list of dependencies we killed one but there are five more to go. I will not take them one by one, they are actually intertwined and cannot be looked at as separate parts.

Below is my solution to attack them all at once!

Generalizing functionality

On a macro level everyone is moving away from monoliths to micro services and FaaS. It makes perfect sense to break up functionality in parts that are more easily understood and maintained.

On the micro level Functional Programming follows a similar model. Creating complex functionality by combining smaller building blocks.

And in the middle we have the applications and services which are still designed and developed pretty much the same way as always.

The natural step is to break up these services and applications in their components too. In the same way that FP creates complex functionality by combining simple functions, we can build complex applications by developing and combining generalized components.

Conceptual components

The #resistance

Think about what we use digital for the most. We collect, process, store, retrieve data, we present it to the user with text, images, videos and to other systems through APIs, and receive responses back through different means. That covers about 99% of the digital stuff we do. No rocket science and hasn’t changed since forever.

My definition of a component is a chunk of clearly defined functionality in a system, such as a button, video, a socket connection, specific data processing and logic. The definition of the encapsulation of functionality should be based on what makes sense and is useful for the designer of the application, not the developer, and they should be based on concepts.

So components are not about code, they are about encapsulating functionality.

Concepts don’t change

While technology changes faster and faster, concepts are almost immovable. The concepts of basic and complex data types have not changed much since the invention of computer science. Think about the functionality mentioned above, basically the same concepts for 40 years or more. That’s the kind of robustness we should aim for!

Since concepts don’t change an implementation of the functionality, i.e. the code of the components themselves, should not have to change either. With the right encapsulation the implementation of a component could stay untouched for 40 years. Or more.

A decoupled architecture

A lonely house

For components to become that robust we need to make them fully decoupled without any external dependencies. They cannot connect through function or method calls but through messages. This requires an event driven architecture with a shared message bus.

The component must have a clearly defined interface that doesn’t change (concepts don’t change, remember?). The component itself is a black box where the implementation can be updated as much as needed as long as the interface, i.e. the functionality of the component doesn’t change.

Full code separation mean that each component can be updated without worrying that the application will break. A component like this can be used again and again, will be constantly tested and refined based in real world scenarios, and has the potential to eventually reach the impossible zero bug state.

Decoupling the concepts from technology

The #inductance

Going back to implementation of concepts for a second. Think about an implemented concept as an idealized virtual instance that doesn’t have to concern itself with the unpredictability and messiness of reality.
It is technology independent.

For some types of functionality this is all we need, everything could happen in our perfectly conceptualized world, our technology independent world.

Other components have to connect to the world outside, display itself, store and retrieve some data, connect over the network. This is the technology dependent world, the environment in which the component is used, the OS and hardware etc.

We don’t want to mix the two. A component like this must have two implementations, one technology independent and one technology dependent. They must also be fully decoupled and communicate through messages.

This separation isolates the fast changing and potentially bug prone part of the functionality in the technology dependent implementation, if something goes wrong we know where to look. The other part might need maintenance every five years, if ever.

To summarize

I prefer vanilla though…

This was just an overview and there is so much more to say, especially when it comes to implementation. There is an infinite number of use cases so I have not even tried to get specific. But I hope I have been able to give you an idea about some of the concepts that this solution is built on.

So now we have applied the method mentioned in the beginning: to remove the hiding places for bugs, and then reduce the remaining so that there is nowhere to hide.
More specifically, we removed the dependencies:

  • to external code.
  • between functionality through the component design and message based architecture
  • between fast and slow changing code through the separate implementations, which reduced the likely space for bugs to hide to the technology dependent code.

And for the programming language and paradigm? Well, the architecture puts quite strong constraints on how code must be written (sorry guys!) so that solves a part of it. For the rest, we’ll get to that in part IV!

To be continued…

…and there is a lot more to say. For a complete list of the articles in the series please check out the introduction in part 1.
Next up is Part IV: Code is for humans, computers don’t care!

Until next time!

RELATED ARTICLES

Do what I mean, not what I say!

Leadership through mind reading — a practical guide

Code is for humans, computers don’t care

How to write bug free code part IV

It’s the principles, stupid!

How to write bug free code part II

Join the Dinahmoe Newsletter

Every sometimes we post about what we have been up to, about new projects, articles and products.

We will never flood your inbox, most likely the opposite (?). But it’ll be fun, we promise!

We use MailerLite as our marketing automation platform.By clicking below to submit this form, you acknowledge that
the information you provide will be transferred to MailerLite for processing in accordance with their Privacy Policy and Terms of Service.