CUSEC 2017 Part 1: Dealing with Legacy Code Bases - Amar Shah

danielle-mustillo

31 January 2017


Earlier this month, I had the priviledge of attending the Canadian University Software Engineering Conference 2017 (CUSEC 2017), and I figured I’d share a little bit about my experience here. I had intended to make one post on the entire experience, but given how much I have to say, I will break up this into a series of posts.

Therefore, this is Part 1 of the CUSEC series. To kick off this series, I’ll start with a blast from the past: Working with Legacy Codebases (OK I’ll see myself out for the dad joke).

The Issues with Legacy Code

One of the best talks I attended was one on legacy code. The talk addressed the emotional burden experienced while dealing with old, ragged, dirty, bad code. In these kinds of code bases, people tend to find one/many/all of the below:

  1. Deprecated/End of Life depedencies (and the burden of not-so-easily upgrading these dependencies)
  2. A severe lack of automated tests (making new changes risky)
  3. Monolithic modules/files of functionality
  4. Poor coding practices
  5. Ambiguious/poor software architecture
  6. No documentation
  7. Security issues (SQL injection vulnerabilities, authorization loopholes)
  8. Lots of WTFs/min

Good Code/Bad Code WTFs/m by Thom Holwerda

Anyone who’s worked with legacy codebases has surely felt the rage that the code bases lead you into. Most of Amar’s talk dealt specifically about dealing with this frustration and changing your way of thinking of legacy code.

While this talk might seem boring to some, I think its an important talk because most people will experience these emotions at one point. Regardless of whether you work with legacy code or not, issues addressed come up often enough to be of concern to everyone. Especially as the codebase matures, so do these issues become more important.

Common Reactions to Legacy Code (and How to Deal with Them)

At this point in the talk, he described what he did to cope and how these coping mechanisms didn’t work. As was described in his talk, all of these concepts are borrowed from Cognitive Behavioural Therapy, specifically Cognitive Distortions.

Cognitive Behaviour Therapy Depicting basic tenets of CBT by Urstadt is licensed under CC BY-SA 3.0

Blame/Labeling

Logic: The code is so bad, therefore the writers of this code are phonies and terrible coders.

False. The easiest way to fall out of this trap is to realize that situations change. Maybe at the time, the mental shortcuts were necessary for a upcoming release, budget restraints, etc. Maybe the good software practices were not invented at the time (that includes the necessary tools as well as community acceptance of certain practices) or that the team did not know of these practices (inexperience, etc). Alternatively, if none of these work, remember that we all worked on something that we said “yolo, good enough” and pushed that code nonetheless. Doing everything perfectly is never possible. As expected, relevant XKCD:

Good Code Good Code by xkcd is licensed under CC BY-NC 2.5

I leave you with this paraphrase (which I will pretend is a quote) I wrote down from the talk:

Developers are not machines. But like code, developers expect other developers to code like machines. But naturally, this is not possible and your setting yourself up to be disappointed. Developers are humans, and humans are imperfect.

Quit

**Logic: go get a job at with the flashy <insert language/framework> codebase, they dont have legacy code there and the grass is greener there.**

This, too, is False. Any useful piece of software is by defintion a legacy code base. That code might be 5 days old, 5 years old or even 5 decades old (COBOL?). But since its being maintained by someone, since that piece of software is useful, it is by definition legacy. Legacy is often percieved negatively, an insult almost, but it really is not a dirty word. For some given period of time, whether past or present, that code just works.

Here’s how I think about it, which once again I will pretend is a quote (its not):

Once you write that code, push it to source control and it passes all tests, that code enters the everlasting adult phase of maintenance (that is, until it dies). That code is patched, sometimes replaced, but it or its derivatives are continually maintained for the life of the project. Its dependencies may need to be updated eventually. That code may contain security issues we haven’t found yet. It may run on an insecure platform one day. But that doesn’t make the code bad. That code was one time good enough, we should appreciate it for what it does, not what it isn’t. I think that this notion is too easy to forget.

I digress, the point is old code is ubiquitous and unavoidable. The quantity and quality may change between code bases, but its always there. Legacy codebases are all around us in all shapes and sizes. It pays to learn to deal with them.

Depression/Overgeneralization

Logic: I hate my life, I have to deal with all this sh!t code, nobody loves me and I might as well marry this code base now.

It’ll be ok. Trust me. We’ve all been there. The world isn’t out to get you and we only want you to do your best. Its not your fault and we need to move forward. I like to think of myself as that dude in the desert with the cactus when working with code like this.

So focus on that, you may know how you got there, but you must figure out what to do about it now.

All-or-Nothing Thinking

Logic: OK Danielle, fine, but I have to bring this codebase upto snuff/rewrite the whole thing because this is unacceptable. It breaks every software engineering rule!

I understand this feeling, I hate putting my name on things I don’t believe in. It probably stems from a perfectionist mentality. Thats OK. But lets be real for a minute: you probably don’t have the resources to invest in doing a huge rewrite. Yes, ideally we should bring all the code up to date, but that quickly becomes impossible when working with huge codebases. You probably won’t get to rewrite from management unless you have a damn good reason. Realistically, you’ll have to settle for the next best thing.

A good practice I’ve adopted is to always leave the codebase in better shape than you found it. It could be adding some more unit test code coverage. It could be refractoring. Anything. Change one line in a method? Add some private helper methods enhance clarity. Adding a new feature? Refractor related functionality so it also obeys SOLID principles. This is known as The Boy Scout Rule and it helps keep everyone sane and on the same page. Even if your 2 minute change now takes all day thanks to code review software or whatever. Do it! You’ll thank yourself one day!

In short: Rome wasn’t built in a day and neither is your perfect application brought back from the hallows of bugs, cobwebs and security vulnerabilities. Small drips in a bucket over time fill any sized bucket.

Jumping to Conclusions

Logic: This bit of code works. But if I don’t refractor/fix/unit test this code, it will blow up in my face somehow, someday.

Partly true. If some piece of functionality you never used before seems flaky, then yes maybe you should look into it. But if this piece of software has been used for years without issue, then more likely than not it works and doesn’t have to be changed. Just because you have :

  • Low code quality
  • No unit tests
  • O(n^3) run time
  • Edge cases aren’t considered

Doesn’t necessarily mean its bad code. It’s only bad when it becomes a problem, and more likely than not it isn’t a problem. An O(n^3) algorithm is probably OK if its run once a day. Edge cases maybe aren’t important since inputs have been prevalidated or shown to never happen. Ultimately, its a judgement call. You probably already have 150 other higher priority things to do anyway. Personally, I wouldn’t worry too much about hypotheticals.

Here’s another pretend quote to summarize:

You can’t be a perfectionist working with legacy codebases and still love your life. Learn to let go!

And maybe another paraphrase I borrowed from Amar’s talk:

Untested legacy code has passed the test of time, which is ultimately the best test anyone can ask for!

Some Coping Mechanisms

Some easy ways to deal with these Cognitive Distrotions are listed above in the respective distortion, but here are a few more tips:

Legacy Code is a Learning Opportunity

Software Engineers are paid to learn! Reading and understanding legacy code is a great way to do so. You not only see the common pitfalls but also learn good, positive ways to write code. Maybe its libraries you never knew about. Maybe its functional programming paradigms you didn’t know existed in Java. Have fun!

Maybe start a satirical blog if the code base is so bad?

Production Use Cases are Compilcated

Often times, we don’t know what the ultimate use of our code will be. Maybe the project fizzels out? Maybe it becomes a hit and becomes used in half of fortune 500 companies? Maybe it will be the backbone of another fork, a yet un-concieved framework or part of a multimillion dollar suite of tools. The choices you make for each of these projects is vastly different and at the time, the compromise made sense! Its hard to know. So, when in doubt, give the original devs some slack! You’ll feel better as a consequence!

Legacy Code Gives You a Framework

If you ever need to rewrite this code or a subsection of the codebase, at least you have a template to work from. Thats better than starting from scratch like that poor developer did one day.

If All Else Fails, Maybe Its Corporate Culture

If you’ve considered everything I’ve written already and you still have problems, it very well could be a poor corporate culture. A corporate culture centered around punishing mistakes, assigning blame or simply not fostering good practices can very well be toxic. So maybe its not you. In that case, consider this final fake quote:

The software job market is too good to be bogged down. If you’re not being treated right, better you move on than waste your time. You will find a place that values you for your skills.

Conclusion

If you got to the end of this article and enjoyed, please stay tunned for Part 2! I hope you learned something and I’m honoured you read this far!