Reflections on Legacy Code

Every company has a legacy code. Often we confuse legacy with multiple different concepts, and this confusion makes improvements harder to happen. Most engineers don’t like to deal with legacy code-we all like shiny new things. Unfortunately, most of the time, there is no easy way out. It is possible to replace a legacy system with a brand new system because it is easier and faster than fixing all the legacy’s problems. We can’t get out of legacy systems because they have old languages, old databases, and often messy. Is that so? However, there are 2 kinds of old languages. The ones who are dying and dated like Delphi or Clipper and the other ones are old but are perfectly fine to use C++ or Java. I’m not defending legacy systems; however, I believe several essential aspects need to be considered.


Old: Mature vs. Dated

Commonly, we confuse mature software with dated. Boring technology is full of good arguments there. Old technology not necessarily means it is terrible. I would argue that’s the case for languages like C, C++, and Java. We often bash relation DBS like MySQL and Postgres; however, they are the solid foundation for many systems. When we try to fix all problems with one single solution, a.k.a is a silver bullet where you might see valid issues for relational databases. However, several use cases are very well addressed by these solutions. We need to avoid confusing old tech with bad tech or dated tech. Dated technology often has several performances, maintainability, security issues. The world moved on, and these dated technologies get into the way. Other times you just can’t find developers any longer. Look at the case of angular.js may be the youngest legacy ever made.

Different Deployment Units

Another confusing is to say that monolithic things are bad, which is not necessary 100% true. Monoliths and microservices can be seen as different deployment units. Deployments units are about DDD Bounded Context and granularity. Bigger or smaller are acceptable solutions in the same spectrum as long as the decision makes sense and is coherent. It could be the case that a software component, let’s say small(i.e., microservices), is wrong, and you need to make it bigger and merge it with something else to have an easier mantaince path. But is also true that something more significant might need to be sliced to have a better-maintained approach. Evolution cannot be associated with the size of the deployment unit only. Otherwise, you will create a legacy, meaning, lousy maintenance path for your company.

Isolation vs. Distributed Monoliths

Isolation IMHO is the most desired property of a good Service, whether it is micro or not. Isolation allows us to change things under the hood and move faster without affecting consumers while driving improvements without endless migrations. Isolation is counterintuitive because we see duplication as a bad thing, which is not.
Leftovers: How flavors are created

Dealing with legacy software often is not easier. People and teams often want to improve things and try different languages, libraries, frameworks, and approaches. However, the highest cost is not to engineer something newer but actually migrate the old consumers to use it. As time passes and priorities change, people, come and go, is common to find leftovers. This means in other worlds unfinished migrations which eventually will charge they price. I totally understand that companies have hard decisions to be made. Representing driving innovation vs. improving the internal system. The problem I see in the industry is that often that’s unbalanced.

Leftovers also happen due to the natural team erosion(people coming and going, projects starting and ending, business needs changing direction).

Rate of Change and Innovation

Eventually, the bill comes. The rate of change you have to deal with could be a considerable amplification in favor of the underlying complexity you might have in legacy systems. At the end of the day is all about managing complexity down and keep systems manageable, meaning you still can change them and drive innovation in the company in a reasonable way.

Legacy systems have an essential role in driving innovation because, eventually, you will need to make changes in legacy systems to support new initiatives. Yes, you can do Shadow IT; you can create new Services, use new technologies but eventually, you will need to change something on the lacy to support the new system. That’s where finally we reach a bottleneck. To de-bottleneck innovation, we need to deal with legacy systems properly. Otherwise, it will be slow. Often this does not happen due to a mentality issue or lack of understanding of the underlying complexity.

Is easier to see the Technology group as a Cost Center, bottleneck, zealots, and holders of the status quo, like anti-bodies. Often this is true. However, I believe technology groups often fail to educate the business, show complexity, and connect technology and innovation. Legacy systems are at the heart of that.

Make it better or make it worst?

IMHO we often make this problem worst by ignoring it. Which makes the complexity higher, and we get slower and slower over time. No engineer likes to deal with the legacy system(well, to be fair, some who want it) but not the majority for sure. However, I would argue that our selective engineering mentality of only give me the cool and new stuff is killing us.

How are Legacies created?

IMHO several factors contribute to legacy systems being created. Before I mention these factors, I would like to share what are the BAD properties I see in the legacy system which make them BAD, such as:

* High Cost of Change
* Slower than it should be to introduce changes
* Constant Outages and Customer experience disruption
* Higher Bug rate, Higher engineering turnover

This is the reasons for me that Legacy systems are not desirable at all; now we understand what legacy systems means, let’s talk about how they end up being born, which has many factors and reasons such as:

* Lack of understanding what we are doing; we often copy and paste code and do things we dont fully understand the consequences of until it is too late to change.
* Poor understanding of the business (therefore making bad abstractions, bad design)
* Poor understanding of technology, no matter if new or old, everything has drawbacks.
* The wrong architecture, i.e., the wrong bounded context, lack of service isolation
* The Lack of refactoring and continuous improvement
* Pressure to release features with the wrong foundation making everything more complex and slower in the long run(Feature Factory problem).

Often technology frame things with the wrong arguments and could easily sound like technology wants actually:

* Not innovate
* Bureaucracy and slow process
* Centralize process and technology decisions and dont deliver value
* Perfectionism

Perhaps there is a change in attitude it needs to happen in the technology industry. That’s why this problem is not only a technical problem but a sociological one.

Complexity Aware

IMHO the crux of the problem is, in the end, all About Complexity. Thats why so many methods and authors talk about the importance of complexity. Most recently, Aron Dignan on Brave New work(Also mention on my slidecasts) with Complexity Awareness on the heart of his company Operation System(OS). Jason Little on Lean Change in 2014, where change is driven by science and experiments instead of a prescriptive process which is very complexity aware, and Dave Snowden on Cynefin way back into 1999 where Dave bashes best practices in complex envs.

Funny enough, in his classical book The Goal, which is one of the most essential Lean books ever written, Goldratt talks about a tale of a factory going bankrupt and the factory was buying new machines. One of the solutions they did was use old machines instead of the new ones; they also did not throw the whole factory away but fundamentally change how they SEE everything. Lean is about SEEING waste and improving continuously; another way to see lean is a significant Complexity awareness enabler where we try to understand deeply what’s going on rather than just ignoring the problems. If we dont see the problem will be impossible to fix it.

Cheers,

Diego Pacheco

Popular posts from this blog

Kafka Streams with Java 15

Rust and Java Interoperability

HMAC in Java