Oskar Dudycz

Pragmatic about programming

Let's take care of ourselves! Thoughts on compatibility

2021-07-07 oskar dudyczCoding Life

cover

In the last year, the word responsibility is used in all possible ways. Responsibility for us, for others. We can be heroes wearing masks instead of cloaks. Caring about others is essential not only in those crazy days but in general. We should take care of our colleagues. I often laugh that the best form of working in a corporation is doing nothing. Working in a corporation is a type of work where you are not rewarded for doing good, but you’ll be punished for doing wrong. So the less we do, the less chance that we’ll be wrong. It’s similar to the doctors’ “Primum non Nocere” rule. Still, not quite the same.

There is also a football saying “it’s better to stand smartly than run around dumbly”. Work smart, not hard. Sometimes doing less or slower produces better and faster results than doing something quickly. It doesn’t mean that we should avoid deeds, but we should think in advance about the effects of our actions.

The basis for working in teams, especially remote ones, is respect for compatibility. We all heard about backward compatibility. But did we understand it?

Backward compatibility literally means that our new changes will work properly with the application’s current (or previous) state. What does this mean in practice? Are migrations backwards compatible? Not really. They are transforming the earlier version of the application to the new one. Why is this not compatible? Imagine that we scale our system horizontally. We have several instances of the same service (to ensure better availability). We have the so-called rolling update, e.g. we deploy the new service’s version instance by instance. We want to maintain the highest possible availability. What will happen if the first deployed instance will upgrade the shared storage schema? All other instances may start failing until we upgrade them.

Another situation is when we make a “core” library, which other libraries use, and decide to add the new parameter to the existing method. That may look like a harmless change, but what will happen if we make this parameter required (no default value) and someone updates to a new version of our package? Yup, compilation will fail.

If we’re using an event-based architecture and we noticed there was a typo in our contract. I know that having UserAnme instead of UserName isn’t cool. We may decide to change it. If we do that and release a version and start publishing the “corrected event other systems subscribed to it will crash. They won’t deserialise and understand the new format without changing their code and making a new deployment. For us humans, readability is essential. Computers do not care about that, as long as they get what they’re expecting.

Of course, automated tests can help with that. Still, they won’t resolve the real issue - thinking in advance and caring about the others work. Maybe you could say, “Oh, someone from the other team should do update, it’s just five minutes of work.”. Perhaps it’s five minutes of work, but it’s 5 minutes of work for you. Even if this someone is informed about it and does it, it is never that short. Add to that:

  • context switching,
  • writing the code and automated tests,
  • integration checking if it really works,
  • pull request,
  • code review,

Add to that time spent by several people reviewing the changes and potential discussions. And we are talking about an optimistic scenario, not a case where someone finds out when they get a production error because “something is wrong”. Is it really five minutes?

Jeff Sutherland, in his book Scrum: The Art of Doing Twice the Work in Half the Time, cites research from Palm:

“They looked at the “Matts” across the entire company—hundreds of developers—and they decided to analyse how long it took to fix a bug if they did it right away versus if they tried to fix it a few weeks later. Now, remember, software can be a pretty complicated and involved thing, so what do you think was the difference?

It took twenty-four times longer. If a bug was addressed on the day it was created, it would take an hour to fix; three weeks later, it would take twenty-four hours. It didn’t even matter if the bug was big or small, complicated or simple—it always took twenty-four times longer three weeks later. As you can imagine, every software developer in the company was soon required to test and fix their code on the same day.”

I suggest the following rule:

DO NOT MAKE BREAKING CHANGES AND DO NOT BREAK OTHER PEOPLE WORK. NEVER!

Sounds radical? Maybe, but it is true. There is always a way to perform changes in a non-breaking manner. We can always do a step by step process.

If we’re creating an OSS library, we can mark features we want to remove with obsolete markers. We should add information about why and when we want to remove it and add a migration path. Then we can give our users time to migrate and not surprise them. In one of the follow-up releases, we can remove it.

Literally, it will be a breaking change but done in a non-breaking manner. If you are a library maintainer and provide breaking changes in each release, I can guarantee that you won’t reach mainstream or broader usage. Most companies expect predictability. If you keep posting breaking changes now and then, users will either not move to newer versions or quit. Read more in my article “How to get started with Open Source?”.

Of course, sometimes breaking changes are needed to remove the ballast. Then migration guidelines are necessary to help your users. Still, it’s a tactical thing that you should avoid if you can.

There are ways to reduce the number of breaking changes. For instance, keeping everything private, that doesn’t have to be public. If you expose something, then you have to assume that someone uses it. Follow the Semantic Versioning rules. Also, grouping the breaking changes into a single release.

It’s also worth providing migration tools (e.g. we did such a thing in EventStoreDB with Replicator). Nevertheless, those are tactics. I stand on my take that the strategy to be reliable and effective is not doing breaking changes.

To makes things harder, backward compatibility is not the only compatibility we should think of. There is also forward compatibility? It means that we have to anticipate that our code may be called by the newer version of the code/service. It is backward compatibility seen from the perspective of this old code/state. For example, if we listen to an event, e.g. UserAdded, which has the fields Id, Username, we can expect that, for example, in the future, we may get its newer version with new fields, e.g. Email, BirthDate, etc.

Of course, we can’t predict how our application will evolve. We will not handle fields that we do not know. We don’t have to predict everything. Nevertheless, we have to keep in mind that the contract may change, and if it is backwards compatible, we should handle it. How can we do that? Well, we can, for example, not throw an error when we get additional fields in the payload. If we do Event Sourcing and save the events to the database as JSON, we should keep them as they are. Thanks to that, when we update the code, we will create the new logic and handle the extended data.

We should help ourselves and help others. Let’s try not to break others’ work because it really pays off, literally. It really isn’t a waste of time.

Cheers!

Oskar

👋 If you found this article helpful and want to get notification about the next one, subscribe to Architecture Weekly.

✉️ Join over 11500 subscribers, get the best resources to boost your skills, and stay updated with Software Architecture trends!

Loading...
Event-Driven by Oskar Dudycz

cover

Through my window, I see the result of good plans but poor execution. Opposite my flat, there is a partially completed construction place. Buildings were supposed to be eye-catching Mediterranean style apartments. Delivery date? Two years ago. Actual? More and more unknown.

Some time ago, I heard that using Event Sourcing makes creating Event-Driven Architecture easier. The arguments were correct, that if we’re already publishing events to trigger business workflows, then at some point, we may want to also store events to not lose information. Agreed. However, I also heard that keeping the state as events will simplify things. We’ll have a source of truth with a record of the system behaviour. This will allow, e.g. to confront the results of the operations with the recorded state. I’d agree with that, with one distinction. It’s easier as long as you already know Event Sourcing.

Many people in the DDD community claim that the essential is to properly break down the system into autonomous parts called bounded contexts. Once we have it, the rest is secondary and will sort itself out. For sure.

Many seasoned programmers speak similarly about new technologies. They claim that they can translate past experience into new technologies. That’s true that by analogy, they can catch the big picture quicker. But isn’t it a bold assumption to say that Win.Forms specialist will learn Angular quickly?

The end result may differ a lot from the initial ideas. I saw the plan of those buildings next to me. Now I can see the effects of the execution. Or actually, the lack.

I believe that we should carefully acknowledge not only the point of view of our authorities but also their seating point. If we want to find out how to form a wall, do we ask an architect or a foreman? An architect may know the theory, but the practice is what we’re looking for. On the other hand, if you want to know where to put the wall, you prefer the architect to do measurements. At least if you don’t want to have the roof falling to your head.

After I had torn a ligament in my knee, I went to two qualified orthopedists. One said I should have surgery and do a reconstruction. The second stated that there is no need for that; rehabilitation should be enough. Guess which one had a specialization in surgery and which in rehabilitation?

People usually give us advice from the point where they’re currently standing. They are entitled to a biased view. An architect who rarely does programming will tend to downplay the value of implementation and tactical patterns. Midlevel developers will focus on technicalities instead of the global system impact. The team manager or consultant will emphasize the importance of soft skills (or esoteric techniques known only to them).

The truth is that we need all of them. The excellent plan will fall on the bad execution. The best execution for the wrong case will be just a waste of time. We should carefully evaluate the advice considering what we need and what an expert can give us.

Therefore, when we’re reading an article, watching a talk, let’s also pay attention to the place where the person is standing. The perspective from there may be much different from where we are right now. That can be good, as it may push us in the right direction. But it may also be misleading, as we accidentally take biases of this person without understanding the tradeoffs. Personally, I prefer to follow not only people from pedestal but also those that are closer to my position. A bit further in the journey, but not too far. That helps me to calibrate my view as those people are more relative to my daily struggles.

Polish historical leader Józef Piłsudzki reportedly used to say: “Right is like an ass, everyone has its own”.

Cheers!

Oskar