Live data from Hacker News

The key points of "Working Effectively with Legacy Code"

understandlegacycode.com

31–40 of 74 posts

Re: The key points of "Working Effectively with Legacy Code"

#31
post #24

I understand and I agree with the author points, specially looking to distance yourself from the dependencies these systems are usually entangled with, however: >But the code examples are in Java and C++ and I do python/JavaScript/ruby/ The problem with real legacy code is that sometimes it's not even in those languages. It's VB.NET, COBOL, AS400, BASIC, FORTRAN...and these may not have a chance to "wrap around your…

> the article doesn't give any compelling argument to answer this Is "[w]hen code is not tested, how do you know you didn’t break anything?" not a compelling argument to your boss?

From personal experience with bosses: no, often times it's not a sufficient argument.

If the choice is thought to be between:

* delivering value directly to the customer to justify a company's existence

* or adding tests to things that already work (shore up) in an effort to make more correct changes in the future

Will anyone be surprised how often it's the former that management will go for?

I've found the appetite for this type of testability/observability improvement work increases proportionally with the number of support calls being made from customers complaining about the current feature set being unstable and buggy. This work is less palatable when customers really are just looking for that next new feature you promised instead and everything else is a-ok. The exception being things like orbital navigation systems etc..

Re: The key points of "Working Effectively with Legacy Code"

#32
I've replaced a few large legacy systems over the years and I disagree with most of the points mentioned in the link. The only way guaranteed to work is to run the old system concurrently with the new system with live inputs and compare results for a trial period and fixing discrepancies as they arise. Only when there is 100% behavioral/data fidelity with the old system can a switchover to the new system occur.

I'm not aware of the formal Design Pattern name, but a google search yielded this blog post on the subject: https://medium.com/@sahayneeta72/parallel-run-strategy-7ff64...

Coincidentally this is the same strategy github employed when verifying libgit2: https://github.blog/engineering/engineering-principles/move-...

Re: The key points of "Working Effectively with Legacy Code"

#33

I've replaced a few large legacy systems over the years and I disagree with most of the points mentioned in the link. The only way guaranteed to work is to run the old system concurrently with the new system with live inputs and compare results for a trial period and fixing discrepancies as they arise. Only when there is 100% behavioral/data fidelity with the old system can a switchover to the new system occur. I'm n…

I’ve seen Martin Fowler call something like what you’re describing the Strangler Fig pattern

https://martinfowler.com/bliki/StranglerFigApplication.html

Re: The key points of "Working Effectively with Legacy Code"

#35
> First, add tests, then do your changes

This can be a superpower.

I once had to replace a PostgreSQL-interfacing layer of a large+complex+important system, and most of the original tests had been lost.

It's the kind of change that could be an existential risk for a company that needs the system to be available and correct.

So I built a comprehensive test suite for the existing PostgreSQL-interfacing layer, tested it, made drop-in compatible API using the new way to talk with PostgreSQL, tested that, and... the entire huge system simply worked with the replacement, without a lot of headache/defects/dataloss/ulcers.

> Before you change code, you should have tests in place. But to put tests in place, you have to change code.

Keep reading. You don't always have to do it in the heroic way the article described in this section. Modularity and backward-compatible interfaces, FTW, when you can. The "Sprout" and "Wrap" techniques that the article describes later.

Also, one complementary general technique that the article didn't get into is that sometimes you can run two implementations in parallel, and check them against each other. There are various ways you can do this, involving which really implementation really takes effect, and what you do if they don't agree. (Related: voting configurations in critical systems.)

> Characterization tests

The test suite not only let me validate the implementation, but the exercise of developing the test suite also helped me understand exactly what I should be implementing in the first place.

Sometimes I had to look at the implementation, to tentatively figure out the semantics, then testing validated that also.

> Use scratch refactoring to get familiar with the code

In reasonable cultures and with reasonable management, or if you don't tell anyone. Otherwise, it's the same kind of risk as the old "throwaway prototype" problem: someone with authority says "looks good; I told the customer/CEO we just need to make these few more changes, and ship it". Or, in popular Agile/Scrum processes, "I thought you already completed the refactor last sprint; why are you doing it again".

Re: The key points of "Working Effectively with Legacy Code"

#36

I understand and I agree with the author points, specially looking to distance yourself from the dependencies these systems are usually entangled with, however: >But the code examples are in Java and C++ and I do python/JavaScript/ruby/ The problem with real legacy code is that sometimes it's not even in those languages. It's VB.NET, COBOL, AS400, BASIC, FORTRAN...and these may not have a chance to "wrap around your…

Would you be so lucky if your legacy code was written in a well-known language. At a previous employer we had a big pile code in a “macro” language. It could interface with the main C++ code base. It was similar to assembly language in that it had no loops (just gotos) and register-like local variables (no custom names, just L0, L1, L2,…). The semantics were weird, something like unexpected pass-value-value versus pa…

Sounds similar to, but not as bletcherous as, BANCStar, a famed "esolang" that was really the output of a form generation tool used in the finance industry in the 90s. Some engineers figured out that the form generator's output was actually expressed in a Turing-complete, though very arcane, notation replete with opcodes and operands, reverse-engineered it, and started working directly in this language without using the tool to do more sophisticated things than the tool enabled on its own. The boss man probably said "Great! When can we put this into production?" and it's been downhill since.

https://esolangs.org/wiki/BANCStar

https://github.com/jloughry/BANCStar

Relatedly, a long time ago I discovered that Zenographics Mirage—a vector graphics/illustration program originally designed to run on like, VAXen, with both a text terminal and a graphics terminal with a digitizing tablet (similar to this setup seen on Reading Rainbow: https://www.youtube.com/watch?v=b_zYaIxb6dY&t=965s), but later ported to PCs—ran on a sort of bytecode. There were a number of sample scripts shipped with the package that allowed you to automate things in Mirage's command language. Some of them had statements like DO 62,2,32472,32476,0 and comments that read "Don't worry about this, this is just Mirage assembly language." Intrigued, I discovered in the manual a feature you could enable called Op Code Monitor that flashed similar numbers whenever you entered a command. It was mentioned but not documented in detail, nor what the numbers meant, but from that and the scripts I could make some pretty good guesses. I figured out how to make Mirage prompt for a point and store it in a register; and with that I made a command to draw a rectangle that could be rotated. A rectangle in Mirage was defined by its corner points, so when you attempted to rotate it it just rotated the corner points and drew an axis-aligned rectangle with the new corner points. My command accepted two corner points and drew a polyline, so that when you rotated it, the whole rectangle rotated.

Re: The key points of "Working Effectively with Legacy Code"

#37
post #29

I understand and I agree with the author points, specially looking to distance yourself from the dependencies these systems are usually entangled with, however: >But the code examples are in Java and C++ and I do python/JavaScript/ruby/ The problem with real legacy code is that sometimes it's not even in those languages. It's VB.NET, COBOL, AS400, BASIC, FORTRAN...and these may not have a chance to "wrap around your…

I read the book shortly after it came out, when I was working on an enormous system of legacy code. Unfortunately, I didn't find the book particularly helpful in terms of strategies for understanding or modifying legacy code. Yes, tests are a good thing, but I expected the book to provide a lot more. I agree with the parent comment that it is useful to follow the "trail" through the code. It can be a big effort just…

>> Yes, tests are a good thing, but I expected the book to provide a lot more

It is strange, actually, how much value we place on any information that sits between two pieces of cardboard.

Re: The key points of "Working Effectively with Legacy Code"

#38
No. Run away from any sort of refactor or rewrite. Don't even think about it. Do not proceed. If you're thinking that you can do better, you will be humbled in the worst way. Ignore my warning at your peril. I'll never again for the rest of my life make the horrible mistake of rewriting any system. The article's tactics are all good advice – but it's like tactics for surviving on the front line. You win by never going there in the first place.

Re: The key points of "Working Effectively with Legacy Code"

#39

I've replaced a few large legacy systems over the years and I disagree with most of the points mentioned in the link. The only way guaranteed to work is to run the old system concurrently with the new system with live inputs and compare results for a trial period and fixing discrepancies as they arise. Only when there is 100% behavioral/data fidelity with the old system can a switchover to the new system occur. I'm n…

Better still, lots of mini-switchovers. I rolled out a bank’s p&l system for bonds that way. Trading book by trading book, those with simpler instruments first, more complex ones later. We finished ahead of schedule!

Re: The key points of "Working Effectively with Legacy Code"

#40
post #24

Earlier quoted context omitted.

> the article doesn't give any compelling argument to answer this Is "[w]hen code is not tested, how do you know you didn’t break anything?" not a compelling argument to your boss?

From personal experience with bosses: no, often times it's not a sufficient argument. If the choice is thought to be between: * delivering value directly to the customer to justify a company's existence * or adding tests to things that already work (shore up) in an effort to make more correct changes in the future Will anyone be surprised how often it's the former that management will go for? I've found the appetite…

>Will anyone be surprised how often it's the former that management will go for?

I don't think I have ever asked permission to do make what I thought was the correct change.

Post reply on HN